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