001: /**
002: * ========================================
003: * JFreeReport : a free Java report library
004: * ========================================
005: *
006: * Project Info: http://reporting.pentaho.org/
007: *
008: * (C) Copyright 2000-2007, by Object Refinery Limited, 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: * $Id: ElementLayoutController.java 3525 2007-10-16 11:43:48Z tmorgner $
027: * ------------
028: * (C) Copyright 2000-2005, by Object Refinery Limited.
029: * (C) Copyright 2005-2007, by Pentaho Corporation.
030: */package org.jfree.report.flow.layoutprocessor;
031:
032: import org.jfree.report.DataSourceException;
033: import org.jfree.report.ReportDataFactoryException;
034: import org.jfree.report.ReportProcessingException;
035: import org.jfree.report.data.ExpressionSlot;
036: import org.jfree.report.data.PrecomputeNodeKey;
037: import org.jfree.report.data.PrecomputedExpressionSlot;
038: import org.jfree.report.data.PrecomputedValueRegistry;
039: import org.jfree.report.data.RunningExpressionSlot;
040: import org.jfree.report.data.StaticExpressionRuntimeData;
041: import org.jfree.report.expressions.Expression;
042: import org.jfree.report.flow.FlowControlOperation;
043: import org.jfree.report.flow.FlowController;
044: import org.jfree.report.flow.ReportTarget;
045: import org.jfree.report.flow.LayoutExpressionRuntime;
046: import org.jfree.report.structure.Element;
047: import org.jfree.util.Log;
048: import org.jfree.layouting.util.AttributeMap;
049:
050: /**
051: * Creation-Date: 24.11.2006, 13:56:30
052: *
053: * @author Thomas Morgner
054: */
055: public abstract class ElementLayoutController implements
056: LayoutController {
057: protected static class ElementPrecomputeKey implements
058: PrecomputeNodeKey {
059: private String name;
060: private String id;
061: private String namespace;
062: private String tagName;
063:
064: protected ElementPrecomputeKey(final Element element) {
065: this .name = element.getName();
066: this .tagName = element.getType();
067: this .namespace = element.getNamespace();
068: this .id = element.getId();
069: }
070:
071: public boolean equals(final Object obj) {
072: if (this == obj) {
073: return true;
074: }
075: if (obj == null || getClass() != obj.getClass()) {
076: return false;
077: }
078:
079: final ElementPrecomputeKey that = (ElementPrecomputeKey) obj;
080:
081: if (id != null ? !id.equals(that.id) : that.id != null) {
082: return false;
083: }
084: if (name != null ? !name.equals(that.name)
085: : that.name != null) {
086: return false;
087: }
088: if (namespace != null ? !namespace.equals(that.namespace)
089: : that.namespace != null) {
090: return false;
091: }
092: if (tagName != null ? !tagName.equals(that.tagName)
093: : that.tagName != null) {
094: return false;
095: }
096:
097: return true;
098: }
099:
100: public int hashCode() {
101: int result = (name != null ? name.hashCode() : 0);
102: result = 29 * result + (id != null ? id.hashCode() : 0);
103: result = 29 * result
104: + (namespace != null ? namespace.hashCode() : 0);
105: result = 29 * result
106: + (tagName != null ? tagName.hashCode() : 0);
107: return result;
108: }
109:
110: public boolean equals(final PrecomputeNodeKey otherKey) {
111: return false;
112: }
113: }
114:
115: public static final int NOT_STARTED = 0;
116: public static final int OPENED = 1;
117: public static final int WAITING_FOR_JOIN = 2;
118: public static final int FINISHING = 3;
119: //public static final int JOINING = 4;
120: public static final int FINISHED = 4;
121:
122: private int processingState;
123: private FlowController flowController;
124: private Element element;
125: private LayoutController parent;
126: private boolean precomputing;
127: private AttributeMap attributeMap;
128: private int expressionsCount;
129: private int iterationCount;
130:
131: protected ElementLayoutController() {
132: this .processingState = ElementLayoutController.NOT_STARTED;
133: }
134:
135: public String toString() {
136: return "ElementLayoutController{" + "processingState="
137: + processingState + ", element=" + element
138: + ", precomputing=" + precomputing
139: + ", expressionsCount=" + expressionsCount
140: + ", iterationCount=" + iterationCount + '}';
141: }
142:
143: /**
144: * Retrieves the parent of this layout controller. This allows childs to query
145: * their context.
146: *
147: * @return the layout controller's parent to <code>null</code> if there is no
148: * parent.
149: */
150: public LayoutController getParent() {
151: return parent;
152: }
153:
154: /**
155: * Initializes the layout controller. This method is called exactly once. It
156: * is the creators responsibility to call this method.
157: * <p/>
158: * Calling initialize after the first advance must result in a
159: * IllegalStateException.
160: *
161: * @param node the currently processed object or layout node.
162: * @param flowController the current flow controller.
163: * @param parent the parent layout controller that was responsible for
164: * instantiating this controller.
165: * @throws DataSourceException if there was a problem reading data from
166: * the datasource.
167: * @throws ReportProcessingException if there was a general problem during
168: * the report processing.
169: * @throws ReportDataFactoryException if a query failed.
170: */
171: public void initialize(final Object node,
172: final FlowController flowController,
173: final LayoutController parent) throws DataSourceException,
174: ReportDataFactoryException, ReportProcessingException {
175:
176: if (processingState != ElementLayoutController.NOT_STARTED) {
177: throw new IllegalStateException();
178: }
179:
180: this .element = (Element) node;
181: this .flowController = flowController;
182: this .parent = parent;
183: this .iterationCount = -1;
184: }
185:
186: /**
187: * Advances the layout controller to the next state. This method delegates the
188: * call to one of the following methods: <ul> <li>{@link
189: * #startElement(org.jfree.report.flow.ReportTarget)} <li>{@link
190: * #processContent(org.jfree.report.flow.ReportTarget)} <li>{@link
191: * #finishElement(org.jfree.report.flow.ReportTarget)} </ul>
192: *
193: * @param target the report target that receives generated events.
194: * @return the new layout controller instance representing the new state.
195: *
196: * @throws DataSourceException if there was a problem reading data from
197: * the datasource.
198: * @throws ReportProcessingException if there was a general problem during
199: * the report processing.
200: * @throws ReportDataFactoryException if a query failed.
201: */
202: public final LayoutController advance(final ReportTarget target)
203: throws DataSourceException, ReportProcessingException,
204: ReportDataFactoryException {
205: final int processingState = getProcessingState();
206: switch (processingState) {
207: case ElementLayoutController.NOT_STARTED:
208: return startElement(target);
209: case ElementLayoutController.OPENED:
210: return processContent(target);
211: case ElementLayoutController.FINISHING:
212: return finishElement(target);
213: // case ElementLayoutController.JOINING:
214: // return joinWithParent();
215: default:
216: throw new IllegalStateException();
217: }
218: }
219:
220: /**
221: * This method is called for each newly instantiated layout controller. The
222: * returned layout controller instance should have a processing state of
223: * either 'OPEN' or 'FINISHING' depending on whether there is any content or
224: * any child nodes to process.
225: *
226: * @param target the report target that receives generated events.
227: * @return the new layout controller instance representing the new state.
228: *
229: * @throws DataSourceException if there was a problem reading data from
230: * the datasource.
231: * @throws ReportProcessingException if there was a general problem during
232: * the report processing.
233: * @throws ReportDataFactoryException if a query failed.
234: */
235: protected LayoutController startElement(final ReportTarget target)
236: throws DataSourceException, ReportProcessingException,
237: ReportDataFactoryException {
238: final Element s = getElement();
239:
240: FlowController fc = getFlowController();
241: // Step 3: Add the expressions. Any expressions defined for the subreport
242: // will work on the queried dataset.
243: fc = startData(target, fc);
244:
245: final Expression[] expressions = s.getExpressions();
246: fc = performElementPrecomputation(expressions, fc);
247:
248: if (s.isVirtual() == false) {
249: attributeMap = computeAttributes(fc, s, target);
250: target.startElement(attributeMap);
251: }
252:
253: final ElementLayoutController derived = (ElementLayoutController) clone();
254: derived.setProcessingState(ElementLayoutController.OPENED);
255: derived.setFlowController(fc);
256: derived.expressionsCount = expressions.length;
257: derived.attributeMap = attributeMap;
258: derived.iterationCount += 1;
259: return derived;
260: }
261:
262: public AttributeMap getAttributeMap() {
263: return attributeMap;
264: }
265:
266: public int getExpressionsCount() {
267: return expressionsCount;
268: }
269:
270: public int getIterationCount() {
271: return iterationCount;
272: }
273:
274: protected FlowController startData(final ReportTarget target,
275: final FlowController fc) throws DataSourceException,
276: ReportProcessingException, ReportDataFactoryException {
277: return fc;
278: }
279:
280: protected AttributeMap computeAttributes(final FlowController fc,
281: final Element element, final ReportTarget target)
282: throws DataSourceException {
283: final LayoutExpressionRuntime ler = LayoutControllerUtil
284: .getExpressionRuntime(fc, element);
285: return LayoutControllerUtil.processAttributes(element, target,
286: ler);
287: }
288:
289: /**
290: * Processes any content in this element. This method is called when the
291: * processing state is 'OPENED'. The returned layout controller will retain
292: * the 'OPENED' state as long as there is more content available. Once all
293: * content has been processed, the returned layout controller should carry a
294: * 'FINISHED' state.
295: *
296: * @param target the report target that receives generated events.
297: * @return the new layout controller instance representing the new state.
298: *
299: * @throws DataSourceException if there was a problem reading data from
300: * the datasource.
301: * @throws ReportProcessingException if there was a general problem during
302: * the report processing.
303: * @throws ReportDataFactoryException if a query failed.
304: */
305: protected abstract LayoutController processContent(
306: final ReportTarget target) throws DataSourceException,
307: ReportProcessingException, ReportDataFactoryException;
308:
309: /**
310: * Finishes the processing of this element. This method is called when the
311: * processing state is 'FINISHING'. The element should be closed now and all
312: * privatly owned resources should be freed. If the element has a parent, it
313: * would be time to join up with the parent now, else the processing state
314: * should be set to 'FINISHED'.
315: *
316: * @param target the report target that receives generated events.
317: * @return the new layout controller instance representing the new state.
318: *
319: * @throws DataSourceException if there was a problem reading data from
320: * the datasource.
321: * @throws ReportProcessingException if there was a general problem during the
322: * report processing.
323: * @throws ReportDataFactoryException if there was an error trying query data.
324: */
325: protected LayoutController finishElement(final ReportTarget target)
326: throws ReportProcessingException, DataSourceException,
327: ReportDataFactoryException {
328: final FlowController fc = handleDefaultEndElement(target);
329: final ElementLayoutController derived = (ElementLayoutController) clone();
330: derived.setProcessingState(ElementLayoutController.FINISHED);
331: derived.setFlowController(fc);
332: return derived;
333: }
334:
335: protected FlowController handleDefaultEndElement(
336: final ReportTarget target)
337: throws ReportProcessingException, DataSourceException,
338: ReportDataFactoryException {
339: final Element e = getElement();
340: // Step 1: call End Element
341: if (e.isVirtual() == false) {
342: target.endElement(getAttributeMap());
343: }
344:
345: FlowController fc = getFlowController();
346: final PrecomputedValueRegistry pcvr = fc
347: .getPrecomputedValueRegistry();
348: // Step 2: Remove the expressions of this element
349: final int expressionsCount = getExpressionsCount();
350: if (expressionsCount != 0) {
351: final ExpressionSlot[] activeExpressions = fc
352: .getActiveExpressions();
353: for (int i = activeExpressions.length - expressionsCount; i < activeExpressions.length; i++) {
354: final ExpressionSlot slot = activeExpressions[i];
355: pcvr.addFunction(slot.getName(), slot.getValue());
356: }
357: fc = fc.deactivateExpressions();
358: }
359:
360: if (isPrecomputing() == false) {
361: pcvr.finishElement(new ElementPrecomputeKey(e));
362: }
363:
364: return fc;
365: }
366:
367: //
368: // /**
369: // * Joins the layout controller with the parent. This simply calls
370: // * {@link #join(org.jfree.report.flow.FlowController)} on the parent. A join
371: // * operation is necessary to propagate changes in the flow-controller to the
372: // * parent for further processing.
373: // *
374: // * @return the joined parent.
375: // * @throws IllegalStateException if this layout controller has no parent.
376: // */
377: // protected LayoutController joinWithParent()
378: // throws ReportProcessingException, ReportDataFactoryException,
379: // DataSourceException
380: // {
381: // final LayoutController parent = getParent();
382: // if (parent == null)
383: // {
384: // // skip to the next step ..
385: // throw new IllegalStateException("There is no parent to join with. " +
386: // "This should not happen in a sane environment!");
387: // }
388: //
389: // return parent.join(getFlowController());
390: // }
391:
392: public boolean isAdvanceable() {
393: return processingState != ElementLayoutController.FINISHED;
394: }
395:
396: public Element getElement() {
397: return element;
398: }
399:
400: public FlowController getFlowController() {
401: return flowController;
402: }
403:
404: public int getProcessingState() {
405: return processingState;
406: }
407:
408: public void setProcessingState(final int processingState) {
409: this .processingState = processingState;
410: }
411:
412: public void setFlowController(final FlowController flowController) {
413: this .flowController = flowController;
414: }
415:
416: public void setParent(final LayoutController parent) {
417: this .parent = parent;
418: }
419:
420: public Object clone() {
421: try {
422: return super .clone();
423: } catch (CloneNotSupportedException e) {
424: Log.error("Clone not supported: ", e);
425: throw new IllegalStateException("Clone must be supported.");
426: }
427: }
428:
429: public boolean isPrecomputing() {
430: return precomputing;
431: }
432:
433: protected FlowController performElementPrecomputation(
434: final Expression[] expressions, FlowController fc)
435: throws ReportProcessingException,
436: ReportDataFactoryException, DataSourceException {
437: final Element element = getElement();
438: final PrecomputedValueRegistry pcvr = fc
439: .getPrecomputedValueRegistry();
440: if (isPrecomputing() == false) {
441: pcvr.startElement(new ElementPrecomputeKey(element));
442: }
443:
444: if (expressions.length > 0) {
445: final ExpressionSlot[] slots = new ExpressionSlot[expressions.length];
446: final StaticExpressionRuntimeData runtimeData = LayoutControllerUtil
447: .getStaticExpressionRuntime(fc, element);
448:
449: for (int i = 0; i < expressions.length; i++) {
450: final Expression expression = expressions[i];
451: if (isPrecomputing() == false
452: && expression.isPrecompute()) {
453: // ok, we have to precompute the expression's value. For that
454: // we fork a new layout process, compute the value and then come
455: // back with the result.
456: final Object value = LayoutControllerUtil
457: .performPrecompute(i,
458: new ElementPrecomputeKey(element),
459: this , getFlowController());
460: slots[i] = new PrecomputedExpressionSlot(expression
461: .getName(), value, expression.isPreserve());
462: } else {
463: // thats a bit easier; we dont have to do anything special ..
464: slots[i] = new RunningExpressionSlot(expression,
465: runtimeData, pcvr.currentNode());
466: }
467: }
468:
469: fc = fc.activateExpressions(slots);
470: }
471: return fc;
472: }
473:
474: protected FlowController tryRepeatingCommit(final FlowController fc)
475: throws DataSourceException {
476: if (isPrecomputing() == false) {
477: // ok, the user wanted us to repeat. So we repeat if the group in which
478: // we are in, is not closed (and at least one advance has been fired
479: // since the last repeat request [to prevent infinite loops]) ...
480: final boolean advanceRequested = fc.isAdvanceRequested();
481: final boolean advanceable = fc.getMasterRow()
482: .isAdvanceable();
483: if (advanceable && advanceRequested) {
484: // we check against the commited target; But we will not use the
485: // commited target if the group is no longer active...
486: final FlowController cfc = fc
487: .performOperation(FlowControlOperation.COMMIT);
488: final boolean groupFinished = LayoutControllerUtil
489: .isGroupFinished(cfc, getElement());
490: if (groupFinished == false) {
491: return cfc;
492: }
493: }
494: }
495: return null;
496: }
497:
498: /**
499: * Derives a copy of this controller that is suitable to perform a
500: * precomputation.
501: *
502: * @param fc
503: * @return
504: */
505: public LayoutController createPrecomputeInstance(
506: final FlowController fc) {
507: final ElementLayoutController lc = (ElementLayoutController) clone();
508: lc.setFlowController(fc);
509: lc.setParent(null);
510: lc.precomputing = true;
511: return lc;
512: }
513:
514: public Object getNode() {
515: return getElement();
516: }
517: }
|