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: * LeftAlignmentProcessor.java
027: * ------------
028: * (C) Copyright 2001-2007, by Object Refinery Ltd, Pentaho Corporation and Contributors.
029: */package org.jfree.report.layout.process.alignment;
030:
031: import org.jfree.report.layout.model.RenderBox;
032: import org.jfree.report.layout.model.RenderNode;
033: import org.jfree.report.layout.process.layoutrules.EndSequenceElement;
034: import org.jfree.report.layout.process.layoutrules.InlineBoxSequenceElement;
035: import org.jfree.report.layout.process.layoutrules.InlineSequenceElement;
036: import org.jfree.report.layout.process.layoutrules.StartSequenceElement;
037: import org.jfree.report.layout.process.layoutrules.TextSequenceElement;
038:
039: /**
040: * Performs the left-alignment computations.
041: * <p/>
042: * The inf-min-step creates the initial sequence of elements. The alignment processor now iterates over the sequence and
043: * produces the layouted line.
044: * <p/>
045: * Elements can be split, splitting is a local operation and does not copy the children. Text splitting may produce a
046: * totally different text (see: TeX hyphenation system).
047: * <p/>
048: * The process is iterative and continues unless all elements have been consumed.
049: *
050: * @author Thomas Morgner
051: */
052: public class LeftAlignmentProcessor extends AbstractAlignmentProcessor {
053: private long position;
054: private int pageSegment;
055:
056: public LeftAlignmentProcessor() {
057: }
058:
059: public int getPageSegment() {
060: return pageSegment;
061: }
062:
063: public void setPageSegment(final int pageSegment) {
064: this .pageSegment = pageSegment;
065: }
066:
067: private long getPosition() {
068: return position;
069: }
070:
071: private void setPosition(final long position) {
072: this .position = position;
073: }
074:
075: private void addPosition(final long width) {
076: this .position += width;
077: }
078:
079: public RenderNode next() {
080: position = getStartOfLine();
081: pageSegment = 0;
082:
083: final RenderNode retval = super .next();
084:
085: position = 0;
086: pageSegment = 0;
087:
088: return retval;
089: }
090:
091: public void performLastLineAlignment() {
092: position = getStartOfLine();
093: pageSegment = 0;
094:
095: super .performLastLineAlignment();
096:
097: position = 0;
098: pageSegment = 0;
099: }
100:
101: /**
102: * Handle the next input chunk.
103: *
104: * @param start the start index
105: * @param count the number of elements in the sequence
106: * @return the index of the last element that will fit on the current line.
107: */
108: protected int handleElement(final int start, final int count) {
109: final InlineSequenceElement[] sequenceElements = getSequenceElements();
110: final RenderNode[] nodes = getNodes();
111: final long[] elementDimensions = getElementDimensions();
112: final long[] elementPositions = getElementPositions();
113:
114: long width = 0;
115: final int endIndex = start + count;
116:
117: // In the given range, there should be only one content element.
118: InlineSequenceElement contentElement = null;
119: int contentIndex = start;
120: for (int i = start; i < endIndex; i++) {
121: final InlineSequenceElement element = sequenceElements[i];
122: final RenderNode node = nodes[i];
123: if (element instanceof StartSequenceElement
124: || element instanceof EndSequenceElement) {
125: width += element.getMaximumWidth(node);
126: continue;
127: }
128:
129: width += element.getMaximumWidth(node);
130: contentElement = element;
131: contentIndex = i;
132: }
133:
134: final long nextPosition = getPosition() + width;
135: final long lastPageBreak = getPageBreak(getPagebreakCount() - 1);
136: // Do we cross a page boundary?
137: if (nextPosition > lastPageBreak) {
138: // On outer break: Stop processing
139:
140: // Dont write through to the stored position; but prepare if
141: // we have to fallback ..
142: long position = getPosition();
143: for (int i = start; i < endIndex; i++) {
144: final InlineSequenceElement element = sequenceElements[i];
145: final RenderNode node = nodes[i];
146: elementPositions[i] = position;
147: final long elementWidth = element.getMaximumWidth(node);
148: elementDimensions[i] = elementWidth;
149: position += elementWidth;
150: }
151:
152: // we cross a pagebreak. Stop working on it - we bail out here.
153:
154: if (contentElement instanceof TextSequenceElement) {
155: // the element may be splittable. Test, and if so, give a hint to the
156: // outside world ..
157: setSkipIndex(endIndex);
158: setBreakableIndex(contentIndex);
159: return (start);
160: }
161:
162: // This is the first element and it still does not fit. How evil.
163: if (start == 0) {
164: if (contentElement instanceof InlineBoxSequenceElement) {
165: final RenderNode node = nodes[contentIndex];
166: if (node instanceof RenderBox) {
167: // OK, limit the size of the box to the maximum line width and
168: // revalidate it.
169: final long contentPosition = elementPositions[contentIndex];
170: final RenderBox box = (RenderBox) node;
171: final long maxWidth = (getEndOfLine() - contentPosition);
172: computeInlineBlock(box, contentPosition,
173: maxWidth);
174:
175: elementDimensions[endIndex - 1] = node
176: .getCachedWidth();
177: }
178: }
179: setSkipIndex(endIndex);
180: }
181: return (start);
182: }
183:
184: final long innerPagebreak = getPageBreak(getPageSegment());
185: if (nextPosition > innerPagebreak) {
186: // It is an inner pagebreak and the current element would not fit into the remaining space.
187: // Move the element to the next page segment (but only if the start is not on
188: setPosition(innerPagebreak);
189: setPageSegment(getPageSegment() + 1);
190: }
191:
192: // No, it is an ordinary advance ..
193: // Check, whether we hit an item-sequence element
194: if (contentElement instanceof InlineBoxSequenceElement == false) {
195: for (int i = start; i < endIndex; i++) {
196: final RenderNode node = nodes[i];
197: final InlineSequenceElement element = sequenceElements[i];
198: elementPositions[i] = getPosition();
199: final long elementWidth = element.getMaximumWidth(node);
200: elementDimensions[i] = elementWidth;
201: addPosition(elementWidth);
202: }
203: return endIndex;
204: }
205:
206: // Handle the ItemSequence element.
207:
208: // This is a bit more complicated. So we encountered an inline-block
209: // element here. That means, the element will try to occuppy its
210: // maximum-content-width.
211: // Log.debug("Advance block at index " + contentIndex);
212: // final long ceWidth = contentElement.getMinimumWidth();
213: // final long extraSpace = contentElement.getMaximumWidth();
214: // Log.debug("Advance block: Min " + ceWidth);
215: // Log.debug("Advance block: Max " + extraSpace);
216:
217: final RenderNode contentNode = nodes[contentIndex];
218: final long itemElementWidth = contentElement
219: .getMaximumWidth(contentNode);
220:
221: if (contentNode instanceof RenderBox) {
222: final RenderBox box = (RenderBox) contentNode;
223: computeInlineBlock(box, getPosition(), itemElementWidth);
224: } else {
225: contentNode.setCachedX(getPosition());
226: contentNode.setCachedWidth(itemElementWidth);
227: }
228:
229: final long preferredEndingPos = getPosition()
230: + itemElementWidth;
231: if (preferredEndingPos > getEndOfLine()) {
232: // We would eat the whole space up to the end of the line and more
233: // So lets move that element to the next line instead...
234:
235: // But: We could easily end in an endless loop here. So check whether
236: // the element is the first in the line
237: if (start == 0) {
238: // As it is guaranteed, that each chunk contains at least one item,
239: // checking for start == 0 is safe enough ..
240: return endIndex;
241: }
242:
243: return start;
244: }
245:
246: for (int i = start; i < contentIndex; i++) {
247: final InlineSequenceElement element = sequenceElements[i];
248: final RenderNode node = nodes[contentIndex];
249: final long elementWidth = element.getMaximumWidth(node);
250: elementPositions[i] = getPosition();
251: elementDimensions[i] = elementWidth;
252: addPosition(elementWidth);
253: }
254:
255: elementPositions[contentIndex] = getPosition();
256: elementDimensions[contentIndex] = itemElementWidth;
257: setPosition(preferredEndingPos);
258:
259: for (int i = contentIndex + 1; i < endIndex; i++) {
260: final InlineSequenceElement element = sequenceElements[i];
261: final RenderNode node = nodes[contentIndex];
262: final long elementWidth = element.getMaximumWidth(node);
263: elementPositions[i] = getPosition();
264: elementDimensions[i] = elementWidth;
265: addPosition(elementWidth);
266: }
267:
268: return endIndex;
269: }
270:
271: public void performSkipAlignment(final int endIndex) {
272: // this is a NO-OP method, as the skip-alignment is simply a left-alignment ...
273: }
274: }
|