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: * ProcessState.java
027: * ------------
028: * (C) Copyright 2001-2007, by Object Refinery Ltd, Pentaho Corporation and Contributors.
029: */package org.jfree.report.states.process;
030:
031: import org.jfree.report.DataRow;
032: import org.jfree.report.Group;
033: import org.jfree.report.JFreeReport;
034: import org.jfree.report.ParameterMapping;
035: import org.jfree.report.ReportDefinition;
036: import org.jfree.report.ReportProcessingException;
037: import org.jfree.report.SubReport;
038: import org.jfree.report.event.ReportEvent;
039: import org.jfree.report.function.Expression;
040: import org.jfree.report.function.ProcessingContext;
041: import org.jfree.report.states.CachingDataFactory;
042: import org.jfree.report.states.FunctionStorage;
043: import org.jfree.report.states.FunctionStorageKey;
044: import org.jfree.report.states.IgnoreEverythingReportErrorHandler;
045: import org.jfree.report.states.LayoutProcess;
046: import org.jfree.report.states.ReportDefinitionImpl;
047: import org.jfree.report.states.ReportProcessingErrorHandler;
048: import org.jfree.report.states.ReportState;
049: import org.jfree.report.states.ReportStateKey;
050: import org.jfree.report.states.datarow.DefaultFlowController;
051: import org.jfree.report.states.datarow.GlobalMasterRow;
052: import org.jfree.report.states.datarow.ReportDataRow;
053: import org.jfree.report.util.ReportProperties;
054:
055: /**
056: * Creation-Date: 03.07.2007, 12:56:46
057: *
058: * @author Thomas Morgner
059: */
060: public class ProcessState implements ReportState {
061: public static final int ARTIFICIAL_EVENT_CODE = 0x80000000;
062:
063: private int currentGroupIndex;
064: private ReportDefinitionImpl report;
065: private ReportProperties reportProperties;
066:
067: private int currentSubReport;
068: private SubReport[] subReports;
069: private ProcessState parentState;
070: private ProcessState parentSubReportState;
071: private FunctionStorage functionStorage;
072:
073: private DefaultFlowController flowController;
074: private LayoutProcess layoutProcess;
075: private ReportStateKey processKey;
076: private AdvanceHandler advanceHandler;
077: private int currentPage;
078: private ReportProcessingErrorHandler errorHandler;
079: private int sequenceCounter;
080:
081: public ProcessState(final JFreeReport report,
082: final ProcessingContext processingContext,
083: final LayoutProcess layoutProcess)
084: throws ReportProcessingException {
085: if (layoutProcess == null) {
086: throw new NullPointerException(
087: "LayoutProcess must not be null.");
088: }
089: if (report == null) {
090: throw new NullPointerException("Report must not be null");
091: }
092: if (processingContext == null) {
093: throw new NullPointerException(
094: "ProcessingContext must not be null.");
095: }
096:
097: this .errorHandler = IgnoreEverythingReportErrorHandler.INSTANCE;
098: this .advanceHandler = BeginReportHandler.HANDLER;
099: this .parentState = null;
100: this .currentPage = BEFORE_FIRST_PAGE;
101: this .currentSubReport = -1;
102: this .currentGroupIndex = BEFORE_FIRST_GROUP;
103: this .functionStorage = new FunctionStorage();
104: this .processKey = new ReportStateKey(null, BEFORE_FIRST_ROW, 0,
105: BEFORE_FIRST_GROUP, -1);
106: this .reportProperties = report.getProperties();
107: this .report = new ReportDefinitionImpl(report, report
108: .getPageDefinition());
109:
110: final DefaultFlowController flowController = new DefaultFlowController(
111: processingContext, report.getDataFactory(), this .report
112: .getProperties());
113: final DefaultFlowController qfc = flowController
114: .performQuery(getReport().getQuery());
115: this .flowController = qfc.activateExpressions(report
116: .getExpressions().getExpressions(), false);
117:
118: this .report.getDataRowConnector().setDataRowBackend(
119: this .flowController.getMasterRow().getGlobalView());
120: this .layoutProcess = layoutProcess;
121: this .processKey = createKey();
122: }
123:
124: public ProcessState(final SubReport[] subReports,
125: final int subReportIndex, final ProcessState parentState)
126: throws ReportProcessingException {
127: this .parentState = parentState;
128: this .parentSubReportState = parentState;
129: this .advanceHandler = BeginReportHandler.HANDLER;
130: this .errorHandler = parentState.errorHandler;
131: this .functionStorage = parentState.functionStorage;
132: this .layoutProcess = parentState.layoutProcess;
133:
134: this .currentPage = BEFORE_FIRST_PAGE;
135: this .currentGroupIndex = BEFORE_FIRST_GROUP;
136: this .currentSubReport = subReportIndex;
137: this .flowController = parentState.flowController;
138: this .subReports = subReports;
139: final SubReport report = subReports[subReportIndex];
140: this .reportProperties = report.getProperties();
141: this .reportProperties.setMasterProperties(parentState
142: .getReportProperties());
143:
144: final ReportDefinition parentReport = parentState.getReport();
145: this .report = new ReportDefinitionImpl(report, parentReport
146: .getPageDefinition());
147: this .sequenceCounter = parentState.getSequenceCounter() + 1;
148:
149: // And now initialize the sub-report.
150: final ParameterMapping[] inputMappings = report
151: .getInputMappings();
152: final ParameterMapping[] exportMappings = report
153: .getExportMappings();
154: final DefaultFlowController qfc = parentState.flowController
155: .performSubReportQuery(report.getQuery(),
156: inputMappings, exportMappings);
157:
158: boolean preserve = true;
159: Expression[] expressions = getFunctionStorage().restore(
160: new FunctionStorageKey(parentSubReportState
161: .getProcessKey(), subReportIndex));
162: if (expressions == null) {
163: // ok, it seems we have entered a new subreport ..
164: // we use the expressions from the report itself ..
165: expressions = report.getExpressions().getExpressions();
166: preserve = false;
167: }
168:
169: this .flowController = qfc.activateExpressions(expressions,
170: preserve);
171: this .report.getDataRowConnector().setDataRowBackend(
172: this .flowController.getMasterRow().getGlobalView());
173: this .processKey = createKey();
174: }
175:
176: public ProcessState(final ProcessState parentState) {
177: this .advanceHandler = parentState.advanceHandler;
178: this .currentGroupIndex = parentState.currentGroupIndex;
179: this .currentPage = parentState.currentPage;
180: this .currentSubReport = parentState.currentSubReport;
181: this .errorHandler = parentState.errorHandler;
182: this .flowController = parentState.flowController;
183: this .functionStorage = parentState.functionStorage;
184: this .layoutProcess = parentState.layoutProcess;
185: this .parentState = parentState;
186: this .parentSubReportState = parentState.parentSubReportState;
187: this .report = parentState.report;
188: this .reportProperties = parentState.reportProperties;
189: this .sequenceCounter = parentState.getSequenceCounter() + 1;
190: this .processKey = createKey();
191: }
192:
193: public ProcessState restart() throws ReportProcessingException {
194: final ProcessState state = this .deriveForStorage();
195: state.currentPage = BEFORE_FIRST_PAGE;
196: state.currentSubReport = -1;
197: state.currentGroupIndex = BEFORE_FIRST_GROUP;
198: state.setAdvanceHandler(BeginReportHandler.HANDLER);
199:
200: final DefaultFlowController fc = state.getFlowController();
201: final DefaultFlowController qfc = fc.performQuery(getReport()
202: .getQuery());
203: final Expression[] expressions = getFunctionStorage().restore(
204: new FunctionStorageKey(null, currentSubReport));
205: final DefaultFlowController efc = qfc.activateExpressions(
206: expressions, true);
207: state.setFlowController(efc);
208: state.sequenceCounter += 1;
209: state.processKey = createKey();
210: return state;
211: }
212:
213: public ReportProcessingErrorHandler getErrorHandler() {
214: return errorHandler;
215: }
216:
217: public void setErrorHandler(
218: final ReportProcessingErrorHandler errorHandler) {
219: this .errorHandler = errorHandler;
220: }
221:
222: public void setSequenceCounter(final int sequenceCounter) {
223: this .sequenceCounter = sequenceCounter;
224: }
225:
226: public int getSequenceCounter() {
227: return sequenceCounter;
228: }
229:
230: /**
231: * This is a more expensive version of the ordinary derive. This method creates a separate copy of the layout-process
232: * so that this operation is expensive in memory and CPU usage.
233: *
234: * @return
235: */
236: public ProcessState deriveForPagebreak() {
237: try {
238: final ProcessState processState = (ProcessState) clone();
239: processState.sequenceCounter += 1;
240: processState.flowController = flowController.derive();
241: processState.report = (ReportDefinitionImpl) report.clone();
242: processState.report.getDataRowConnector()
243: .setDataRowBackend(processState.getDataRow());
244: processState.layoutProcess = layoutProcess
245: .deriveForPagebreak();
246: return processState;
247: } catch (CloneNotSupportedException e) {
248: throw new IllegalStateException(
249: "Clone failed but I dont know why ..");
250: }
251: }
252:
253: public ProcessState deriveForAdvance() {
254: try {
255: final ProcessState processState = (ProcessState) clone();
256: processState.sequenceCounter += 1;
257: return processState;
258: } catch (CloneNotSupportedException e) {
259: throw new IllegalStateException(
260: "Clone failed but I dont know why ..");
261: }
262: }
263:
264: public ProcessState deriveForStorage() {
265: try {
266: final ProcessState result = (ProcessState) clone();
267: result.flowController = flowController.derive();
268: result.report = (ReportDefinitionImpl) report.clone();
269: result.report.getDataRowConnector().setDataRowBackend(
270: result.getDataRow());
271: result.layoutProcess = layoutProcess.deriveForStorage();
272: return result;
273: } catch (CloneNotSupportedException e) {
274: throw new IllegalStateException(
275: "Clone failed but I dont know why ..");
276: }
277: }
278:
279: public Object clone() throws CloneNotSupportedException {
280: final ProcessState result = (ProcessState) super .clone();
281: result.processKey = createKey();
282: return result;
283: }
284:
285: public AdvanceHandler getAdvanceHandler() {
286: return advanceHandler;
287: }
288:
289: private ReportStateKey createKey() {
290: if (parentState != null) {
291: return new ReportStateKey(parentState.createKey(),
292: getCurrentDataItem(), getEventCode(),
293: getCurrentGroupIndex(), getCurrentSubReport());
294: }
295:
296: return new ReportStateKey(null, getCurrentDataItem(),
297: getEventCode(), getCurrentGroupIndex(),
298: getCurrentSubReport());
299: }
300:
301: public void setAdvanceHandler(final AdvanceHandler advanceHandler) {
302: if (advanceHandler == null) {
303: throw new NullPointerException();
304: }
305: this .advanceHandler = advanceHandler;
306: }
307:
308: public final ProcessState advance()
309: throws ReportProcessingException {
310: return advanceHandler.advance(this );
311: }
312:
313: public final ProcessState commit() throws ReportProcessingException {
314: return advanceHandler.commit(this );
315: }
316:
317: public int getCurrentDataItem() {
318: return this .flowController.getCursor();
319: }
320:
321: public int getProgressLevel() {
322: return flowController.getReportContext().getProgressLevel();
323: }
324:
325: public int getProgressLevelCount() {
326: return flowController.getReportContext()
327: .getProgressLevelCount();
328: }
329:
330: public boolean isPrepareRun() {
331: return flowController.getReportContext().isPrepareRun();
332: }
333:
334: public int getLevel() {
335: return flowController.getReportContext().getProcessingLevel();
336: }
337:
338: public boolean isFinish() {
339: return advanceHandler.isFinish();
340: }
341:
342: public int getEventCode() {
343: return advanceHandler.getEventCode();
344: }
345:
346: public int getCurrentGroupIndex() {
347: return currentGroupIndex;
348: }
349:
350: public void setCurrentGroupIndex(final int currentGroupIndex) {
351: this .currentGroupIndex = currentGroupIndex;
352: }
353:
354: public ReportDefinition getReport() {
355: return report;
356: }
357:
358: private ReportDefinitionImpl getReportDefinition() {
359: return report;
360: }
361:
362: public void setReport(final ReportDefinitionImpl report) {
363: this .report = report;
364: }
365:
366: public ReportProperties getReportProperties() {
367: return reportProperties;
368: }
369:
370: public void setReportProperties(
371: final ReportProperties reportProperties) {
372: this .reportProperties = reportProperties;
373: }
374:
375: public int getCurrentSubReport() {
376: return currentSubReport;
377: }
378:
379: public void setCurrentSubReport(final int currentSubReport) {
380: this .currentSubReport = currentSubReport;
381: }
382:
383: public ReportState getParentState() {
384: return parentState;
385: }
386:
387: public ReportState getParentSubReportState() {
388: return parentSubReportState;
389: }
390:
391: public FunctionStorage getFunctionStorage() {
392: return functionStorage;
393: }
394:
395: public void setFunctionStorage(final FunctionStorage functionStorage) {
396: this .functionStorage = functionStorage;
397: }
398:
399: public DefaultFlowController getFlowController() {
400: return flowController;
401: }
402:
403: public void setFlowController(
404: final DefaultFlowController flowController) {
405: if (flowController == null) {
406: throw new NullPointerException();
407: }
408: this .flowController = flowController;
409: this .report.getDataRowConnector().setDataRowBackend(
410: flowController.getMasterRow().getGlobalView());
411: }
412:
413: public LayoutProcess getLayoutProcess() {
414: return layoutProcess;
415: }
416:
417: public void setLayoutProcess(final LayoutProcess layoutProcess) {
418: this .layoutProcess = layoutProcess;
419: }
420:
421: public ReportStateKey getProcessKey() {
422: return processKey;
423: }
424:
425: public void setProcessKey(final ReportStateKey processKey) {
426: this .processKey = processKey;
427: }
428:
429: public DataRow getDataRow() {
430: return flowController.getMasterRow().getGlobalView();
431: }
432:
433: public int getNumberOfRows() {
434: final GlobalMasterRow masterRow = flowController.getMasterRow();
435: final ReportDataRow reportDataRow = masterRow
436: .getReportDataRow();
437: if (reportDataRow != null) {
438: return reportDataRow.getReportData().getRowCount();
439: }
440: return 0;
441: }
442:
443: /**
444: * Fires a 'prepare' event.
445: */
446: public void firePrepareEvent() {
447: if (flowController.getMasterRow().isPrepareEventListener() == false
448: && layoutProcess.isPrepareListener() == false) {
449: return;
450: }
451:
452: final ReportEvent event = new ReportEvent(this ,
453: (ReportEvent.PREPARE_EVENT | getEventCode()));
454: flowController = flowController.fireReportEvent(event);
455: getReportDefinition().getDataRowConnector().setDataRowBackend(
456: getDataRow());
457: layoutProcess.fireReportEvent(event);
458: }
459:
460: /**
461: * Fires a 'page-started' event.
462: *
463: * @param baseEvent the type of the base event which caused the page start to be triggered.
464: */
465: public void firePageStartedEvent(final int baseEvent) {
466: final ReportEvent event = new ReportEvent(this ,
467: ReportEvent.PAGE_STARTED | baseEvent);
468: flowController = flowController.fireReportEvent(event);
469: getReportDefinition().getDataRowConnector().setDataRowBackend(
470: getDataRow());
471: layoutProcess.fireReportEvent(event);
472: }
473:
474: /**
475: * Fires a '<code>page-finished</code>' event. The <code>pageFinished(...)</code> method is called for every report
476: * function.
477: */
478: public void firePageFinishedEvent() {
479: final ReportEvent event = new ReportEvent(this ,
480: ReportEvent.PAGE_FINISHED);
481: flowController = flowController.fireReportEvent(event);
482: getReportDefinition().getDataRowConnector().setDataRowBackend(
483: getDataRow());
484: layoutProcess.fireReportEvent(event);
485: }
486:
487: protected void fireReportEvent() {
488: if ((advanceHandler.getEventCode() & ARTIFICIAL_EVENT_CODE) == ARTIFICIAL_EVENT_CODE) {
489: throw new IllegalStateException(
490: "Cannot fire artificial events.");
491: }
492:
493: final ReportEvent event = new ReportEvent(this , advanceHandler
494: .getEventCode());
495: flowController = flowController.fireReportEvent(event);
496: getReportDefinition().getDataRowConnector().setDataRowBackend(
497: getDataRow());
498: layoutProcess.fireReportEvent(event);
499: }
500:
501: /**
502: * Returns the value of a property with the specified name.
503: *
504: * @param key the property name.
505: * @return the property value.
506: */
507: public Object getProperty(final String key) {
508: return reportProperties.get(key);
509: }
510:
511: /**
512: * Returns a property with the specified name. If no property with the specified name is found, returns def.
513: *
514: * @param key the property name.
515: * @param def the default value.
516: * @return the property value.
517: */
518: public Object getProperty(final String key, final Object def) {
519: return reportProperties.get(key, def);
520: }
521:
522: /**
523: * Sets a property.
524: *
525: * @param key the property name.
526: * @param o the property value.
527: */
528: public void setProperty(final String key, final Object o) {
529: reportProperties.put(key, o);
530: }
531:
532: /**
533: * Returns the report properties.
534: *
535: * @return the report properties.
536: */
537: public ReportProperties getProperties() {
538: return reportProperties;
539: }
540:
541: /**
542: * Returns true if this is the last item in the group, and false otherwise.
543: *
544: * @param g the group that should be checked.
545: * @param currentDataRow the current data row.
546: * @param nextDataRow the next data row, or null, if this is the last datarow.
547: * @return A flag indicating whether or not the current item is the last in its group.
548: */
549: public static boolean isLastItemInGroup(final Group g,
550: final GlobalMasterRow currentDataRow,
551: final GlobalMasterRow nextDataRow) {
552: // return true if this is the last row in the model.
553: if (currentDataRow.isAdvanceable() == false
554: || nextDataRow == null) {
555: return true;
556: }
557:
558: final DataRow nextView = nextDataRow.getGlobalView();
559:
560: // compare item and item+1, if any field differs, then item==last in group
561: final String[] fieldsCached = g.getFieldsArray();
562: for (int i = 0; i < fieldsCached.length; i++) {
563: final String field = fieldsCached[i];
564: final int column2 = nextView.findColumn(field);
565: if (column2 == -1) {
566: continue;
567: }
568:
569: if (nextView.isChanged(column2)) {
570: return true;
571: }
572: }
573: return false;
574: }
575:
576: public boolean isDeepTraversing() {
577: return parentState != null;
578: }
579:
580: public int getCurrentPage() {
581: return currentPage;
582: }
583:
584: public void setCurrentPage(final int currentPage) {
585: this .currentPage = currentPage;
586: }
587:
588: public SubReport[] getSubReports() {
589: return subReports;
590: }
591:
592: public CachingDataFactory getDataFactory() {
593: return flowController.getDataFactory();
594: }
595: }
|