/**
* ===========================================
* LibLayout : a free Java layouting library
* ===========================================
*
* Project Info: http://reporting.pentaho.org/liblayout/
*
* (C) Copyright 2006-2007, by Pentaho Corporation and Contributors.
*
* This library is free software; you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Foundation;
* either version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along with this
* library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
* Boston, MA 02111-1307, USA.
*
* [Java is a trademark or registered trademark of Sun Microsystems, Inc.
* in the United States and other countries.]
*
* ------------
* $Id: RenderNode.java 6653 2008-12-02 14:53:40Z tmorgner $
* ------------
* (C) Copyright 2006-2007, by Pentaho Corporation.
*/
package org.jfree.layouting.renderer.model;
import org.jfree.layouting.input.style.keys.line.LineStyleKeys;
import org.jfree.layouting.input.style.values.CSSNumericValue;
import org.jfree.layouting.input.style.values.CSSValue;
import org.jfree.layouting.layouter.context.LayoutContext;
import org.jfree.layouting.layouter.context.PageContext;
import org.jfree.layouting.output.OutputProcessorMetaData;
import org.jfree.layouting.renderer.border.RenderLength;
import org.jfree.layouting.renderer.model.page.LogicalPageBox;
/**
* A node of the rendering model. The renderer model keeps track of the
* rendering state - what has already been rendered, what is pending, etc.
* Things that have been rendered completely get removed from the model.
* <p/>
* The default model makes some assumptions about the node processing:
* <p/>
* <ul> <li>Once placed somewhere, nodes will never move anywhere else</li>
* <li>Once size computation is complete, nodes will not be modified
* anymore.</li> </ul>
* <p/>
* Finished nodes can be removed as soon as they have been physically rendered.
* <p/>
* In case a node cannot be rendered right now (think of tables, which need all
* cells before a row or in some cases even the columns can be computed), a cell
* receives the 'pending' flag. If a cell is added to a parent which is pending,
* the cell is pending as well. The pending state must be resolved by the
* outer-most element. An element cannot be finished or removed unless it
* resolved the pending state.
*
* @author Thomas Morgner
*/
public abstract class RenderNode implements Cloneable
{
public static final int HORIZONTAL_AXIS = 0;
public static final int VERTICAL_AXIS = 1;
private RenderBox parent;
private RenderNode prev;
private RenderNode next;
private long changeTracker;
// The element's positions.
private long width;
private long height;
private long x;
private long y;
private boolean frozen;
private boolean hibernated;
private Object instanceId;
private NodeLayoutProperties layoutProperties;
private LayoutContext layoutContext;
private boolean dirty;
private long maximumBoxWidth;
private long minimumChunkWidth;
private boolean icmMetricsFinished;
private long stickyMarker;
private ComputedLayoutProperties computedLayoutProperties;
public RenderNode()
{
this.instanceId = new Object();
this.layoutProperties = new NodeLayoutProperties();
}
public void appyStyle(final LayoutContext context, final OutputProcessorMetaData metaData)
{
this.layoutProperties.setAlignmentBaseline
(context.getValue(LineStyleKeys.ALIGNMENT_BASELINE));
final CSSValue alignmentAdjust = context.getValue(LineStyleKeys.ALIGNMENT_ADJUST);
this.layoutProperties.setAlignmentAdjust(alignmentAdjust);
if (alignmentAdjust instanceof CSSNumericValue)
{
this.layoutProperties.setAlignmentAdjustResolved
(RenderLength.convertToInternal (alignmentAdjust, context, metaData));
}
final CSSValue baselineShift = context.getValue(LineStyleKeys.BASELINE_SHIFT);
this.layoutProperties.setBaselineShift(baselineShift);
if (baselineShift instanceof CSSNumericValue)
{
this.layoutProperties.setBaselineShiftResolved(
RenderLength.convertToInternal(baselineShift, context, metaData));
}
this.layoutProperties.setVerticalAlignment
(normalizeAlignment(context.getValue(LineStyleKeys.VERTICAL_ALIGN)));
this.layoutProperties.setNamespace(context.getNamespace());
this.layoutProperties.setTagName (context.getTagName());
this.layoutContext = context;
}
public ComputedLayoutProperties getComputedLayoutProperties()
{
return computedLayoutProperties;
}
public void setComputedLayoutProperties(final ComputedLayoutProperties clp)
{
this.computedLayoutProperties = clp;
}
public LayoutContext getLayoutContext()
{
return layoutContext;
}
public boolean isDirty()
{
return dirty;
}
public void setDirty(final boolean dirty)
{
this.dirty = dirty;
}
public String getNamespace()
{
return layoutProperties.getNamespace();
}
public String getTagName()
{
return layoutProperties.getTagName();
}
protected CSSValue normalizeAlignment(final CSSValue verticalAlignment)
{
return verticalAlignment;
}
public CSSValue getVerticalAlignment()
{
return layoutProperties.getVerticalAlignment();
}
public RenderLength getBaselineShiftResolved()
{
return layoutProperties.getBaselineShiftResolved();
}
public CSSValue getAlignmentBaseline()
{
return layoutProperties.getAlignmentBaseline();
}
public CSSValue getBaselineShift()
{
return layoutProperties.getBaselineShift();
}
public CSSValue getAlignmentAdjust()
{
return layoutProperties.getAlignmentAdjust();
}
public RenderLength getAlignmentAdjustResolved()
{
return layoutProperties.getAlignmentAdjustResolved();
}
public PageContext getPageContext()
{
final RenderBox parent = getParent();
if (parent != null)
{
parent.getPageContext();
}
return null;
}
public Object getInstanceId()
{
return instanceId;
}
public int getMajorAxis()
{
return layoutProperties.getMajorAxis();
}
protected void setMajorAxis(final int majorAxis)
{
layoutProperties.setMajorAxis(majorAxis);
}
public int getMinorAxis()
{
return layoutProperties.getMinorAxis();
}
protected void setMinorAxis(final int minorAxis)
{
layoutProperties.setMinorAxis(minorAxis);
this.updateChangeTracker();
}
public void setWidth(final long width)
{
if (width < 0)
{
throw new IllegalArgumentException("Width cannot be negative: " + width);
}
this.width = width;
this.updateChangeTracker();
}
public long getWidth()
{
return width;
}
public void setHeight(final long height)
{
if (height < 0)
{
throw new IllegalArgumentException("Height cannot be negative: " + height);
}
this.height = height;
// this.updateChangeTracker();
}
public long getHeight()
{
return this.height;
}
public long getX()
{
return x;
}
public void setX(final long x)
{
this.x = x;
this.updateChangeTracker();
}
public final void setPosition(final int axis, final long value)
{
if (axis == HORIZONTAL_AXIS)
{
setX(value);
}
else
{
setY(value);
}
}
public final long getPosition(final int axis)
{
if (axis == HORIZONTAL_AXIS)
{
return getX();
}
else
{
return getY();
}
}
public final void setDimension(final int axis, final long value)
{
if (axis == HORIZONTAL_AXIS)
{
setWidth(value);
}
else
{
setHeight(value);
}
}
public final long getDimension(final int axis)
{
if (axis == HORIZONTAL_AXIS)
{
return getWidth();
}
else
{
return getHeight();
}
}
public long getY()
{
return y;
}
public void setY(final long y)
{
this.y = y;
}
public RenderBox getParent()
{
return parent;
}
protected void setParent(final RenderBox parent)
{
// Object oldParent = this.parent;
this.parent = parent;
if (parent != null && prev == parent)
{
throw new IllegalStateException();
}
}
public RenderNode getVisiblePrev()
{
RenderNode node = prev;
while (node != null)
{
if (node.isIgnorableForRendering() == false)
{
return node;
}
node = node.getPrev();
}
return null;
}
public RenderNode getPrev()
{
return prev;
}
protected void setPrev(final RenderNode prev)
{
this.prev = prev;
if (prev != null && prev == parent)
{
throw new IllegalStateException();
}
}
public RenderNode getVisibleNext()
{
RenderNode node = next;
while (node != null)
{
if (node.isIgnorableForRendering() == false)
{
return node;
}
node = node.getNext();
}
return null;
}
public RenderNode getNext()
{
return next;
}
protected void setNext(final RenderNode next)
{
this.next = next;
}
public LogicalPageBox getLogicalPage()
{
final RenderNode parent = getParent();
if (parent != null)
{
return parent.getLogicalPage();
}
return null;
}
public NormalFlowRenderBox getNormalFlow()
{
final RenderNode parent = getParent();
if (parent != null)
{
return parent.getNormalFlow();
}
return null;
}
/**
* Clones this node. Be aware that cloning can get you into deep trouble, as
* the relations this node has may no longer be valid.
*
* @return
*/
public Object clone()
{
try
{
final RenderNode renderNode = (RenderNode) super.clone();
return renderNode;
}
catch (CloneNotSupportedException e)
{
throw new IllegalStateException("Clone failed for some reason.");
}
}
/**
* Derive creates a disconnected node that shares all the properties of the
* original node. The derived node will no longer have any parent, silbling,
* child or any other relationships with other nodes.
*
* @return
*/
public RenderNode derive(final boolean deep)
{
final RenderNode node = (RenderNode) clone();
node.parent = null;
node.next = null;
node.prev = null;
node.hibernated = false;
return node;
}
/**
* Derives an hibernation copy. The resulting object should get stripped of
* all unnecessary caching information and all objects, which will be
* regenerated when the layouting restarts. Size does matter here.
*
* @return
*/
public RenderNode hibernate ()
{
final RenderNode node = (RenderNode) clone();
node.parent = null;
node.next = null;
node.prev = null;
node.hibernated = true;
return node;
}
public RenderNode deriveFrozen(final boolean deep)
{
final RenderNode node = (RenderNode) clone();
node.parent = null;
node.next = null;
node.prev = null;
node.frozen = true;
node.hibernated = false;
return node;
}
public boolean isFrozen()
{
return frozen;
}
public boolean isHibernated()
{
return hibernated;
}
protected void setHibernated(final boolean hibernated)
{
this.hibernated = hibernated;
}
public RenderNode findNodeById(final Object instanceId)
{
if (instanceId == getInstanceId())
{
return this;
}
return null;
}
public boolean isOpen()
{
return false;
}
public boolean isEmpty()
{
return false;
}
public RenderBox getParentBlockContext()
{
if (parent == null)
{
return null;
}
if (parent instanceof BlockRenderBox)
{
return parent;
}
return parent.getParentBlockContext();
}
public boolean isDiscardable()
{
return false;
}
/**
* If that method returns true, the element will not be used for rendering.
* For the purpose of computing sizes or performing the layouting (in the
* validate() step), this element will treated as if it is not there.
* <p/>
* If the element reports itself as non-empty, however, it will affect the
* margin computation.
*
* @return
*/
public boolean isIgnorableForRendering()
{
return isEmpty();
}
/**
* Returns the baseline info for the given node. This can be null, if the node
* does not have any baseline info at all. If the element has more than one
* set of baselines, the baseline of the first element is returned.
*
* @return
*/
// public abstract ExtendedBaselineInfo getBaselineInfo();
public NodeLayoutProperties getNodeLayoutProperties()
{
return layoutProperties;
}
public void freeze()
{
frozen = true;
}
public boolean isDirectionLTR()
{
return true;
}
public void updateChangeTracker()
{
changeTracker += 1;
if (parent != null)
{
parent.updateChangeTracker();
}
}
public long getChangeTracker()
{
return changeTracker;
}
public long getStickyMarker()
{
return stickyMarker;
}
public void setStickyMarker(final long stickyMarker)
{
this.stickyMarker = stickyMarker;
}
public long getEffectiveMarginTop()
{
return 0;
}
public long getEffectiveMarginBottom()
{
return 0;
}
public long getMaximumBoxWidth()
{
return maximumBoxWidth;
}
public void setMaximumBoxWidth(final long maximumBoxWidth)
{
this.maximumBoxWidth = maximumBoxWidth;
}
public long getMinimumChunkWidth()
{
return minimumChunkWidth;
}
public void setMinimumChunkWidth(final long minimumChunkWidth)
{
this.minimumChunkWidth = minimumChunkWidth;
}
public boolean isIcmMetricsFinished()
{
return icmMetricsFinished;
}
public void setIcmMetricsFinished(final boolean icmMetricsFinished)
{
this.icmMetricsFinished = icmMetricsFinished;
}
}