001: /**
002: * ===========================================
003: * JFreeReport : a free Java reporting library
004: * ===========================================
005: *
006: * Project Info: http://reporting.pentaho.org/
007: *
008: * (C) Copyright 2001-2007, by Object Refinery Ltd, Pentaho Corporation and Contributors.
009: *
010: * This library is free software; you can redistribute it and/or modify it under the terms
011: * of the GNU Lesser General Public License as published by the Free Software Foundation;
012: * either version 2.1 of the License, or (at your option) any later version.
013: *
014: * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
015: * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
016: * See the GNU Lesser General Public License for more details.
017: *
018: * You should have received a copy of the GNU Lesser General Public License along with this
019: * library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
020: * Boston, MA 02111-1307, USA.
021: *
022: * [Java is a trademark or registered trademark of Sun Microsystems, Inc.
023: * in the United States and other countries.]
024: *
025: * ------------
026: * DefaultOutputFunction.java
027: * ------------
028: * (C) Copyright 2001-2007, by Object Refinery Ltd, Pentaho Corporation and Contributors.
029: */package org.jfree.report.layout.output;
030:
031: import java.util.ArrayList;
032:
033: import org.jfree.report.Band;
034: import org.jfree.report.Group;
035: import org.jfree.report.GroupFooter;
036: import org.jfree.report.GroupHeader;
037: import org.jfree.report.PageFooter;
038: import org.jfree.report.PageHeader;
039: import org.jfree.report.ReportDefinition;
040: import org.jfree.report.ReportProcessingException;
041: import org.jfree.report.Watermark;
042: import org.jfree.report.event.PageEventListener;
043: import org.jfree.report.event.ReportEvent;
044: import org.jfree.report.function.AbstractFunction;
045: import org.jfree.report.function.Expression;
046: import org.jfree.report.function.ExpressionRuntime;
047: import org.jfree.report.function.FunctionProcessingException;
048: import org.jfree.report.function.OutputFunction;
049: import org.jfree.report.layout.Renderer;
050: import org.jfree.report.states.ReportState;
051: import org.jfree.report.states.datarow.DefaultFlowController;
052: import org.jfree.report.states.datarow.GlobalMasterRow;
053: import org.jfree.report.states.datarow.ReportDataRow;
054: import org.jfree.report.style.BandStyleKeys;
055: import org.jfree.report.style.ElementStyleSheet;
056: import org.jfree.util.Log;
057:
058: /**
059: * Creation-Date: 08.04.2007, 16:22:18
060: *
061: * @author Thomas Morgner
062: */
063: public class DefaultOutputFunction extends AbstractFunction implements
064: OutputFunction, PageEventListener {
065: public static final String HANDLE_PENDING_GROUP_FOOTER_KEY = "org.jfree.report.modules.output.support.pagelayout.HandlePendingGroupFooter";
066:
067: private static final LayouterLevel[] EMPTY_LAYOUTER_LEVEL = new LayouterLevel[0];
068:
069: private ReportEvent currentEvent;
070: private Renderer renderer;
071: private boolean lastPagebreak;
072: private DefaultLayoutPagebreakHandler pagebreakHandler;
073: private boolean groupStartPending;
074:
075: /**
076: * Creates an unnamed function. Make sure the name of the function is set using {@link #setName} before the function
077: * is added to the report's function collection.
078: */
079: public DefaultOutputFunction() {
080: pagebreakHandler = new DefaultLayoutPagebreakHandler();
081: }
082:
083: /**
084: * Return the current expression value. <P> The value depends (obviously) on the expression implementation.
085: *
086: * @return the value of the function.
087: */
088: public Object getValue() {
089: return null;
090: }
091:
092: public void reportInitialized(final ReportEvent event) {
093: // there can be no pending page-start, we just have started ...
094:
095: // activating this state after the page has ended is invalid.
096: setCurrentEvent(event);
097: try {
098: // activating this state after the page has ended is invalid.
099: final ReportDefinition report = event.getReport();
100: if (event.isDeepTraversing() == false) {
101: renderer.startReport(report.getPageDefinition());
102:
103: final ReportState reportState = event.getState();
104: final ExpressionRuntime runtime = getRuntime();
105: reportState.setCurrentPage(1);
106: reportState.firePageStartedEvent(reportState
107: .getEventCode());
108: // restore the current event, as the page-started event will clear it ..
109: setRuntime(runtime);
110: setCurrentEvent(event);
111: }
112: } catch (FunctionProcessingException fe) {
113: throw fe;
114: } catch (Exception e) {
115: throw new FunctionProcessingException(
116: "ReportInitialized failed", e);
117: } finally {
118: clearCurrentEvent();
119: }
120: }
121:
122: /**
123: * Receives notification that the report has started. Also invokes the start of the first page ... <P> Layout and draw
124: * the report header after the PageStartEvent was fired.
125: *
126: * @param event the event.
127: */
128: public void reportStarted(final ReportEvent event) {
129:
130: // activating this state after the page has ended is invalid.
131: setCurrentEvent(event);
132: try {
133: // activating this state after the page has ended is invalid.
134: updateFooterArea(event);
135:
136: renderer.startSection(Renderer.TYPE_NORMALFLOW);
137: final ReportDefinition report = event.getReport();
138: print(getRuntime(), report.getReportHeader());
139: renderer.endSection();
140: } catch (FunctionProcessingException fe) {
141: throw fe;
142: } catch (Exception e) {
143: throw new FunctionProcessingException(
144: "ReportStarted failed", e);
145: } finally {
146: clearCurrentEvent();
147: }
148: }
149:
150: /**
151: * Receives notification that a group has started. <P> Prints the GroupHeader
152: *
153: * @param event Information about the event.
154: */
155: public void groupStarted(final ReportEvent event) {
156: groupStartPending = true;
157: clearPendingPageStart(event.getState());
158: groupStartPending = false;
159:
160: // activating this state after the page has ended is invalid.
161: setCurrentEvent(event);
162: try {
163: updateFooterArea(event);
164:
165: final int gidx = event.getState().getCurrentGroupIndex();
166: final Group g = event.getReport().getGroup(gidx);
167: final Band b = g.getHeader();
168: renderer.startGroup(false);
169: renderer.startSection(Renderer.TYPE_NORMALFLOW);
170: print(getRuntime(), b);
171: renderer.endSection();
172: } catch (FunctionProcessingException fe) {
173: throw fe;
174: } catch (Exception e) {
175: throw new FunctionProcessingException(
176: "GroupStarted failed", e);
177: } finally {
178: clearCurrentEvent();
179: }
180: }
181:
182: /**
183: * Receives notification that a group of item bands is about to be processed. <P> The next events will be
184: * itemsAdvanced events until the itemsFinished event is raised.
185: *
186: * @param event The event.
187: */
188: public void itemsStarted(final ReportEvent event) {
189: clearPendingPageStart(event.getState());
190:
191: setCurrentEvent(event);
192: // effectiveGroupIndex += 1;
193: // Log.debug ("Items-Started: " + effectiveGroupIndex + " SR: " + event.isDeepTraversing() + " GI:" + event.getState().getCurrentGroupIndex());
194:
195: try {
196: // activating this state after the page has ended is invalid.
197: final int numberOfRows = event.getState().getNumberOfRows();
198: if (numberOfRows == 0) {
199: // ups, we have no data. Lets signal that ...
200: try {
201: updateFooterArea(event);
202: renderer.startSection(Renderer.TYPE_NORMALFLOW);
203: print(getRuntime(), event.getReport()
204: .getNoDataBand());
205: renderer.endSection();
206: } catch (FunctionProcessingException fe) {
207: throw fe;
208: } catch (Exception e) {
209: throw new FunctionProcessingException(
210: "ItemsStarted failed", e);
211: }
212: // there will be no item-band printed.
213: }
214: } finally {
215: clearCurrentEvent();
216: }
217: }
218:
219: /**
220: * Receives notification that a row of data is being processed. <P> prints the ItemBand.
221: *
222: * @param event Information about the event.
223: */
224: public void itemsAdvanced(final ReportEvent event) {
225: clearPendingPageStart(event.getState());
226:
227: setCurrentEvent(event);
228: try {
229: updateFooterArea(event);
230:
231: renderer.startSection(Renderer.TYPE_NORMALFLOW);
232: print(getRuntime(), event.getReport().getItemBand());
233: renderer.endSection();
234: } catch (FunctionProcessingException fe) {
235: throw fe;
236: } catch (Exception e) {
237: throw new FunctionProcessingException(
238: "ItemsAdvanced failed", e);
239: } finally {
240: clearCurrentEvent();
241: }
242: }
243:
244: /**
245: * Receives notification that a group has finished. <P> Prints the GroupFooter.
246: *
247: * @param event Information about the event.
248: */
249: public void groupFinished(final ReportEvent event) {
250: clearPendingPageStart(event.getState());
251:
252: setCurrentEvent(event);
253: try {
254: updateFooterArea(event);
255:
256: final int gidx = event.getState().getCurrentGroupIndex();
257: final Group g = event.getReport().getGroup(gidx);
258: final Band b = g.getFooter();
259:
260: renderer.startSection(Renderer.TYPE_NORMALFLOW);
261: print(getRuntime(), b);
262: renderer.endSection();
263: renderer.endGroup();
264:
265: // effectiveGroupIndex -= 1;
266: // Log.debug ("Group-Finished: " + effectiveGroupIndex + " SR: " + event.isDeepTraversing() + " GI:" + event.getState().getCurrentGroupIndex());
267: } catch (FunctionProcessingException fe) {
268: throw fe;
269: } catch (Exception e) {
270: throw new FunctionProcessingException(
271: "GroupFinished failed", e);
272: } finally {
273: clearCurrentEvent();
274: }
275: }
276:
277: /**
278: * Receives notification that the report has finished. <P> Prints the ReportFooter and forces the last pagebreak.
279: *
280: * @param event Information about the event.
281: */
282: public void reportFinished(final ReportEvent event) {
283: clearPendingPageStart(event.getState());
284:
285: setCurrentEvent(event);
286: try {
287: // a deep traversing event means, we are in a subreport ..
288:
289: // force that this last pagebreak ... (This is an indicator for the
290: // pagefooter's print-on-last-page) This is highly unclean and may or
291: // may not work ..
292: final Band b = event.getReport().getReportFooter();
293: renderer.startSection(Renderer.TYPE_NORMALFLOW);
294: print(getRuntime(), b);
295: renderer.endSection();
296:
297: // if (effectiveGroupIndex != -1)
298: // {
299: // throw new IllegalStateException("Assertation failed: Group index is invalid");
300: // }
301: // Log.debug ("Report-Finished: " + effectiveGroupIndex + " SR: " + event.isDeepTraversing() + " GI:" + event.getState().getCurrentGroupIndex());
302: lastPagebreak = true;
303: updateFooterArea(event);
304: } catch (FunctionProcessingException fe) {
305: throw fe;
306: } catch (Exception e) {
307: throw new FunctionProcessingException(
308: "ReportFinished failed", e);
309: } finally {
310: clearCurrentEvent();
311: }
312: }
313:
314: /**
315: * Receives notification that report generation has completed, the report footer was printed, no more output is done.
316: * This is a helper event to shut down the output service.
317: *
318: * @param event The event.
319: */
320: public void reportDone(final ReportEvent event) {
321: if (event.isDeepTraversing() == false) {
322: final ReportState state = event.getState();
323: state.firePageFinishedEvent();
324: renderer.endReport();
325: }
326: }
327:
328: protected static LayouterLevel[] collectSubReportStates(
329: final ReportEvent event, final ExpressionRuntime parent) {
330: final ReportState state = event.getState();
331: ReportState parentState = state.getParentSubReportState();
332: if (parentState == null) {
333: return EMPTY_LAYOUTER_LEVEL;
334: }
335: GlobalMasterRow dataRow = state.getFlowController()
336: .getMasterRow();
337: dataRow = dataRow.getParentDataRow();
338:
339: final ArrayList stack = new ArrayList();
340: while (parentState != null) {
341: final ReportState realState = parentState;
342: final DefaultFlowController flowController = realState
343: .getFlowController();
344: final GlobalMasterRow masterRow = flowController
345: .getMasterRow();
346: final ReportDataRow reportDataRow = masterRow
347: .getReportDataRow();
348: final LayoutExpressionRuntime runtime = new LayoutExpressionRuntime(
349: dataRow.getGlobalView(), realState
350: .getCurrentDataItem(), reportDataRow
351: .getReportData(), parent
352: .getProcessingContext());
353: stack.add(new LayouterLevel(realState.getReport(),
354: realState.getCurrentGroupIndex(), runtime));
355: parentState = parentState.getParentSubReportState();
356: }
357: return (LayouterLevel[]) stack.toArray(new LayouterLevel[stack
358: .size()]);
359: }
360:
361: private boolean isPageHeaderPrinting(final Band b,
362: final ReportEvent event) {
363: final boolean displayOnFirstPage = b.getStyle()
364: .getBooleanStyleProperty(
365: BandStyleKeys.DISPLAY_ON_FIRSTPAGE);
366: if ((event.getState().getCurrentPage() == 1)
367: && (displayOnFirstPage == false)) {
368: return false;
369: }
370:
371: final boolean displayOnLastPage = b.getStyle()
372: .getBooleanStyleProperty(
373: BandStyleKeys.DISPLAY_ON_LASTPAGE);
374: if (isLastPagebreak() && (displayOnLastPage == false)) {
375: return false;
376: }
377:
378: return true;
379: }
380:
381: protected boolean isLastPagebreak() {
382: return lastPagebreak;
383: }
384:
385: /**
386: * Receives notification that a page has started. <P> This prints the PageHeader. If this is the first page, the
387: * header is not printed if the pageheader style-flag DISPLAY_ON_FIRSTPAGE is set to false. If this event is known to
388: * be the last pageStarted event, the DISPLAY_ON_LASTPAGE is evaluated and the header is printed only if this flag is
389: * set to TRUE.
390: * <p/>
391: * If there is an active repeating GroupHeader, print the last one. The GroupHeader is searched for the current group
392: * and all parent groups, starting at the current group and ascending to the parents. The first goupheader that has
393: * the StyleFlag REPEAT_HEADER set to TRUE is printed.
394: * <p/>
395: * The PageHeader and the repeating GroupHeader are spooled until the first real content is printed. This way, the
396: * LogicalPage remains empty until an other band is printed.
397: *
398: * @param event Information about the event.
399: */
400: public void pageStarted(final ReportEvent event) {
401: // activating this state after the page has ended is invalid.
402: setCurrentEvent(event);
403: try {
404: updateHeaderArea(event);
405: } catch (FunctionProcessingException fe) {
406: throw fe;
407: } catch (Exception e) {
408: throw new FunctionProcessingException("PageStarted failed",
409: e);
410: } finally {
411: clearCurrentEvent();
412: }
413: }
414:
415: protected void updateHeaderArea(final ReportEvent event)
416: throws ReportProcessingException {
417: final ReportDefinition report = event.getReport();
418: LayouterLevel[] levels = null;
419: final OutputProcessorMetaData metaData = renderer
420: .getOutputProcessor().getMetaData();
421: if (metaData
422: .isFeatureSupported(OutputProcessorFeature.WATERMARK_SECTION)) {
423: renderer.startSection(Renderer.TYPE_WATERMARK);
424: // a new page has started, so reset the cursor ...
425: // Check the subreport for sticky watermarks ...
426: levels = collectSubReportStates(event, getRuntime());
427:
428: for (int i = levels.length - 1; i >= 0; i -= 1) {
429: final LayouterLevel level = levels[i];
430: final ReportDefinition def = level
431: .getReportDefinition();
432: final Watermark watermark = def.getWatermark();
433: if (watermark.isSticky()
434: && isPageHeaderPrinting(watermark, event)) {
435: print(level.getRuntime(), watermark);
436: }
437: }
438:
439: // and finally print the watermark of the subreport itself ..
440: final Band watermark = report.getWatermark();
441: if (isPageHeaderPrinting(watermark, event)) {
442: print(getRuntime(), watermark);
443: }
444: renderer.endSection();
445: }
446:
447: if (metaData
448: .isFeatureSupported(OutputProcessorFeature.PAGE_SECTIONS)) {
449: renderer.startSection(Renderer.TYPE_HEADER);
450: // after printing the watermark, we are still at the top of the page.
451:
452: if (levels == null) {
453: levels = collectSubReportStates(event, getRuntime());
454: }
455:
456: for (int i = levels.length - 1; i >= 0; i -= 1) {
457: // This is propably wrong (or at least incomplete) in case a subreport uses header or footer which should
458: // not be printed with the report-footer or header ..
459: final LayouterLevel level = levels[i];
460: final ReportDefinition def = level
461: .getReportDefinition();
462: final PageHeader header = def.getPageHeader();
463: if (header.isSticky()
464: && isPageHeaderPrinting(header, event)) {
465: print(level.getRuntime(), header);
466: }
467: }
468:
469: // and print the ordinary page header ..
470: final Band b = report.getPageHeader();
471: if (isPageHeaderPrinting(b, event)) {
472: print(getRuntime(), b);
473: }
474:
475: /**
476: * Repeating group header are only printed while ItemElements are
477: * processed.
478: *
479: * Dive into the pending group to print the group header ...
480: */
481:
482: for (int i = levels.length - 1; i >= 0; i -= 1) {
483: final LayouterLevel level = levels[i];
484: final ReportDefinition def = level
485: .getReportDefinition();
486:
487: for (int gidx = 0; gidx < level.getGroupIndex(); gidx++) {
488: final Group g = def.getGroup(gidx);
489: final GroupHeader header = g.getHeader();
490: if (header.isSticky()
491: && header
492: .getStyle()
493: .getBooleanStyleProperty(
494: BandStyleKeys.REPEAT_HEADER)) {
495: print(level.getRuntime(), header);
496: }
497: }
498: }
499:
500: final int groupsPrinted;
501: if (groupStartPending) {
502: groupsPrinted = event.getState().getCurrentGroupIndex() - 1;
503: } else {
504: groupsPrinted = event.getState().getCurrentGroupIndex();
505: }
506:
507: for (int gidx = 0; gidx <= groupsPrinted; gidx++) {
508: final Group g = report.getGroup(gidx);
509: final GroupHeader header = g.getHeader();
510: if (header.getStyle().getBooleanStyleProperty(
511: BandStyleKeys.REPEAT_HEADER)) {
512: print(getRuntime(), header);
513: }
514: }
515:
516: renderer.endSection();
517: }
518: // mark the current position to calculate the maxBand-Height
519: }
520:
521: /**
522: * Receives notification that a page has ended.
523: * <p/>
524: * This prints the PageFooter. If this is the first page, the footer is not printed if the pagefooter style-flag
525: * DISPLAY_ON_FIRSTPAGE is set to false. If this event is known to be the last pageFinished event, the
526: * DISPLAY_ON_LASTPAGE is evaluated and the footer is printed only if this flag is set to TRUE.
527: * <p/>
528: *
529: * @param event the report event.
530: */
531: public void pageFinished(final ReportEvent event) {
532: setCurrentEvent(event);
533: try {
534: updateFooterArea(event);
535: } catch (FunctionProcessingException fe) {
536: throw fe;
537: } catch (Exception e) {
538: throw new FunctionProcessingException(
539: "PageFinished failed", e);
540: } finally {
541: clearCurrentEvent();
542: }
543: }
544:
545: protected void updateFooterArea(final ReportEvent event)
546: throws ReportProcessingException {
547: final OutputProcessorMetaData metaData = renderer
548: .getOutputProcessor().getMetaData();
549: if (metaData
550: .isFeatureSupported(OutputProcessorFeature.PAGE_SECTIONS) == false) {
551: return;
552: }
553:
554: renderer.startSection(Renderer.TYPE_FOOTER);
555:
556: final ReportDefinition report = event.getReport();
557: /**
558: * Repeating group header are only printed while ItemElements are
559: * processed.
560: */
561: final int groupsPrinted = event.getState()
562: .getCurrentGroupIndex();
563: for (int gidx = groupsPrinted; gidx >= 0; gidx -= 1) {
564: final Group g = report.getGroup(gidx);
565: final GroupFooter footer = g.getFooter();
566: if (footer.getStyle().getBooleanStyleProperty(
567: BandStyleKeys.REPEAT_HEADER)) {
568: print(getRuntime(), footer);
569: }
570: }
571:
572: final LayouterLevel[] levels = collectSubReportStates(event,
573: getRuntime());
574: final int levelCount = levels.length;
575: for (int i = 0; i < levelCount; i++) {
576: final LayouterLevel level = levels[i];
577: final ReportDefinition def = level.getReportDefinition();
578: for (int gidx = level.getGroupIndex(); gidx >= 0; gidx -= 1) {
579: final Group g = def.getGroup(gidx);
580: final GroupFooter footer = g.getFooter();
581: final ElementStyleSheet groupFooterStyle = footer
582: .getStyle();
583: if (footer.isSticky()
584: && groupFooterStyle.getBooleanStyleProperty(
585: BandStyleKeys.REPEAT_HEADER, false)) {
586: print(level.getRuntime(), footer);
587: }
588: }
589: }
590:
591: final Band pageFooter = report.getPageFooter();
592: final ElementStyleSheet pageFooterStyle = pageFooter.getStyle();
593: if (event.getState().getCurrentPage() == 1) {
594: if (pageFooterStyle
595: .getBooleanStyleProperty(BandStyleKeys.DISPLAY_ON_FIRSTPAGE) == true) {
596: print(getRuntime(), pageFooter);
597: }
598: } else if (isLastPagebreak()) {
599: if (pageFooterStyle.getBooleanStyleProperty(
600: BandStyleKeys.DISPLAY_ON_LASTPAGE, false) == true) {
601: print(getRuntime(), pageFooter);
602: }
603: } else {
604: print(getRuntime(), pageFooter);
605: }
606:
607: for (int i = 0; i < levelCount; i++) {
608: final LayouterLevel level = levels[i];
609: final ReportDefinition def = level.getReportDefinition();
610: final PageFooter b = def.getPageFooter();
611: if (b.isSticky() == false) {
612: continue;
613: }
614:
615: final ElementStyleSheet style = b.getStyle();
616: if (event.getState().getCurrentPage() == 1) {
617: if (style
618: .getBooleanStyleProperty(BandStyleKeys.DISPLAY_ON_FIRSTPAGE) == true) {
619: print(level.getRuntime(), b);
620: }
621: } else if (isLastPagebreak()) {
622: if (style
623: .getBooleanStyleProperty(BandStyleKeys.DISPLAY_ON_LASTPAGE) == true) {
624: print(level.getRuntime(), b);
625: }
626: } else {
627: print(level.getRuntime(), b);
628: }
629: }
630: renderer.endSection();
631: }
632:
633: /**
634: * Returns the current report event.
635: *
636: * @return the event.
637: */
638: protected ReportEvent getCurrentEvent() {
639: return currentEvent;
640: }
641:
642: /**
643: * Sets the current event (also updates the report reference).
644: *
645: * @param currentEvent event.
646: */
647: protected void setCurrentEvent(final ReportEvent currentEvent) {
648: if (currentEvent == null) {
649: throw new NullPointerException("Event must not be null.");
650: }
651: this .currentEvent = currentEvent;
652: this .pagebreakHandler.setReportState(currentEvent.getState());
653: this .renderer.setStateKey(currentEvent.getState()
654: .getProcessKey());
655: }
656:
657: /**
658: * Clears the current event.
659: */
660: protected void clearCurrentEvent() {
661: if (currentEvent == null) {
662: Log
663: .error(
664: "Failed to clear current event; we don't have an event set!",
665: new IllegalStateException(
666: "stacktrace generated:"));
667: }
668: this .currentEvent = null;
669: this .pagebreakHandler.setReportState(null);
670: this .renderer.setStateKey(null);
671: }
672:
673: /**
674: * Clones the function. <P> Be aware, this does not create a deep copy. If you have complex strucures contained in
675: * objects, you have to override this function.
676: *
677: * @return a clone of this function.
678: * @throws CloneNotSupportedException this should never happen.
679: */
680: public final Object clone() throws CloneNotSupportedException {
681: final DefaultOutputFunction sl = (DefaultOutputFunction) super
682: .clone();
683: sl.currentEvent = null;
684: return sl;
685: }
686:
687: public Expression getInstance() {
688: return deriveForStorage();
689: }
690:
691: /**
692: * Creates a storage-copy of the output function. A storage copy must create a deep clone of all referenced objects so
693: * that it is guaranteed that changes to either the original or the clone do not affect the other instance.
694: * <p/>
695: * Any failure to implement this method correctly will be a great source of very subtle bugs.
696: *
697: * @return the deep clone.
698: */
699: public OutputFunction deriveForStorage() {
700: try {
701: final DefaultOutputFunction sl = (DefaultOutputFunction) super
702: .clone();
703: sl.renderer = renderer.deriveForStorage();
704: sl.currentEvent = null;
705: sl.pagebreakHandler = (DefaultLayoutPagebreakHandler) pagebreakHandler
706: .clone();
707: sl.pagebreakHandler.setReportState(null);
708: return sl;
709: } catch (CloneNotSupportedException e) {
710: throw new IllegalStateException();
711: }
712: }
713:
714: /**
715: * Creates a cheaper version of the deep-copy of the output function. A pagebreak-derivate is created on every possible
716: * pagebreak position and must contain all undo/rollback information to restore the state of any shared object when
717: * a roll-back is requested.
718: * <p/>
719: * Any failure to implement this method correctly will be a great source of very subtle bugs.
720: *
721: * @return the deep clone.
722: */
723: public OutputFunction deriveForPagebreak() {
724: try {
725: final DefaultOutputFunction sl = (DefaultOutputFunction) super
726: .clone();
727: sl.renderer = renderer.deriveForPagebreak();
728: sl.currentEvent = null;
729: sl.pagebreakHandler = (DefaultLayoutPagebreakHandler) pagebreakHandler
730: .clone();
731: return sl;
732: } catch (CloneNotSupportedException e) {
733: throw new IllegalStateException();
734: }
735: }
736:
737: public void setRenderer(final Renderer renderer) {
738: this .renderer = renderer;
739: }
740:
741: public Renderer getRenderer() {
742: return renderer;
743: }
744:
745: /**
746: * Prints the given band at the current cursor position.
747: *
748: * @param dataRow the datarow for evaluating the band's value-expressions.
749: * @param band the band to be printed.
750: */
751: protected void print(final ExpressionRuntime dataRow,
752: final Band band) {
753: renderer.add(band, dataRow, getCurrentEvent().getState()
754: .getProcessKey());
755: }
756:
757: private void clearPendingPageStart(final ReportState state) {
758: pagebreakHandler.setReportState(state);
759: try {
760: renderer.clearPendingPageStart(pagebreakHandler);
761: } finally {
762: pagebreakHandler.setReportState(null);
763: }
764: }
765:
766: }
|