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: * RenderNode.java
027: * ------------
028: * (C) Copyright 2001-2007, by Object Refinery Ltd, Pentaho Corporation and Contributors.
029: */package org.jfree.report.layout.model;
030:
031: import org.jfree.report.layout.model.context.NodeLayoutProperties;
032: import org.jfree.report.style.StyleSheet;
033: import org.jfree.report.style.VerticalTextAlign;
034: import org.jfree.report.util.InstanceID;
035: import org.jfree.report.util.geom.StrictBounds;
036:
037: /**
038: * Creation-Date: 03.04.2007, 13:16:06
039: *
040: * @author Thomas Morgner
041: */
042: public abstract class RenderNode implements Cloneable {
043: public static final int HORIZONTAL_AXIS = 0;
044: public static final int VERTICAL_AXIS = 1;
045:
046: public static final int CACHE_CLEAN = 0;
047: public static final int CACHE_DIRTY = 1;
048: public static final int CACHE_DEEP_DIRTY = 2;
049:
050: private long changeTracker;
051: private RenderBox parent;
052: private RenderNode next;
053: private RenderNode prev;
054: private boolean frozen;
055: private boolean hibernated;
056:
057: private long minimumChunkWidth;
058: private long maximumBoxWidth;
059: private NodeLayoutProperties nodeLayoutProperties;
060:
061: private long computedX;
062: private long computedWidth;
063:
064: private long cachedAge;
065: private long cachedParentWidth;
066:
067: private long cachedX;
068: private long cachedY;
069: private long cachedWidth;
070: private long cachedHeight;
071:
072: private long x;
073: private long y;
074: private long width;
075: private long height;
076:
077: private int cacheState;
078: private boolean finished;
079: private boolean virtualNode;
080:
081: protected RenderNode(final StyleSheet styleSheet) {
082: this .nodeLayoutProperties = new NodeLayoutProperties(styleSheet);
083: this .cacheState = CACHE_DEEP_DIRTY;
084: }
085:
086: public boolean isSizeSpecifiesBorderBox() {
087: return true;
088: }
089:
090: public long getComputedWidth() {
091: return computedWidth;
092: }
093:
094: /**
095: * Defines the computed width. The computed-width is a static-property and is always specified as border-box size.
096: *
097: * @param computedWidth
098: */
099: public void setComputedWidth(final long computedWidth) {
100: if (computedWidth < 0) {
101: throw new IllegalArgumentException();
102: }
103: // if (computedWidth == 0)
104: // {
105: // Log.debug ("HJERE");
106: // }
107: this .computedWidth = computedWidth;
108: }
109:
110: protected void setMinorAxis(final int axis) {
111: this .nodeLayoutProperties.setMinorAxis(axis);
112: }
113:
114: public int getMinorAxis() {
115: return this .nodeLayoutProperties.getMinorAxis();
116: }
117:
118: protected void setMajorAxis(final int axis) {
119: this .nodeLayoutProperties.setMajorAxis(axis);
120: }
121:
122: public int getMajorAxis() {
123: return this .nodeLayoutProperties.getMajorAxis();
124: }
125:
126: public final NodeLayoutProperties getNodeLayoutProperties() {
127: return nodeLayoutProperties;
128: }
129:
130: public final long getX() {
131: return x;
132: }
133:
134: public final void setX(final long x) {
135: this .x = x;
136: //this.updateChangeTracker();
137: }
138:
139: public final long getY() {
140: return y;
141: }
142:
143: public final void shift(final long amount) {
144: this .y += amount;
145: this .updateCacheState(CACHE_DEEP_DIRTY);
146: }
147:
148: public final void setY(final long y) {
149: this .y = y;
150: this .updateCacheState(CACHE_DEEP_DIRTY);
151: }
152:
153: protected final void updateCacheState(final int state) {
154: switch (state) {
155: // case CACHE_CLEAN:
156: // this.cacheState = CACHE_CLEAN;
157: // break;
158: case CACHE_DIRTY:
159: if (cacheState == CACHE_CLEAN) {
160: this .cacheState = CACHE_DIRTY;
161: if (parent != null) {
162: parent.updateCacheState(CACHE_DIRTY);
163: }
164: }
165: // if cache-state either dirty or deep-dirty, no need to update.
166: break;
167: case CACHE_DEEP_DIRTY:
168: if (cacheState == CACHE_CLEAN) {
169: this .cacheState = CACHE_DEEP_DIRTY;
170: if (parent != null) {
171: parent.updateCacheState(CACHE_DIRTY);
172: }
173: }
174: break;
175: default:
176: throw new IllegalArgumentException();
177: }
178: }
179:
180: public final long getWidth() {
181: return width;
182: }
183:
184: public final void setWidth(final long width) {
185: this .width = width;
186: //this.updateChangeTracker();
187: }
188:
189: public final long getHeight() {
190: return height;
191: }
192:
193: public final void setHeight(final long height) {
194: this .height = height;
195: this .updateCacheState(CACHE_DIRTY);
196: //this.updateChangeTracker();
197: }
198:
199: public final StyleSheet getStyleSheet() {
200: return nodeLayoutProperties.getStyleSheet();
201: }
202:
203: public InstanceID getInstanceId() {
204: return nodeLayoutProperties.getInstanceId();
205: }
206:
207: protected void updateChangeTracker() {
208: changeTracker += 1;
209: cacheState = CACHE_DIRTY;
210: if (parent != null) {
211: parent.updateChangeTracker();
212: }
213: }
214:
215: public final long getChangeTracker() {
216: return changeTracker;
217: }
218:
219: public final RenderBox getParent() {
220: return parent;
221: }
222:
223: protected final void setParent(final RenderBox parent) {
224: if (parent != null && prev == parent) {
225: throw new IllegalStateException("Assertation failed.");
226: }
227: // Object oldParent = this.parent;
228: this .parent = parent;
229: }
230:
231: public RenderNode getVisiblePrev() {
232: RenderNode node = prev;
233: while (node != null) {
234: if (node.isIgnorableForRendering() == false) {
235: return node;
236: }
237: node = node.getPrev();
238: }
239: return null;
240: }
241:
242: public final RenderNode getPrev() {
243: return prev;
244: }
245:
246: protected final void setPrev(final RenderNode prev) {
247: this .prev = prev;
248: if (prev != null && prev == parent) {
249: throw new IllegalStateException();
250: }
251: }
252:
253: public RenderNode getVisibleNext() {
254: RenderNode node = next;
255: while (node != null) {
256: if (node.isIgnorableForRendering() == false) {
257: return node;
258: }
259: node = node.getNext();
260: }
261: return null;
262: }
263:
264: public final RenderNode getNext() {
265: return next;
266: }
267:
268: protected final void setNext(final RenderNode next) {
269: this .next = next;
270: }
271:
272: public LogicalPageBox getLogicalPage() {
273: RenderNode parent = this ;
274: while (parent != null) {
275: if (parent instanceof LogicalPageBox) {
276: return (LogicalPageBox) parent;
277: }
278:
279: parent = parent.getParent();
280: }
281: return null;
282: }
283:
284: /**
285: * Clones this node. Be aware that cloning can get you into deep trouble, as the relations this node has may no longer
286: * be valid.
287: *
288: * @return
289: */
290: public Object clone() {
291: try {
292: return super .clone();
293: } catch (CloneNotSupportedException e) {
294: throw new IllegalStateException(
295: "Clone failed for some reason.");
296: }
297: }
298:
299: /**
300: * Derive creates a disconnected node that shares all the properties of the original node. The derived node will no
301: * longer have any parent, silbling, child or any other relationships with other nodes.
302: *
303: * @return
304: */
305: public RenderNode derive(final boolean deep) {
306: final RenderNode node = (RenderNode) clone();
307: node.parent = null;
308: node.next = null;
309: node.prev = null;
310: node.hibernated = false;
311: return node;
312: }
313:
314: /**
315: * Derives an hibernation copy. The resulting object should get stripped of all unnecessary caching information and
316: * all objects, which will be regenerated when the layouting restarts. Size does matter here.
317: *
318: * @return
319: */
320: public RenderNode hibernate() {
321: final RenderNode node = (RenderNode) clone();
322: node.parent = null;
323: node.next = null;
324: node.prev = null;
325: node.hibernated = true;
326: return node;
327: }
328:
329: public RenderNode deriveFrozen(final boolean deep) {
330: final RenderNode node = (RenderNode) clone();
331: node.parent = null;
332: node.next = null;
333: node.prev = null;
334: node.frozen = true;
335: node.hibernated = false;
336: return node;
337: }
338:
339: public boolean isFrozen() {
340: return frozen;
341: }
342:
343: public boolean isHibernated() {
344: return hibernated;
345: }
346:
347: protected void setHibernated(final boolean hibernated) {
348: this .hibernated = hibernated;
349: }
350:
351: public RenderNode findNodeById(final Object instanceId) {
352: if (instanceId == getInstanceId()) {
353: return this ;
354: }
355: return null;
356: }
357:
358: public boolean isOpen() {
359: return false;
360: }
361:
362: public boolean isEmpty() {
363: return false;
364: }
365:
366: public boolean isDiscardable() {
367: return false;
368: }
369:
370: /**
371: * If that method returns true, the element will not be used for rendering. For the purpose of computing sizes or
372: * performing the layouting (in the validate() step), this element will treated as if it is not there.
373: * <p/>
374: * If the element reports itself as non-empty, however, it will affect the margin computation.
375: *
376: * @return
377: */
378: public boolean isIgnorableForRendering() {
379: return isEmpty();
380: }
381:
382: public void freeze() {
383: frozen = true;
384: }
385:
386: public long getMaximumBoxWidth() {
387: return maximumBoxWidth;
388: }
389:
390: public void setMaximumBoxWidth(final long maximumBoxWidth) {
391: this .maximumBoxWidth = maximumBoxWidth;
392: }
393:
394: public long getMinimumChunkWidth() {
395: return minimumChunkWidth;
396: }
397:
398: public void setMinimumChunkWidth(final long minimumChunkWidth) {
399: this .minimumChunkWidth = minimumChunkWidth;
400: }
401:
402: public long getComputedX() {
403: return computedX;
404: }
405:
406: public void setComputedX(final long computedX) {
407: this .computedX = computedX;
408: }
409:
410: public long getEffectiveMarginTop() {
411: return 0;
412: }
413:
414: public long getEffectiveMarginBottom() {
415: return 0;
416: }
417:
418: public VerticalTextAlign getVerticalTextAlignment() {
419: return nodeLayoutProperties.getVerticalTextAlign();
420: }
421:
422: //
423: // /**
424: // * The sticky-Marker contains the original Y of this node.
425: // * @return
426: // */
427: // public long getStickyMarker()
428: // {
429: // return stickyMarker;
430: // }
431: //
432: // public void setStickyMarker(final long stickyMarker)
433: // {
434: // this.stickyMarker = stickyMarker;
435: // }
436:
437: public String getName() {
438: return null;
439: }
440:
441: public boolean isBreakAfter() {
442: return false;
443: }
444:
445: public final long getCachedAge() {
446: return cachedAge;
447: }
448:
449: public final void setCachedAge(final long cachedAge) {
450: this .cachedAge = cachedAge;
451: }
452:
453: public final long getCachedParentWidth() {
454: return cachedParentWidth;
455: }
456:
457: public final void setCachedParentWidth(final long cachedParentWidth) {
458: this .cachedParentWidth = cachedParentWidth;
459: }
460:
461: /**
462: * Returns the cached y position. This position is known after all layouting steps have been finished. In most
463: * cases the layouter tries to reuse the cached values instead of recomputing everything from scratch on each
464: * iteration.
465: *
466: * The cached positions always specify the border-box. If the user specified sizes as content-box sizes, the
467: * layouter converts them into border-box sizes before filling the cache.
468: *
469: * @return the cached x position
470: */
471: public final long getCachedX() {
472: return cachedX;
473: }
474:
475: /**
476: * Defines the cached x position. This position is known after all layouting steps have been finished. In most
477: * cases the layouter tries to reuse the cached values instead of recomputing everything from scratch on each
478: * iteration.
479: *
480: * The cached positions always specify the border-box. If the user specified sizes as content-box sizes, the
481: * layouter converts them into border-box sizes before filling the cache.
482: *
483: * @param cachedX the cached x position
484: */
485: public final void setCachedX(final long cachedX) {
486: this .cachedX = cachedX;
487: }
488:
489: /**
490: * Returns the cached y position. This position is known after all layouting steps have been finished. In most
491: * cases the layouter tries to reuse the cached values instead of recomputing everything from scratch on each
492: * iteration.
493: *
494: * The cached positions always specify the border-box. If the user specified sizes as content-box sizes, the
495: * layouter converts them into border-box sizes before filling the cache.
496: *
497: * @return the cached y position
498: */
499: public final long getCachedY() {
500: return cachedY;
501: }
502:
503: /**
504: * Defines the cached y position. This position is known after all layouting steps have been finished. In most
505: * cases the layouter tries to reuse the cached values instead of recomputing everything from scratch on each
506: * iteration.
507: *
508: * The cached positions always specify the border-box. If the user specified sizes as content-box sizes, the
509: * layouter converts them into border-box sizes before filling the cache.
510: *
511: * @param cachedY the cached y position
512: */
513: public final void setCachedY(final long cachedY) {
514: this .cachedY = cachedY;
515: }
516:
517: public final void shiftCached(final long amount) {
518: this .cachedY += amount;
519: }
520:
521: public final long getCachedWidth() {
522: return cachedWidth;
523: }
524:
525: public final void setCachedWidth(final long cachedWidth) {
526: this .cachedWidth = cachedWidth;
527: }
528:
529: public final long getCachedHeight() {
530: return cachedHeight;
531: }
532:
533: public final void setCachedHeight(final long cachedHeight) {
534: this .cachedHeight = cachedHeight;
535: }
536:
537: public void apply() {
538: this .x = this .cachedX;
539: this .y = this .cachedY;
540: this .width = this .cachedWidth;
541: this .height = this .cachedHeight;
542: this .cachedAge = this .changeTracker;
543: this .cacheState = CACHE_CLEAN;
544: if (this .parent == null) {
545: this .cachedParentWidth = 0;
546: } else {
547: this .cachedParentWidth = this .parent.getWidth();
548: }
549: }
550:
551: public final boolean isCacheValid() {
552: if (cachedAge != changeTracker) {
553: return false;
554: }
555:
556: if (this .parent == null) {
557: if (this .cachedParentWidth != 0) {
558: // cachedAge = -1;
559: return false;
560: }
561: } else {
562: if (this .cachedParentWidth != this .parent.getWidth()) {
563: // cachedAge = -1;
564: return false;
565: }
566: }
567: return true;
568: }
569:
570: /**
571: * Checks, whether this node can be removed. This flag is used by iterative streaming output targets to mark nodes
572: * that have been fully processed.
573: *
574: * @return
575: */
576: public boolean isFinished() {
577: return finished;
578: }
579:
580: public void setFinished(final boolean finished) {
581: if (this .finished == true && finished == false) {
582: throw new IllegalStateException();
583: }
584: this .finished = finished;
585: }
586:
587: public int getCacheState() {
588: return cacheState;
589: }
590:
591: public void markCacheClean() {
592: if (cachedY != y) {
593: throw new IllegalStateException();
594: }
595: cacheState = CACHE_CLEAN;
596: }
597:
598: public Object getStateKey() {
599: return null;
600: }
601:
602: public final boolean isNodeVisible(final StrictBounds drawArea) {
603: final long drawAreaX0 = drawArea.getX();
604: final long drawAreaY0 = drawArea.getY();
605: final long drawAreaX1 = drawAreaX0 + drawArea.getWidth();
606: final long drawAreaY1 = drawAreaY0 + drawArea.getHeight();
607:
608: final long x2 = x + width;
609: final long y2 = y + height;
610:
611: if (width == 0) {
612: if (x2 < drawAreaX0) {
613: return false;
614: }
615: if (x > drawAreaX1) {
616: return false;
617: }
618: } else {
619: if (x2 <= drawAreaX0) {
620: return false;
621: }
622: if (x >= drawAreaX1) {
623: return false;
624: }
625: }
626: if (height == 0) {
627: if (y2 < drawAreaY0) {
628: return false;
629: }
630: if (y > drawAreaY1) {
631: return false;
632: }
633: } else {
634: if (y2 <= drawAreaY0) {
635: return false;
636: }
637: if (y >= drawAreaY1) {
638: return false;
639: }
640: }
641: return true;
642: }
643:
644: public final boolean isNodeVisibleInParent() {
645: final RenderBox parent = getParent();
646: if (parent == null) {
647: // Nodes without a parent are always visible.
648: return true;
649: }
650:
651: final long drawAreaX0 = parent.getX();
652: final long drawAreaY0 = parent.getY();
653: final long drawAreaX1 = drawAreaX0 + parent.getWidth();
654: final long drawAreaY1 = drawAreaY0 + parent.getHeight();
655:
656: final long x2 = x + width;
657: final long y2 = y + height;
658:
659: if (width == 0) {
660: if (x2 < drawAreaX0) {
661: return false;
662: }
663: if (x > drawAreaX1) {
664: return false;
665: }
666: } else {
667: if (x2 <= drawAreaX0) {
668: return false;
669: }
670: if (x >= drawAreaX1) {
671: return false;
672: }
673: }
674: if (height == 0) {
675: if (y2 < drawAreaY0) {
676: return false;
677: }
678: if (y > drawAreaY1) {
679: return false;
680: }
681: } else {
682: if (y2 <= drawAreaY0) {
683: return false;
684: }
685: if (y >= drawAreaY1) {
686: return false;
687: }
688: }
689: return true;
690: }
691:
692: public boolean isVirtualNode() {
693: return virtualNode;
694: }
695:
696: public void setVirtualNode(final boolean virtualNode) {
697: this.virtualNode = virtualNode;
698: }
699: }
|