001/* 002 * Copyright 2007-2009 The Kuali Foundation 003 * 004 * Licensed under the Educational Community License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.opensource.org/licenses/ecl2.php 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package org.kuali.ole.sys.service.impl; 017 018import java.io.File; 019import java.io.FileNotFoundException; 020import java.io.PrintStream; 021import java.util.ArrayList; 022import java.util.HashMap; 023import java.util.Iterator; 024import java.util.List; 025import java.util.Map; 026 027import org.apache.commons.lang.StringUtils; 028import org.kuali.ole.sys.OLEConstants; 029import org.kuali.ole.sys.Message; 030import org.kuali.ole.sys.batch.service.WrappingBatchService; 031import org.kuali.ole.sys.context.SpringContext; 032import org.kuali.ole.sys.report.BusinessObjectReportHelper; 033import org.kuali.ole.sys.service.ReportWriterService; 034import org.kuali.rice.core.api.datetime.DateTimeService; 035import org.kuali.rice.krad.bo.BusinessObject; 036import org.kuali.rice.krad.util.ObjectUtils; 037 038/** 039 * Text output implementation of <code>ReportWriterService</code> interface. If you are a developer attempting to add a new business 040 * object for error report writing, take a look at the Spring definitions for BusinessObjectReportHelper.<br> 041 * This class CANNOT be used by 2 processes simultaneously. It is for very specific batch processes that should not run at the same 042 * time, and initialize and destroy must be called and the beginning and end of each process that uses it. 043 * 044 * @see org.kuali.ole.sys.report.BusinessObjectReportHelper 045 */ 046public class ReportWriterTextServiceImpl implements ReportWriterService, WrappingBatchService { 047 private static org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(ReportWriterTextServiceImpl.class); 048 049 // Changing the initial line number would only affect that a page break occurs early. It does not actually print in the 050 // middle of the page. Hence changing this has little use. 051 protected static final int INITIAL_LINE_NUMBER = 0; 052 053 protected String filePath; 054 protected String fileNamePrefix; 055 protected String fileNameSuffix; 056 protected String title; 057 protected int pageWidth; 058 protected int pageLength; 059 protected int initialPageNumber; 060 protected String errorSubTitle; 061 protected String statisticsLabel; 062 protected String statisticsLeftPadding; 063 private String parametersLabel; 064 private String parametersLeftPadding; 065 protected String pageLabel; 066 protected String newLineCharacter; 067 protected DateTimeService dateTimeService; 068 protected boolean aggregationModeOn; 069 070 /** 071 * A map of BO classes to {@link BusinessObjectReportHelper} bean names, to configure which BO's will be rendered by which 072 * BusinessObjectReportHelper. This property should be configured via the spring bean definition 073 */ 074 protected Map<Class<? extends BusinessObject>, String> classToBusinessObjectReportHelperBeanNames; 075 076 // Local caching field to speed up the selection of formatting BusinessObjectReportHelper to use per configuration in Spring 077 protected Map<Class<? extends BusinessObject>, BusinessObjectReportHelper> businessObjectReportHelpers; 078 079 protected PrintStream printStream; 080 protected int page; 081 protected int line = INITIAL_LINE_NUMBER; 082 protected String errorFormat; 083 084 // Ensures that the statistics header isn't written multiple times. Does not check that a user doesn't write other stuff into 085 // the statistics 086 // section. A developer is responsible for ensuring that themselves 087 protected boolean modeStatistics = false; 088 089 // Ensures that the parameters header isn't written multiple times. Does not check that a user doesn't write other stuff into 090 // the parameters 091 // section. A developer is responsible for ensuring that themselves 092 protected boolean modeParameters = false; 093 094 // So that writeError knows when to writeErrorHeader 095 protected boolean newPage = true; 096 097 // For printing new headers when the BO is changed 098 protected Class<? extends BusinessObject> businessObjectClass; 099 100 public ReportWriterTextServiceImpl() { 101 // TODO Auto-generated constructor stub 102 } 103 104 /** 105 * @see org.kuali.ole.sys.batch.service.WrappingBatchService#initialize() 106 */ 107 public void initialize() { 108 try { 109 printStream = new PrintStream(generateFullFilePath()); 110 } 111 catch (FileNotFoundException e) { 112 throw new RuntimeException(e); 113 } 114 115 page = initialPageNumber; 116 initializeBusinessObjectReportHelpers(); 117 // Initial header 118 this.writeHeader(title); 119 } 120 121 protected void initializeBusinessObjectReportHelpers() { 122 businessObjectReportHelpers = new HashMap<Class<? extends BusinessObject>, BusinessObjectReportHelper>(); 123 if (classToBusinessObjectReportHelperBeanNames != null) { 124 for (Class<? extends BusinessObject> clazz : classToBusinessObjectReportHelperBeanNames.keySet()) { 125 String businessObjectReportHelperBeanName = classToBusinessObjectReportHelperBeanNames.get(clazz); 126 BusinessObjectReportHelper reportHelper = (BusinessObjectReportHelper) SpringContext.getService(businessObjectReportHelperBeanName); 127 if (ObjectUtils.isNull(reportHelper)) { 128 LOG.error("Cannot find BusinessObjectReportHelper implementation for class: " + clazz.getName() + " bean name: " + businessObjectReportHelperBeanName); 129 throw new RuntimeException("Cannot find BusinessObjectReportHelper implementation for class: " + clazz.getName() + " bean name: " + businessObjectReportHelperBeanName); 130 } 131 businessObjectReportHelpers.put(clazz, reportHelper); 132 } 133 } 134 } 135 136 protected String generateFullFilePath() { 137 if (aggregationModeOn) { 138 return filePath + File.separator + this.fileNamePrefix + fileNameSuffix; 139 } 140 else { 141 return filePath + File.separator + this.fileNamePrefix + dateTimeService.toDateTimeStringForFilename(dateTimeService.getCurrentDate()) + fileNameSuffix; 142 } 143 } 144 145 /** 146 * @see org.kuali.ole.sys.batch.service.WrappingBatchService#destroy() 147 */ 148 public void destroy() { 149 if(printStream != null) { 150 printStream.close(); 151 printStream = null; 152 } 153 154 // reset variables that track state 155 page = initialPageNumber; 156 line = INITIAL_LINE_NUMBER; 157 modeStatistics = false; 158 modeParameters = false; 159 newPage = true; 160 businessObjectClass = null; 161 } 162 163 /** 164 * @see org.kuali.ole.sys.service.ReportWriterService#writeSubTitle(java.lang.String) 165 */ 166 public void writeSubTitle(String message) { 167 if (message.length() > pageWidth) { 168 LOG.warn("sub title to be written exceeds pageWidth. Printing anyway."); 169 this.writeFormattedMessageLine(message); 170 } 171 else { 172 int padding = (pageWidth - message.length()) / 2; 173 this.writeFormattedMessageLine("%" + (padding + message.length()) + "s", message); 174 } 175 } 176 177 /** 178 * @see org.kuali.ole.sys.service.ReportWriterService#writeError(java.lang.Class, org.kuali.ole.sys.Message) 179 */ 180 public void writeError(BusinessObject businessObject, Message message) { 181 this.writeError(businessObject, message, true); 182 } 183 184 /** 185 * @param printBusinessObjectValues indicates whether the bo values should be printed before the message 186 * @see org.kuali.ole.sys.service.ReportWriterService#writeError(java.lang.Class, org.kuali.ole.sys.Message) 187 */ 188 public void writeError(BusinessObject businessObject, Message message, boolean printBusinessObjectValues) { 189 // Check if we need to write a new table header. We do this if it hasn't been written before or if the businessObject 190 // changed 191 if (newPage || businessObjectClass == null || !businessObjectClass.getName().equals(businessObject.getClass().getName())) { 192 if (businessObjectClass == null) { 193 // If we didn't write the header before, write it with a subTitle 194 this.writeSubTitle(errorSubTitle); 195 } 196 else if (!businessObjectClass.getName().equals(businessObject.getClass().getName())) { 197 // If it changed push a newline in for neater formatting 198 this.writeNewLines(1); 199 } 200 201 this.writeErrorHeader(businessObject); 202 newPage = false; 203 businessObjectClass = businessObject.getClass(); 204 } 205 206 // Get business object formatter that will be used 207 BusinessObjectReportHelper businessObjectReportHelper = getBusinessObjectReportHelper(businessObject); 208 209 // Print the values of the businessObject per formatting determined by writeErrorHeader 210 List<Object> formatterArgs = new ArrayList<Object>(); 211 if (printBusinessObjectValues) { 212 formatterArgs.addAll(businessObjectReportHelper.getValues(businessObject)); 213 } 214 else { 215 formatterArgs.addAll(businessObjectReportHelper.getBlankValues(businessObject)); 216 } 217 218 // write rest of message on new line(s) if it was cut off 219 int maxMessageLength = Integer.parseInt(StringUtils.substringBefore(StringUtils.substringAfterLast(errorFormat, "%-"), "s")); 220 String messageToPrint = message.getMessage(); 221 222 boolean firstMessageLine = true; 223 while (messageToPrint.length() > 0 && StringUtils.isNotBlank(messageToPrint)) { 224 if (!firstMessageLine) { 225 formatterArgs = new ArrayList<Object>(); 226 formatterArgs.addAll(businessObjectReportHelper.getBlankValues(businessObject)); 227 } 228 else { 229 firstMessageLine = false; 230 } 231 232 messageToPrint = StringUtils.trim(messageToPrint); 233 String messageLine = messageToPrint; 234 if (messageLine.length() > maxMessageLength) { 235 messageLine = StringUtils.substring(messageLine, 0, maxMessageLength); 236 if (StringUtils.contains(messageLine, " ")) { 237 messageLine = StringUtils.substringBeforeLast(messageLine, " "); 238 } 239 } 240 241 formatterArgs.add(new Message(messageLine, message.getType())); 242 this.writeFormattedMessageLine(errorFormat, formatterArgs.toArray()); 243 244 messageToPrint = StringUtils.removeStart(messageToPrint, messageLine); 245 } 246 } 247 248 /** 249 * @see org.kuali.ole.sys.service.ReportWriterService#writeError(java.lang.Class, java.util.List) 250 */ 251 public void writeError(BusinessObject businessObject, List<Message> messages) { 252 int i = 0; 253 for (Iterator<Message> messagesIter = messages.iterator(); messagesIter.hasNext();) { 254 Message message = messagesIter.next(); 255 256 if (i == 0) { 257 // First one has its values written 258 this.writeError(businessObject, message, true); 259 } 260 else { 261 // Any consecutive one only has message written 262 this.writeError(businessObject, message, false); 263 } 264 265 i++; 266 } 267 } 268 269 /** 270 * @see org.kuali.ole.sys.service.ReportWriterService#writeNewLines(int) 271 */ 272 public void writeNewLines(int lines) { 273 for (int i = 0; i < lines; i++) { 274 this.writeFormattedMessageLine(""); 275 } 276 } 277 278 /** 279 * @see org.kuali.ole.sys.service.ReportWriterService#writeStatisticLine(java.lang.String, java.lang.Object[]) 280 */ 281 public void writeStatisticLine(String message, Object... args) { 282 // Statistics header is only written if it hasn't been written before 283 if (!modeStatistics) { 284 this.modeStatistics = true; 285 286 // If nothing has been written to the report we don't want to page break 287 if (!(page == initialPageNumber && line == INITIAL_LINE_NUMBER + 2)) { 288 this.pageBreak(); 289 } 290 291 this.writeFormattedMessageLine("*********************************************************************************************************************************"); 292 this.writeFormattedMessageLine("*********************************************************************************************************************************"); 293 this.writeFormattedMessageLine("*******************" + statisticsLabel + "*******************"); 294 this.writeFormattedMessageLine("*********************************************************************************************************************************"); 295 this.writeFormattedMessageLine("*********************************************************************************************************************************"); 296 } 297 298 this.writeFormattedMessageLine(statisticsLeftPadding + message, args); 299 } 300 301 /** 302 * @see org.kuali.ole.sys.service.ReportWriterService#writeParameterLine(java.lang.String, java.lang.Object[]) 303 */ 304 public void writeParameterLine(String message, Object... args) { 305 // Statistics header is only written if it hasn't been written before 306 if (!modeParameters) { 307 this.modeParameters = true; 308 309 // If nothing has been written to the report we don't want to page break 310 if (!(page == initialPageNumber && line == INITIAL_LINE_NUMBER + 2)) { 311 this.pageBreak(); 312 } 313 314 this.writeFormattedMessageLine("*********************************************************************************************************************************"); 315 this.writeFormattedMessageLine("*********************************************************************************************************************************"); 316 this.writeFormattedMessageLine("*******************" + getParametersLabel() + "*******************"); 317 this.writeFormattedMessageLine("*********************************************************************************************************************************"); 318 this.writeFormattedMessageLine("*********************************************************************************************************************************"); 319 } 320 321 this.writeFormattedMessageLine(getParametersLeftPadding() + message, args); 322 } 323 324 /** 325 * @see org.kuali.ole.sys.service.ReportWriterService#writeFormattedMessageLine(java.lang.String) 326 */ 327 public void writeFormattedMessageLine(String format) { 328 this.writeFormattedMessageLine(format, new Object()); 329 } 330 331 /** 332 * @see org.kuali.ole.sys.service.ReportWriterService#writeFormattedMessageLine(java.lang.String, java.lang.Object[]) 333 */ 334 public void writeFormattedMessageLine(String format, Object... args) { 335 if (format.indexOf("% s") > -1) { 336 LOG.warn("Cannot properly format: "+format); 337 } 338 else { 339 Object[] escapedArgs = escapeArguments(args); 340 if (LOG.isDebugEnabled()) { 341 LOG.debug("writeFormattedMessageLine, format: "+format); 342 } 343 344 String message = null; 345 346 if (escapedArgs.length > 0) { 347 message = String.format(format + newLineCharacter, escapedArgs); 348 } else { 349 message = format+newLineCharacter; 350 } 351 352 // Log we are writing out of bounds. Would be nice to show message here but not so sure if it's wise to dump that data into 353 // logs 354 if (message.length() > pageWidth) { 355 if (LOG.isDebugEnabled()) { 356 LOG.debug("message is out of bounds writing anyway"); 357 } 358 } 359 360 printStream.print(message); 361 printStream.flush(); 362 363 line++; 364 if (line >= pageLength) { 365 this.pageBreak(); 366 } 367 } 368 } 369 370 /** 371 * Determines if all formatting on the given String is escaped - ie, that it has no formatting 372 * @param format the format to test 373 * @return true if the String is without formatting, false otherwise 374 */ 375 protected boolean allFormattingEscaped(String format) { 376 int currPoint = 0; 377 int currIndex = format.indexOf('%', currPoint); 378 while (currIndex > -1) { 379 char nextChar = format.charAt(currIndex+1); 380 if (nextChar != '%') { 381 return false; 382 } 383 currPoint = currIndex + 2; 384 } 385 return true; 386 } 387 388 /** 389 * @see org.kuali.ole.sys.service.ReportWriterService#pageBreak() 390 */ 391 public void pageBreak() { 392 // Intentionally not using writeFormattedMessageLine here since it would loop trying to page break ;) 393 // 12 represents the ASCII Form Feed character 394 printStream.printf("%c" + newLineCharacter, 12); 395 page++; 396 line = INITIAL_LINE_NUMBER; 397 newPage = true; 398 399 this.writeHeader(title); 400 } 401 402 /** 403 * Helper method to write a header for placement at top of new page 404 * 405 * @param title that should be printed on this header 406 */ 407 protected void writeHeader(String title) { 408 String headerText = String.format("%1$tY-%1$tm-%1$td %1$tH:%1$tM", dateTimeService.getCurrentDate()); 409 int reportTitlePadding = pageWidth / 2 - headerText.length() - title.length() / 2; 410 headerText = String.format("%s%" + (reportTitlePadding + title.length()) + "s%" + reportTitlePadding + "s", headerText, title, ""); 411 412 if (aggregationModeOn) { 413 this.writeFormattedMessageLine("%s%s%s", headerText, pageLabel, OLEConstants.REPORT_WRITER_SERVICE_PAGE_NUMBER_PLACEHOLDER); 414 } 415 else { 416 this.writeFormattedMessageLine("%s%s%,9d", headerText, pageLabel, page); 417 } 418 this.writeNewLines(1); 419 } 420 421 /** 422 * Helper method to write the error header 423 * 424 * @param businessObject to print header for 425 */ 426 protected void writeErrorHeader(BusinessObject businessObject) { 427 BusinessObjectReportHelper businessObjectReportHelper = getBusinessObjectReportHelper(businessObject); 428 List<String> errorHeader = businessObjectReportHelper.getTableHeader(pageWidth); 429 430 // If we are at end of page and don't have space for table header, go ahead and page break 431 if (errorHeader.size() + line >= pageLength) { 432 this.pageBreak(); 433 } 434 435 // Print the header one by one. Note the last element is the formatter. So capture that seperately 436 for (Iterator<String> headers = errorHeader.iterator(); headers.hasNext();) { 437 String header = headers.next(); 438 439 if (headers.hasNext()) { 440 this.writeFormattedMessageLine("%s", header); 441 } 442 else { 443 errorFormat = header; 444 } 445 } 446 } 447 448 /** 449 * @see org.kuali.ole.sys.service.ReportWriterService#writeTableHeader(org.kuali.rice.krad.bo.BusinessObject) 450 */ 451 public void writeTableHeader(BusinessObject businessObject) { 452 BusinessObjectReportHelper businessObjectReportHelper = getBusinessObjectReportHelper(businessObject); 453 454 Map<String, String> tableDefinition = businessObjectReportHelper.getTableDefinition(); 455 String tableHeaderFormat = tableDefinition.get(OLEConstants.ReportConstants.TABLE_HEADER_LINE_KEY); 456 457 String[] headerLines = this.getMultipleFormattedMessageLines(tableHeaderFormat, new Object()); 458 this.writeMultipleFormattedMessageLines(headerLines); 459 } 460 461 /** 462 * Writes out the table header, based on a business object class 463 * @param businessObjectClass the class to write the header out for 464 */ 465 public void writeTableHeader(Class<? extends BusinessObject> businessObjectClass) { 466 BusinessObjectReportHelper businessObjectReportHelper = getBusinessObjectReportHelper(businessObjectClass); 467 468 Map<String, String> tableDefinition = businessObjectReportHelper.getTableDefinition(); 469 String tableHeaderFormat = tableDefinition.get(OLEConstants.ReportConstants.TABLE_HEADER_LINE_KEY); 470 471 String[] headerLines = this.getMultipleFormattedMessageLines(tableHeaderFormat, new Object()); 472 this.writeMultipleFormattedMessageLines(headerLines); 473 } 474 475 /** 476 * @see org.kuali.ole.sys.service.ReportWriterService#writeTableRow(org.kuali.rice.krad.bo.BusinessObject) 477 */ 478 public void writeTableRowSeparationLine(BusinessObject businessObject) { 479 BusinessObjectReportHelper businessObjectReportHelper = getBusinessObjectReportHelper(businessObject); 480 Map<String, String> tableDefinition = businessObjectReportHelper.getTableDefinition(); 481 482 String separationLine = tableDefinition.get(OLEConstants.ReportConstants.SEPARATOR_LINE_KEY); 483 this.writeFormattedMessageLine(separationLine); 484 } 485 486 /** 487 * @see org.kuali.ole.sys.service.ReportWriterService#writeTableRow(org.kuali.rice.krad.bo.BusinessObject) 488 */ 489 public void writeTableRow(BusinessObject businessObject) { 490 BusinessObjectReportHelper businessObjectReportHelper = getBusinessObjectReportHelper(businessObject); 491 Map<String, String> tableDefinition = businessObjectReportHelper.getTableDefinition(); 492 493 String tableCellFormat = tableDefinition.get(OLEConstants.ReportConstants.TABLE_CELL_FORMAT_KEY); 494 List<String> tableCellValues = businessObjectReportHelper.getTableCellValuesPaddingWithEmptyCell(businessObject, false); 495 496 String[] rowMessageLines = this.getMultipleFormattedMessageLines(tableCellFormat, tableCellValues.toArray()); 497 this.writeMultipleFormattedMessageLines(rowMessageLines); 498 } 499 500 /** 501 * @see org.kuali.ole.sys.service.ReportWriterService#writeTableRowWithColspan(org.kuali.rice.krad.bo.BusinessObject) 502 */ 503 public void writeTableRowWithColspan(BusinessObject businessObject) { 504 BusinessObjectReportHelper businessObjectReportHelper = getBusinessObjectReportHelper(businessObject); 505 Map<String, String> tableDefinition = businessObjectReportHelper.getTableDefinition(); 506 507 String tableCellFormat = businessObjectReportHelper.getTableCellFormat(true, true, StringUtils.EMPTY); 508 List<String> tableCellValues = businessObjectReportHelper.getTableCellValuesPaddingWithEmptyCell(businessObject, true); 509 510 String[] rowMessageLines = this.getMultipleFormattedMessageLines(tableCellFormat, tableCellValues.toArray()); 511 this.writeMultipleFormattedMessageLines(rowMessageLines); 512 } 513 514 /** 515 * @see org.kuali.ole.sys.service.ReportWriterService#writeTable(java.util.List, boolean, boolean) 516 */ 517 public void writeTable(List<? extends BusinessObject> businessObjects, boolean isHeaderRepeatedInNewPage, boolean isRowBreakAcrossPageAllowed) { 518 if (ObjectUtils.isNull(businessObjects) || businessObjects.isEmpty()) { 519 return; 520 } 521 522 BusinessObject firstBusinessObject = businessObjects.get(0); 523 this.writeTableHeader(firstBusinessObject); 524 525 BusinessObjectReportHelper businessObjectReportHelper = getBusinessObjectReportHelper(businessObjects.get(0)); 526 Map<String, String> tableDefinition = businessObjectReportHelper.getTableDefinition(); 527 String tableHeaderFormat = tableDefinition.get(OLEConstants.ReportConstants.TABLE_HEADER_LINE_KEY); 528 String[] headerLines = this.getMultipleFormattedMessageLines(tableHeaderFormat, new Object()); 529 530 String tableCellFormat = tableDefinition.get(OLEConstants.ReportConstants.TABLE_CELL_FORMAT_KEY); 531 532 for (BusinessObject businessObject : businessObjects) { 533 534 List<String> tableCellValues = businessObjectReportHelper.getTableCellValuesPaddingWithEmptyCell(businessObject, false); 535 String[] messageLines = this.getMultipleFormattedMessageLines(tableCellFormat, tableCellValues.toArray()); 536 537 boolean hasEnoughLinesInPage = messageLines.length <= (this.pageLength - this.line); 538 if (!hasEnoughLinesInPage && !isRowBreakAcrossPageAllowed) { 539 this.pageBreak(); 540 541 if (isHeaderRepeatedInNewPage) { 542 this.writeTableHeader(firstBusinessObject); 543 } 544 } 545 546 this.writeMultipleFormattedMessageLines(messageLines, headerLines, isRowBreakAcrossPageAllowed); 547 } 548 549 } 550 551 /** 552 * get the business report helper for the given business object 553 * 554 * @param businessObject the given business object 555 * @return the business report helper for the given business object 556 */ 557 public BusinessObjectReportHelper getBusinessObjectReportHelper(BusinessObject businessObject) { 558 if (LOG.isDebugEnabled()) { 559 if (businessObject == null) { 560 LOG.debug("reporting "+filePath+" but can't because null business object sent in"); 561 } else if (businessObjectReportHelpers == null) { 562 LOG.debug("Logging "+businessObject+" in report "+filePath+" but businessObjectReportHelpers are null"); 563 } 564 } 565 BusinessObjectReportHelper businessObjectReportHelper = this.businessObjectReportHelpers.get(businessObject.getClass()); 566 if (ObjectUtils.isNull(businessObjectReportHelper)) { 567 throw new RuntimeException(businessObject.getClass().toString() + " is not handled"); 568 } 569 570 return businessObjectReportHelper; 571 } 572 573 /** 574 * get the business report helper for the given business object 575 * 576 * @param businessObject the given business object 577 * @return the business report helper for the given business object 578 */ 579 public BusinessObjectReportHelper getBusinessObjectReportHelper(Class<? extends BusinessObject> businessObjectClass) { 580 BusinessObjectReportHelper businessObjectReportHelper = this.businessObjectReportHelpers.get(businessObjectClass); 581 if (ObjectUtils.isNull(businessObjectReportHelper)) { 582 throw new RuntimeException(businessObjectClass.getName() + " is not handled"); 583 } 584 585 return businessObjectReportHelper; 586 } 587 588 /** 589 * write the given information as multiple lines if it contains more than one line breaks 590 * 591 * @param format the given text format definition 592 * @param messageLines the given information being written out 593 * @param headerLinesInNewPage the given header lines being written in the begin of a new page 594 */ 595 protected void writeMultipleFormattedMessageLines(String[] messageLines, String[] headerLinesInNewPage, boolean isRowBreakAcrossPageAllowed) { 596 int currentPageNumber = this.page; 597 598 for (String line : messageLines) { 599 boolean hasEnoughLinesInPage = messageLines.length <= (this.pageLength - this.line); 600 if (!hasEnoughLinesInPage && !isRowBreakAcrossPageAllowed) { 601 this.pageBreak(); 602 } 603 604 if (currentPageNumber < this.page && ObjectUtils.isNotNull(headerLinesInNewPage)) { 605 currentPageNumber = this.page; 606 607 for (String headerLine : headerLinesInNewPage) { 608 this.writeFormattedMessageLine(headerLine); 609 } 610 } 611 612 this.writeFormattedMessageLine(line); 613 } 614 } 615 616 /** 617 * write the given information as multiple lines if it contains more than one line breaks 618 * 619 * @param format the given text format definition 620 * @param args the given information being written out 621 */ 622 public void writeMultipleFormattedMessageLines(String[] messageLines) { 623 this.writeMultipleFormattedMessageLines(messageLines, null, false); 624 } 625 626 public void writeMultipleFormattedMessageLines(String format, Object... args) { 627 Object[] escapedArgs = escapeArguments(args); 628 String[] messageLines = getMultipleFormattedMessageLines(format, escapedArgs); 629 writeMultipleFormattedMessageLines(messageLines); 630 } 631 632 /** 633 * This method... 634 * 635 * @param format 636 * @param args 637 * @return 638 */ 639 public String[] getMultipleFormattedMessageLines(String format, Object... args) { 640 Object[] escapedArgs = escapeArguments(args); 641 String message = String.format(format, escapedArgs); 642 return StringUtils.split(message, newLineCharacter); 643 } 644 645 /** 646 * Iterates through array and escapes special formatting characters 647 * 648 * @param args Object array to process 649 * @return Object array with String members escaped 650 */ 651 protected Object[] escapeArguments(Object... args) { 652 Object[] escapedArgs = new Object[args.length]; 653 for (int i = 0; i < args.length; i++) { 654 Object arg = args[i]; 655 if (arg == null) { 656 args[i] = ""; 657 } else if (arg != null && arg instanceof String) { 658 String escapedArg = escapeFormatCharacters((String)arg); 659 escapedArgs[i] = escapedArg; 660 } 661 else { 662 escapedArgs[i] = arg; 663 } 664 } 665 666 return escapedArgs; 667 } 668 669 /** 670 * Escapes characters in a string that have special meaning for formatting 671 * 672 * @param replacementString string to escape 673 * @return string with format characters escaped 674 * @see OLEConstants.ReportConstants.FORMAT_ESCAPE_CHARACTERS 675 */ 676 protected String escapeFormatCharacters(String replacementString) { 677 String escapedString = replacementString; 678 for (int i = 0; i < OLEConstants.ReportConstants.FORMAT_ESCAPE_CHARACTERS.length; i++) { 679 String characterToEscape = OLEConstants.ReportConstants.FORMAT_ESCAPE_CHARACTERS[i]; 680 escapedString = StringUtils.replace(escapedString, characterToEscape, characterToEscape + characterToEscape); 681 } 682 683 return escapedString; 684 } 685 686 /** 687 * Sets the filePath 688 * 689 * @param filePath The filePath to set. 690 */ 691 public void setFilePath(String filePath) { 692 this.filePath = filePath; 693 } 694 695 /** 696 * Sets the fileNamePrefix 697 * 698 * @param fileNamePrefix The fileNamePrefix to set. 699 */ 700 public void setFileNamePrefix(String fileNamePrefix) { 701 this.fileNamePrefix = fileNamePrefix; 702 } 703 704 /** 705 * Sets the fileNameSuffix 706 * 707 * @param fileNameSuffix The fileNameSuffix to set. 708 */ 709 public void setFileNameSuffix(String fileNameSuffix) { 710 this.fileNameSuffix = fileNameSuffix; 711 } 712 713 /** 714 * Sets the title 715 * 716 * @param title The title to set. 717 */ 718 public void setTitle(String title) { 719 this.title = title; 720 } 721 722 /** 723 * Sets the pageWidth 724 * 725 * @param pageWidth The pageWidth to set. 726 */ 727 public void setPageWidth(int pageWidth) { 728 this.pageWidth = pageWidth; 729 } 730 731 /** 732 * Sets the pageLength 733 * 734 * @param pageLength The pageLength to set. 735 */ 736 public void setPageLength(int pageLength) { 737 this.pageLength = pageLength; 738 } 739 740 /** 741 * Sets the initialPageNumber 742 * 743 * @param initialPageNumber The initialPageNumber to set. 744 */ 745 public void setInitialPageNumber(int initialPageNumber) { 746 this.initialPageNumber = initialPageNumber; 747 } 748 749 /** 750 * Sets the errorSubTitle 751 * 752 * @param errorSubTitle The errorSubTitle to set. 753 */ 754 public void setErrorSubTitle(String errorSubTitle) { 755 this.errorSubTitle = errorSubTitle; 756 } 757 758 /** 759 * Sets the statisticsLabel 760 * 761 * @param statisticsLabel The statisticsLabel to set. 762 */ 763 public void setStatisticsLabel(String statisticsLabel) { 764 this.statisticsLabel = statisticsLabel; 765 } 766 767 /** 768 * Sets the statisticsLeftPadding 769 * 770 * @param statisticsLeftPadding The statisticsLeftPadding to set. 771 */ 772 public void setStatisticsLeftPadding(String statisticsLeftPadding) { 773 this.statisticsLeftPadding = statisticsLeftPadding; 774 } 775 776 /** 777 * Sets the pageLabel 778 * 779 * @param pageLabel The pageLabel to set. 780 */ 781 public void setPageLabel(String pageLabel) { 782 this.pageLabel = pageLabel; 783 } 784 785 /** 786 * Sets the newLineCharacter 787 * 788 * @param newLineCharacter The newLineCharacter to set. 789 */ 790 public void setNewLineCharacter(String newLineCharacter) { 791 this.newLineCharacter = newLineCharacter; 792 } 793 794 /** 795 * Sets the DateTimeService 796 * 797 * @param dateTimeService The DateTimeService to set. 798 */ 799 public void setDateTimeService(DateTimeService dateTimeService) { 800 this.dateTimeService = dateTimeService; 801 } 802 803 /** 804 * Sets a map of BO classes to {@link BusinessObjectReportHelper} bean names, to configure which BO's will be rendered by which 805 * BusinessObjectReportHelper. This property should be configured via the spring bean definition 806 * 807 * @param classToBusinessObjectReportHelperBeanNames The classToBusinessObjectReportHelperBeanNames to set. 808 */ 809 public void setClassToBusinessObjectReportHelperBeanNames(Map<Class<? extends BusinessObject>, String> classToBusinessObjectReportHelperBeanNames) { 810 this.classToBusinessObjectReportHelperBeanNames = classToBusinessObjectReportHelperBeanNames; 811 } 812 813 /** 814 * Gets the parametersLabel attribute. 815 * @return Returns the parametersLabel. 816 */ 817 public String getParametersLabel() { 818 return parametersLabel; 819 } 820 821 /** 822 * Sets the parametersLabel attribute value. 823 * @param parametersLabel The parametersLabel to set. 824 */ 825 public void setParametersLabel(String parametersLabel) { 826 this.parametersLabel = parametersLabel; 827 } 828 829 /** 830 * Gets the parametersLeftPadding attribute. 831 * @return Returns the parametersLeftPadding. 832 */ 833 public String getParametersLeftPadding() { 834 return parametersLeftPadding; 835 } 836 837 /** 838 * Sets the parametersLeftPadding attribute value. 839 * @param parametersLeftPadding The parametersLeftPadding to set. 840 */ 841 public void setParametersLeftPadding(String parametersLeftPadding) { 842 this.parametersLeftPadding = parametersLeftPadding; 843 } 844 845 /** 846 * Gets the aggregationModeOn attribute. 847 * @return Returns the aggregationModeOn. 848 */ 849 public boolean isAggregationModeOn() { 850 return aggregationModeOn; 851 } 852 853 /** 854 * Sets the aggregationModeOn attribute value. 855 * @param aggregationModeOn The aggregationModeOn to set. 856 */ 857 public void setAggregationModeOn(boolean aggregationModeOn) { 858 this.aggregationModeOn = aggregationModeOn; 859 } 860 861 862}