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: * CleanFlowBoxesStep.java
027: * ------------
028: * (C) Copyright 2001-2007, by Object Refinery Ltd, Pentaho Corporation and Contributors.
029: */package org.jfree.report.layout.process;
030:
031: import java.util.HashMap;
032:
033: import org.jfree.report.layout.model.BlockRenderBox;
034: import org.jfree.report.layout.model.CanvasRenderBox;
035: import org.jfree.report.layout.model.FinishedRenderNode;
036: import org.jfree.report.layout.model.LogicalPageBox;
037: import org.jfree.report.layout.model.ParagraphRenderBox;
038: import org.jfree.report.layout.model.RenderBox;
039: import org.jfree.report.layout.model.RenderNode;
040: import org.jfree.report.util.InstanceID;
041: import org.jfree.util.FastStack;
042:
043: /**
044: * Removed finished block-boxes. The boxes have to be marked as 'finished' by the flow output target or nothing will be
045: * removed at all. The boxes marked as finished will be replaced by 'FinishedRenderNodes'. This step preserves nodes
046: * that have pagebreaks.
047: *
048: * @author Thomas Morgner
049: */
050: public final class CleanFlowBoxesStep extends
051: IterateStructuralProcessStep {
052: private HashMap finishContexts;
053: private InstanceID canvasProcessingId;
054: private FastStack blockContexts;
055: private LogicalPageBox pageBox;
056:
057: public CleanFlowBoxesStep() {
058: finishContexts = new HashMap();
059: blockContexts = new FastStack();
060: }
061:
062: public void compute(final LogicalPageBox pageBox) {
063: this .pageBox = pageBox;
064: //Log.debug ("START CLEAR");
065: finishContexts.clear();
066: blockContexts.clear();
067: if (startBlockBox(pageBox)) {
068: // not processing the header and footer area: they are 'out-of-context' bands
069: processBoxChilds(pageBox);
070: }
071: finishBlockBox(pageBox);
072: finishContexts.clear();
073: blockContexts.clear();
074: this .pageBox = null;
075: }
076:
077: protected void processParagraphChilds(final ParagraphRenderBox box) {
078: // we do not process the paragraph lines. This should have been done
079: // in the startblock thing and they get re-added anyway as long as the
080: // paragraph is active.
081: }
082:
083: public boolean startCanvasBox(final CanvasRenderBox box) {
084: // it is guaranteed that the finished flag is only set to true, if the box is closed.
085: if (box.isFinished() == false || box.isCommited() == false) {
086: if (box.getParent() != null) {
087: finishContexts.put(box.getParent().getInstanceId(),
088: Boolean.FALSE);
089: }
090: }
091:
092: if (canvasProcessingId == null) {
093: canvasProcessingId = box.getInstanceId();
094: }
095:
096: finishContexts.put(box.getInstanceId(), Boolean.TRUE);
097: return true;
098: }
099:
100: public void finishCanvasBox(final CanvasRenderBox box) {
101: if (canvasProcessingId == box.getInstanceId()) {
102: canvasProcessingId = null;
103: }
104:
105: if (box.isFinished() == false || box.isCommited() == false) {
106: finishContexts.remove(box.getInstanceId());
107: return;
108: }
109: final Boolean finishedFlag = (Boolean) finishContexts.get(box
110: .getInstanceId());
111: if (Boolean.FALSE.equals(finishedFlag)) {
112: finishContexts.put(box.getParent().getInstanceId(),
113: Boolean.FALSE);
114: } else {
115: // The whole box and all childs are finished. We could now safely remove the box.
116: // (We only remove blocklevel boxes to avoid layouting-troubles, but *we could*.
117: }
118: finishContexts.remove(box.getInstanceId());
119: }
120:
121: // We cannot clear the box until we have verified that all childs of that box have been cleared.
122:
123: protected boolean startBlockBox(final BlockRenderBox box) {
124: if (box.isFinished() == false) {
125: if (box.getParent() != null) {
126: finishContexts.put(box.getParent().getInstanceId(),
127: Boolean.FALSE);
128: }
129: finishContexts.put(box.getInstanceId(), Boolean.FALSE);
130: } else {
131: finishContexts.put(box.getInstanceId(), Boolean.TRUE);
132: }
133: return true;
134: }
135:
136: protected void finishBlockBox(final BlockRenderBox box) {
137: // if (box.isFinished() == false)
138: // {
139: // return;
140: // }
141:
142: final Boolean finishedFlag = (Boolean) finishContexts.get(box
143: .getInstanceId());
144: if (Boolean.FALSE.equals(finishedFlag)) {
145: if (box.getParent() != null) {
146: finishContexts.put(box.getParent().getInstanceId(),
147: Boolean.FALSE);
148: }
149: // Log.debug ("Not removing box " + box + " as this box is not finished.");
150: box.setDeepFinished(false);
151: } else {
152: box.setDeepFinished(true);
153: }
154:
155: finishContexts.remove(box.getInstanceId());
156: if (canvasProcessingId != null) {
157: return;
158: }
159:
160: final RenderNode first = box.getFirstChild();
161: if (first == null) {
162: return;
163: }
164: if (first.isFinished() == false) {
165: return;
166: }
167: if (first instanceof RenderBox) {
168: final RenderBox nextBox = (RenderBox) first;
169: if (nextBox.isDeepFinished() == false) {
170: return;
171: }
172: }
173:
174: RenderNode last = first;
175: while (true) {
176: final RenderNode next = last.getNext();
177: if (next == null) {
178: break;
179: }
180: if (next instanceof RenderBox) {
181: final RenderBox nextBox = (RenderBox) next;
182: if (next.isFinished() == false
183: && nextBox.isDeepFinished() == false) {
184: break;
185: }
186: }
187: last = next;
188: }
189:
190: if (last == first && first instanceof FinishedRenderNode) {
191: // In this case, we can skip the replace-action below ..
192: return;
193: }
194:
195: // So lets get started. We remove all nodes between (and inclusive)
196: // node and last.
197: final long nodeY = first.getY();
198: final long width = box.getContentAreaX2()
199: - box.getContentAreaX1();
200: final long lastY2 = last.getY() + last.getHeight();
201: final long height = lastY2 - nodeY;
202:
203: // make sure that the finished-box inherits the margins ..
204: final long marginsTop = first.getEffectiveMarginTop();
205: final long marginsBottom = last.getEffectiveMarginBottom();
206: final boolean breakAfter = last.isBreakAfter();
207: final FinishedRenderNode replacement = new FinishedRenderNode(
208: width, height, marginsTop, marginsBottom, breakAfter);
209:
210: // Log.debug (" (" + last.getInstanceId() + ") " +
211: // (box.getLastChild() == last) + " " + (box.getFirstChild() == first));
212: int counter = 0;
213: RenderNode removeNode = first;
214: while (removeNode != last) {
215: final RenderNode next = removeNode.getNext();
216: if (removeNode.isOpen()) {
217: throw new IllegalStateException(
218: "A node is still open. We should not have come that far.");
219: }
220: box.remove(removeNode);
221: removeNode = next;
222: counter += 1;
223: }
224:
225: if (last.isOpen()) {
226: throw new IllegalStateException(
227: "The last node is still open. We should not have come that far.");
228: }
229: counter += 1;
230: box.replaceChild(last, replacement);
231: if (replacement.getParent() != box) {
232: throw new IllegalStateException(
233: "The replacement did not work.");
234: }
235: // Log.debug ("Removed " + counter + " nodes from " + box.getName() + " " + last.getName());
236: }
237: }
|