View Javadoc
1   /*
2    * Copyright 2007-2009 The Kuali Foundation
3    * 
4    * Licensed under the Educational Community License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    * 
8    * http://www.opensource.org/licenses/ecl2.php
9    * 
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.kuali.ole.sys.service.impl;
17  
18  import java.io.File;
19  import java.io.FileNotFoundException;
20  import java.io.PrintStream;
21  import java.util.ArrayList;
22  import java.util.HashMap;
23  import java.util.Iterator;
24  import java.util.List;
25  import java.util.Map;
26  
27  import org.apache.commons.lang.StringUtils;
28  import org.kuali.ole.sys.OLEConstants;
29  import org.kuali.ole.sys.Message;
30  import org.kuali.ole.sys.batch.service.WrappingBatchService;
31  import org.kuali.ole.sys.context.SpringContext;
32  import org.kuali.ole.sys.report.BusinessObjectReportHelper;
33  import org.kuali.ole.sys.service.ReportWriterService;
34  import org.kuali.rice.core.api.datetime.DateTimeService;
35  import org.kuali.rice.krad.bo.BusinessObject;
36  import org.kuali.rice.krad.util.ObjectUtils;
37  
38  /**
39   * Text output implementation of <code>ReportWriterService</code> interface. If you are a developer attempting to add a new business
40   * object for error report writing, take a look at the Spring definitions for BusinessObjectReportHelper.<br>
41   * This class CANNOT be used by 2 processes simultaneously. It is for very specific batch processes that should not run at the same
42   * time, and initialize and destroy must be called and the beginning and end of each process that uses it.
43   * 
44   * @see org.kuali.ole.sys.report.BusinessObjectReportHelper
45   */
46  public class ReportWriterTextServiceImpl implements ReportWriterService, WrappingBatchService {
47      private static org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(ReportWriterTextServiceImpl.class);
48  
49      // Changing the initial line number would only affect that a page break occurs early. It does not actually print in the
50      // middle of the page. Hence changing this has little use.
51      protected static final int INITIAL_LINE_NUMBER = 0;
52  
53      protected String filePath;
54      protected String fileNamePrefix;
55      protected String fileNameSuffix;
56      protected String title;
57      protected int pageWidth;
58      protected int pageLength;
59      protected int initialPageNumber;
60      protected String errorSubTitle;
61      protected String statisticsLabel;
62      protected String statisticsLeftPadding;
63      private String parametersLabel;
64      private String parametersLeftPadding;
65      protected String pageLabel;
66      protected String newLineCharacter;
67      protected DateTimeService dateTimeService;
68      protected boolean aggregationModeOn;
69      
70      /**
71       * A map of BO classes to {@link BusinessObjectReportHelper} bean names, to configure which BO's will be rendered by which
72       * BusinessObjectReportHelper. This property should be configured via the spring bean definition
73       */
74      protected Map<Class<? extends BusinessObject>, String> classToBusinessObjectReportHelperBeanNames;
75  
76      // Local caching field to speed up the selection of formatting BusinessObjectReportHelper to use per configuration in Spring
77      protected Map<Class<? extends BusinessObject>, BusinessObjectReportHelper> businessObjectReportHelpers;
78  
79      protected PrintStream printStream;
80      protected int page;
81      protected int line = INITIAL_LINE_NUMBER;
82      protected String errorFormat;
83  
84      // Ensures that the statistics header isn't written multiple times. Does not check that a user doesn't write other stuff into
85      // the statistics
86      // section. A developer is responsible for ensuring that themselves
87      protected boolean modeStatistics = false;
88      
89      // Ensures that the parameters header isn't written multiple times. Does not check that a user doesn't write other stuff into
90      // the parameters
91      // section. A developer is responsible for ensuring that themselves
92      protected boolean modeParameters = false;
93  
94      // So that writeError knows when to writeErrorHeader
95      protected boolean newPage = true;
96  
97      // For printing new headers when the BO is changed
98      protected Class<? extends BusinessObject> businessObjectClass;
99  
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 }