Package org.pentaho.reporting.engine.classic.core.layout.process

Source Code of org.pentaho.reporting.engine.classic.core.layout.process.InfiniteMajorAxisLayoutStep

/*
* This program is free software; you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License, version 2.1 as published by the Free Software
* Foundation.
*
* You should have received a copy of the GNU Lesser General Public License along with this
* program; if not, you can obtain a copy at http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html
* or from the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* This program 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.
*
* Copyright (c) 2001 - 2013 Object Refinery Ltd, Pentaho Corporation and Contributors..  All rights reserved.
*/

package org.pentaho.reporting.engine.classic.core.layout.process;

import org.pentaho.reporting.engine.classic.core.ElementAlignment;
import org.pentaho.reporting.engine.classic.core.layout.model.FinishedRenderNode;
import org.pentaho.reporting.engine.classic.core.layout.model.LayoutNodeTypes;
import org.pentaho.reporting.engine.classic.core.layout.model.LogicalPageBox;
import org.pentaho.reporting.engine.classic.core.layout.model.ParagraphPoolBox;
import org.pentaho.reporting.engine.classic.core.layout.model.ParagraphRenderBox;
import org.pentaho.reporting.engine.classic.core.layout.model.RenderBox;
import org.pentaho.reporting.engine.classic.core.layout.model.RenderLength;
import org.pentaho.reporting.engine.classic.core.layout.model.RenderNode;
import org.pentaho.reporting.engine.classic.core.layout.model.RenderableReplacedContentBox;
import org.pentaho.reporting.engine.classic.core.layout.model.RenderableText;
import org.pentaho.reporting.engine.classic.core.layout.model.WatermarkAreaBox;
import org.pentaho.reporting.engine.classic.core.layout.model.context.BoxDefinition;
import org.pentaho.reporting.engine.classic.core.layout.model.context.StaticBoxLayoutProperties;
import org.pentaho.reporting.engine.classic.core.layout.model.table.TableCellRenderBox;
import org.pentaho.reporting.engine.classic.core.layout.model.table.TableRowRenderBox;
import org.pentaho.reporting.engine.classic.core.layout.model.table.TableSectionRenderBox;
import org.pentaho.reporting.engine.classic.core.layout.output.OutputProcessorFeature;
import org.pentaho.reporting.engine.classic.core.layout.output.OutputProcessorMetaData;
import org.pentaho.reporting.engine.classic.core.layout.process.util.CacheBoxShifter;
import org.pentaho.reporting.engine.classic.core.layout.process.util.MajorAxisParagraphBreakState;
import org.pentaho.reporting.engine.classic.core.layout.process.util.ProcessUtility;
import org.pentaho.reporting.engine.classic.core.layout.process.util.ReplacedContentUtil;
import org.pentaho.reporting.engine.classic.core.layout.process.valign.BoxAlignContext;
import org.pentaho.reporting.engine.classic.core.layout.process.valign.InlineBlockAlignContext;
import org.pentaho.reporting.engine.classic.core.layout.process.valign.NodeAlignContext;
import org.pentaho.reporting.engine.classic.core.layout.process.valign.ReplacedContentAlignContext;
import org.pentaho.reporting.engine.classic.core.layout.process.valign.TextElementAlignContext;
import org.pentaho.reporting.engine.classic.core.layout.process.valign.VerticalAlignmentProcessor;


/**
* Computes the absolute layout. The computed height and y positions of all abolutely positioned elements will be stored
* in the 'canvasY' and 'canvasHeight' properties of RenderNode. Percentages will be resolved to zero.
*
* @author Thomas Morgner
* @noinspection PointlessArithmeticExpression, ConstantConditions
*/
public final class InfiniteMajorAxisLayoutStep extends AbstractMajorAxisLayoutStep
{
  private MajorAxisParagraphBreakState breakState;
  private VerticalAlignmentProcessor processor;
  private boolean complexText;

  public InfiniteMajorAxisLayoutStep()
  {
    super(false);
    this.breakState = new MajorAxisParagraphBreakState();
    this.processor = new VerticalAlignmentProcessor();
  }

  public void initialize(OutputProcessorMetaData metaData)
  {
    complexText = metaData.isFeatureSupported(OutputProcessorFeature.COMPLEX_TEXT);
  }

  public void compute(final LogicalPageBox pageBox)
  {
    this.breakState.deinit();
    try
    {
      super.compute(pageBox);
    }
    finally
    {
      this.breakState.deinit();
    }
  }

  /**
   * Continues processing. The renderbox must have a valid x-layout (that is: X, content-X1, content-X2 and Width)
   *
   * @param box the box.
   */
  public void continueComputation(final RenderBox box)
  {
    // This is most-likely wrong, but as we do not support inline-block elements yet, we can ignore this for now.
    if (box.getCachedWidth() == 0)
    {
      throw new IllegalStateException("Box must be layouted a bit ..");
    }

    this.breakState.deinit();
    try
    {
      super.continueComputation(box);
    }
    finally
    {
      this.breakState.deinit();
    }
  }


  protected boolean startBlockLevelBox(final RenderBox box)
  {
    if (checkCacheValid(box))
    {
      return false;
    }

    performStartTable(box);
    // Compute the block-position of the box. The box is positioned relative to the previous sibling or
    // relative to the parent.
    box.setCachedY(computeVerticalBlockPosition(box));

    if (breakState.isActive())
    {
      if (complexText) {
        return true;
      }

      // No breakstate and not being suspended? Why this?
      if (breakState.isSuspended() == false)
      {
        throw new IllegalStateException("This cannot be.");
      }

      // this way or another - we are suspended now. So there is no need to look
      // at the children anymore ..

      // This code is only executed for inline-block elements. Inline-block elements are not part of
      // the 0.8.9 or 1.0 engine layouting.
      return false;
    }

    final int layoutNodeType = box.getLayoutNodeType();
    if (layoutNodeType == LayoutNodeTypes.TYPE_BOX_PARAGRAPH)
    {
      final ParagraphRenderBox paragraphBox = (ParagraphRenderBox) box;
      // We cant cache that ... the shift operations later would misbehave
      // One way around would be to at least store the layouted offsets
      // (which should be immutable as long as the line did not change its
      // contents) and to reapply them on each run. This is cheaper than
      // having to compute the whole v-align for the whole line.
      breakState.init(paragraphBox);
    }
    else if (layoutNodeType == LayoutNodeTypes.TYPE_BOX_CONTENT)
    {
      final RenderableReplacedContentBox contentBox = (RenderableReplacedContentBox) box;
      contentBox.setCachedHeight(ReplacedContentUtil.computeHeight
          (contentBox, 0, contentBox.getCachedWidth()));

    }
    else if (layoutNodeType == LayoutNodeTypes.TYPE_BOX_WATERMARK)
    {
      final WatermarkAreaBox watermarkAreaBox = (WatermarkAreaBox) box;
      box.setCachedHeight(watermarkAreaBox.getLogicalPage().getPageHeight());
    }
    return true;
  }

  protected void processBlockLevelNode(final RenderNode node)
  {
    // This could be anything, text, or an image.
    node.setCachedY(computeVerticalBlockPosition(node));

    if (node.getNodeType() == LayoutNodeTypes.TYPE_NODE_FINISHEDNODE)
    {
      final FinishedRenderNode fnode = (FinishedRenderNode) node;
      node.setCachedHeight(fnode.getLayoutedHeight());
    }
  }

  protected void finishBlockLevelBox(final RenderBox box)
  {
    if (checkCacheValid(box))
    {
      return;
    }

    performFinishTable(box);

    final int nodeType = box.getNodeType();
    if (nodeType == LayoutNodeTypes.TYPE_BOX_WATERMARK)
    {
      final WatermarkAreaBox watermarkAreaBox = (WatermarkAreaBox) box;
      box.setCachedHeight(watermarkAreaBox.getLogicalPage().getPageHeight());
    }
    else
    {
      final int layoutNodeType = box.getLayoutNodeType();
      final RenderBox watermark = isWatermark(box);
      if (watermark != null)
      {
        final WatermarkAreaBox watermarkAreaBox = (WatermarkAreaBox) watermark;
        box.setCachedHeight(watermarkAreaBox.getLogicalPage().getPageHeight());
      }
      else if ((layoutNodeType & LayoutNodeTypes.MASK_BOX_BLOCK) == LayoutNodeTypes.MASK_BOX_BLOCK)
      {
        box.setCachedHeight(computeBlockHeightAndAlign(box));
      }
      else if ((layoutNodeType & LayoutNodeTypes.MASK_BOX_ROW) == LayoutNodeTypes.MASK_BOX_ROW)
      {
        box.setCachedHeight(computeRowHeight(box, 0));
      }
      else
      {
        box.setCachedHeight(computeCanvasHeight(box));
      }
    }

    if (breakState.isActive())
    {
      final Object suspender = breakState.getSuspendItem();
      if (box.getInstanceId() == suspender)
      {
        breakState.setSuspendItem(null);
        return;
      }
      if (suspender != null)
      {
        return;
      }

      if (nodeType == LayoutNodeTypes.TYPE_BOX_PARAGRAPH)
      {
        breakState.deinit();
      }
    }
  }

  private RenderBox isWatermark(final RenderBox box)
  {
    final RenderBox parent = box.getParent();
    if (parent == null)
    {
      return null;
    }
    if (parent.getNodeType() == LayoutNodeTypes.TYPE_BOX_WATERMARK)
    {
      return parent;
    }
    final RenderBox parent2 = parent.getParent();
    if (parent2 == null)
    {
      return null;
    }
    if (parent2.getNodeType() == LayoutNodeTypes.TYPE_BOX_WATERMARK)
    {
      return parent2;
    }
    return null;
  }

  public static long computeVerticalBlockPosition(final RenderNode node)
  {
    // we have no margins yet ..
    final long marginTop = 0;

    // The y-position of a box depends on the parent.
    final RenderBox parent = node.getParent();

    // A table row is something special. Although it is a block box,
    // it layouts its children from left to right
    if (parent != null)
    {
      final RenderNode prev = node.getPrev();
      if (prev != null)
      {
        if (prev.isVisible())
        {
          // we have a silbling. Position yourself directly below your silbling ..
          return (marginTop + prev.getCachedY() + prev.getCachedHeight());
        }
        else
        {
          return (marginTop + prev.getCachedY());
        }
      }
      else
      {
        final StaticBoxLayoutProperties blp = parent.getStaticBoxLayoutProperties();
        final BoxDefinition bdef = parent.getBoxDefinition();
        final long insetTop = (blp.getBorderTop() + bdef.getPaddingTop());

        return (marginTop + insetTop + parent.getCachedY());
      }
    }
    else
    {
      // there's no parent ..
      return (marginTop);
    }
  }

  private long computeBlockHeightAndAlign(final RenderBox box)
  {
    return computeBlockHeightAndAlign(box, box.getBoxDefinition(), 0, true);
  }

  private static long computeTableHeightAndAlign(final RenderBox box)
  {
    return computeBlockHeightAndAlign(box, BoxDefinition.EMPTY, 0, true);
  }

  public static long computeBlockHeightAndAlign(final RenderBox box,
                                                final BoxDefinition boxDefinition,
                                                final long resolveSize,
                                                final boolean alignChilds)
  {
    if (resolveSize < 0)
    {
      throw new IllegalArgumentException("ResovleSize cannot be negative");
    }

    // Check the height. Set the height.
    final RenderLength preferredHeight = boxDefinition.getPreferredHeight();
    final RenderLength minimumHeight = boxDefinition.getMinimumHeight();
    final RenderLength maximumHeight = boxDefinition.getMaximumHeight();

    final long usedHeight;
    final long childY2;
    final long childY1;
    final RenderNode lastChildNode = box.getLastChild();
    if (lastChildNode != null)
    {
      childY1 = box.getFirstChild().getCachedY();
      if (lastChildNode.isVisible())
      {
        childY2 = lastChildNode.getCachedY() + lastChildNode.getCachedHeight() + lastChildNode.getEffectiveMarginBottom();
      }
      else
      {
        childY2 = lastChildNode.getCachedY();
      }
      usedHeight = (childY2 - childY1);
    }
    else
    {
      usedHeight = 0;
      childY2 = 0;
      childY1 = 0;
    }

    //final long blockContextWidth = box.getStaticBoxLayoutProperties().getBlockContextWidth();
    final long rminH = minimumHeight.resolve(resolveSize, 0);
    final long rmaxH = maximumHeight.resolve(resolveSize, InfiniteMajorAxisLayoutStep.MAX_AUTO);

    final StaticBoxLayoutProperties blp = box.getStaticBoxLayoutProperties();
    final long insetBottom = blp.getBorderBottom() + boxDefinition.getPaddingBottom();
    final long insetTop = blp.getBorderTop() + boxDefinition.getPaddingTop();

    // computed height is always the height of the content-box, excluding any paddings and borders
    final long computedContentHeight;
    if (boxDefinition.isSizeSpecifiesBorderBox())
    {
      final long rprefH = preferredHeight.resolve(resolveSize, usedHeight + insetTop + insetBottom);
      final long specifiedHeight = ProcessUtility.computeLength(rminH, rmaxH, rprefH);
      computedContentHeight = specifiedHeight - insetTop - insetBottom;
    }
    else
    {
      final long rprefH = preferredHeight.resolve(resolveSize, usedHeight);
      computedContentHeight = ProcessUtility.computeLength(rminH, rmaxH, rprefH);
    }

    if (alignChilds && lastChildNode != null)
    {
      // grab the node's y2
      if (computedContentHeight > usedHeight)
      {
        // we have extra space to distribute. So lets shift some boxes.
        final ElementAlignment valign = box.getNodeLayoutProperties().getVerticalAlignment();
        if (ElementAlignment.BOTTOM.equals(valign))
        {
          final long boxBottom = (box.getCachedY() + computedContentHeight - insetBottom);
          final long delta = boxBottom - childY2;
          CacheBoxShifter.shiftBoxChilds(box, delta);
        }
        else if (ElementAlignment.MIDDLE.equals(valign))
        {
          final long extraHeight = computedContentHeight - usedHeight;
          final long boxTop = box.getCachedY() + insetTop + (extraHeight / 2);
          final long delta = boxTop - childY1;
          CacheBoxShifter.shiftBoxChilds(box, delta);
        }
      }
    }

    final long retval = Math.max(0, computedContentHeight + insetTop + insetBottom);
    // For the water-mark area, this computation is different. The Watermark-area uses the known height of
    // the parent (=the page size)
    if (box.getNodeType() == LayoutNodeTypes.TYPE_BOX_WATERMARK)
    {
      final WatermarkAreaBox watermarkAreaBox = (WatermarkAreaBox) box;
      final LogicalPageBox lpb = watermarkAreaBox.getLogicalPage();
      // set the page-height as watermark size.
      return Math.max(retval, Math.max(0, lpb.getPageHeight() - insetTop - insetBottom));
    }
    return retval;
  }

  /**
   * We will do the alignment during the CanvasMajorAxisLayoutStep.
   *
   * @param box         the box to be computed. Must be a box with row-layout
   * @param resolveSize the current height that makes 100%
   * @return the row's height.
   */
  private long computeRowHeight(final RenderBox box, final long resolveSize)
  {
    // For the water-mark area, this computation is different. The Watermark-area uses the known height of
    // the parent (=the page size)
    if (box.getNodeType() == LayoutNodeTypes.TYPE_BOX_WATERMARK)
    {
      final WatermarkAreaBox watermarkAreaBox = (WatermarkAreaBox) box;
      final LogicalPageBox lpb = watermarkAreaBox.getLogicalPage();
      // set the page-height as watermark size.
      return lpb.getPageHeight();
    }

    // Check the height. Set the height.
    final BoxDefinition boxDefinition = box.getBoxDefinition();
    final RenderLength preferredHeight = boxDefinition.getPreferredHeight();
    final RenderLength minimumHeight = boxDefinition.getMinimumHeight();
    final RenderLength maximumHeight = boxDefinition.getMaximumHeight();

    final StaticBoxLayoutProperties blp = box.getStaticBoxLayoutProperties();
    final long insetBottom = blp.getBorderBottom() + boxDefinition.getPaddingBottom();
    final long insetTop = blp.getBorderTop() + boxDefinition.getPaddingTop();

    // usedHeight already contains the insetsTop ..
    final long usedHeight;
    RenderNode child = box.getFirstChild();
    if (child != null)
    {
      long maxChildY2 = 0;
      while (child != null)
      {
        if (child.isVisible())
        {
          final long childY2 = child.getCachedY() + child.getCachedHeight() + child.getEffectiveMarginBottom();
          maxChildY2 = Math.max(childY2, maxChildY2);
        }
        child = child.getNext();
      }
      usedHeight = (maxChildY2 - box.getCachedY());
    }
    else
    {
      usedHeight = insetTop;
    }

    final long rminH = minimumHeight.resolve(resolveSize, 0);
    final long rmaxH = maximumHeight.resolve(resolveSize, InfiniteMajorAxisLayoutStep.MAX_AUTO);

    final long computedHeight; // always the height of the content box
    if (boxDefinition.isSizeSpecifiesBorderBox())
    {
      final long rprefH = preferredHeight.resolve(resolveSize, usedHeight + insetBottom);
      final long specifiedHeight = ProcessUtility.computeLength(rminH, rmaxH, rprefH);
      computedHeight = Math.max(0, specifiedHeight - insetTop - insetBottom);
    }
    else
    {
      final long rprefH = preferredHeight.resolve(resolveSize, usedHeight - insetTop);
      computedHeight = Math.max(0, ProcessUtility.computeLength(rminH, rmaxH, rprefH));
    }
    return Math.max(0, computedHeight + insetTop + insetBottom);
  }

  protected void processParagraphChilds(final ParagraphRenderBox box)
  {
    // todo Arabic text
    if(complexText) {
      processBoxChilds(box);
    }
    else {
      // Process the direct childs of the paragraph
      // Each direct child represents a line ..

      RenderNode node = box.getFirstChild();
      while (node != null)
      {
        // all childs of the linebox container must be inline boxes. They
        // represent the lines in the paragraph. Any other element here is
        // a error that must be reported
        final ParagraphPoolBox inlineRenderBox = (ParagraphPoolBox) node;
        if (startLine(inlineRenderBox))
        {
          processBoxChilds(inlineRenderBox);
          finishLine(inlineRenderBox);
        }

        node = node.getNext();
      }
    }
  }

  private boolean startLine(final ParagraphPoolBox box)
  {
    box.setCachedY(computeVerticalBlockPosition(box));

    if (breakState.isActive() == false)
    {
      return false;
    }

    if (breakState.isSuspended())
    {
      return false;
    }

    breakState.openContext(box);
    return true;
  }

  private void finishLine(final ParagraphPoolBox inlineRenderBox)
  {
    if (breakState.isActive() == false || breakState.isSuspended())
    {
      return;
    }

    final BoxAlignContext boxAlignContext = breakState.closeContext();

    // This aligns all direct childs. Once that is finished, we have to
    // check, whether possibly existing inner-paragraphs are still valid
    // or whether moving them violated any of the inner-pagebreak constraints.

    final StaticBoxLayoutProperties blp = inlineRenderBox.getStaticBoxLayoutProperties();
    final BoxDefinition bdef = inlineRenderBox.getBoxDefinition();
    final long insetTop = (blp.getBorderTop() + bdef.getPaddingTop());

    final long contentAreaY1 = inlineRenderBox.getCachedY() + insetTop;
    final long lineHeight = inlineRenderBox.getLineHeight();
    processor.align(boxAlignContext, contentAreaY1, lineHeight);
  }


  protected boolean startInlineLevelBox(final RenderBox box)
  {
    if (checkCacheValid(box))
    {
      return false;
    }

    box.setCachedY(computeVerticalInlinePosition(box));
    computeBaselineInfo(box);

    if (breakState == null)
    {
      // ignore .. should not happen anyway ..
      return true;
    }

    if (breakState.isSuspended())
    {
      return false;
    }

    final int nodeType = box.getLayoutNodeType();
    if ((nodeType & LayoutNodeTypes.MASK_BOX_INLINE) == LayoutNodeTypes.MASK_BOX_INLINE)
    {
      breakState.openContext(box);
      return true;
    }
    else if (nodeType == LayoutNodeTypes.TYPE_BOX_CONTENT)
    {
      breakState.getCurrentLine().addChild(new ReplacedContentAlignContext((RenderableReplacedContentBox) box, 0));
      return false;
    }

    breakState.getCurrentLine().addChild(new InlineBlockAlignContext(box));
    breakState.setSuspendItem(box.getInstanceId());
    return false;
  }

  private void computeBaselineInfo(final RenderBox box)
  {
    if (box.getBaselineInfo() == null)
    {
      return;
    }

    RenderNode node = box.getFirstChild();
    while (node != null)
    {
      if (node.getNodeType() == LayoutNodeTypes.TYPE_NODE_TEXT)
      {
        // grab the baseline info from there ...
        final RenderableText text = (RenderableText) node;
        box.setBaselineInfo(text.getBaselineInfo());
        break;
      }

      node = node.getNext();
    }

    // If we have no baseline info here, ask the parent. If that one has none
    // either, then we cant do anything about it.
    if (box.getBaselineInfo() == null)
    {
      box.setBaselineInfo(box.getStaticBoxLayoutProperties().getNominalBaselineInfo());
    }
  }

  protected void processInlineLevelNode(final RenderNode node)
  {
    // compute the intial position.
    node.setCachedY(computeVerticalInlinePosition(node));
    // the height and the real position will be computed during the vertical-alignment computation.

    if (breakState.isActive() == false || breakState.isSuspended())
    {
      return;
    }

    if(complexText) {
      return;
    }

    if (node.getNodeType() == LayoutNodeTypes.TYPE_NODE_TEXT)
    {
      breakState.getCurrentLine().addChild(new TextElementAlignContext((RenderableText) node));
    }
    else
    {
      breakState.getCurrentLine().addChild(new NodeAlignContext(node));
    }
  }


  protected void finishInlineLevelBox(final RenderBox box)
  {
    // todo Arabic text

    if (checkCacheValid(box))
    {
      return;
    }

    // The height of an inline-level box will be computed when the vertical-alignemnt is done.

    if (breakState.isActive() == false)
    {
      return;
    }

    final int nodeType = box.getLayoutNodeType();
    if ((nodeType & LayoutNodeTypes.MASK_BOX_INLINE) == LayoutNodeTypes.MASK_BOX_INLINE)
    {
      breakState.closeContext();
      return;
    }

    final Object suspender = breakState.getSuspendItem();
    if (box.getInstanceId() == suspender)
    {
      breakState.setSuspendItem(null);
      return;
    }

    if (suspender != null)
    {
      return;
    }

    if (nodeType == LayoutNodeTypes.TYPE_BOX_PARAGRAPH)
    {
      throw new IllegalStateException("This cannot be; Why is there a paragraph inside a inline context");
    }
  }

  private long computeVerticalInlinePosition(final RenderNode node)
  {
    final RenderBox parent = node.getParent();

    if (parent != null)
    {
      // the computed position of an inline-element must be the same as the position of the parent element.
      // A inline-box always has an other inline-box as parent (the paragraph-pool-box is the only exception;
      // and this one is handled elsewhere).

      // Top and bottom margins are not applied to inline-elements.
      final StaticBoxLayoutProperties blp = parent.getStaticBoxLayoutProperties();
      final BoxDefinition bdef = parent.getBoxDefinition();
      final long insetTop = (blp.getBorderTop() + bdef.getPaddingTop());

      return (insetTop + parent.getCachedY());
    }
    else
    {
      // there's no parent .. Should not happen, shouldn't it?
      return (0);
    }
  }


  protected boolean startCanvasLevelBox(final RenderBox box)
  {
    if (checkCacheValid(box))
    {
      return false;
    }

    performStartTable(box);

    box.setCachedY(computeVerticalCanvasPosition(box));

    if (breakState.isActive() == false)
    {
      final int nodeType = box.getNodeType();
      if (nodeType == LayoutNodeTypes.TYPE_BOX_PARAGRAPH)
      {
        final ParagraphRenderBox paragraphBox = (ParagraphRenderBox) box;
        // We cant cache that ... the shift operations later would misbehave
        // One way around would be to at least store the layouted offsets
        // (which should be immutable as long as the line did not change its
        // contents) and to reapply them on each run. This is cheaper than
        // having to compute the whole v-align for the whole line.
        breakState.init(paragraphBox);
      }
      else if (nodeType == LayoutNodeTypes.TYPE_BOX_CONTENT)
      {
        final RenderableReplacedContentBox rpc = (RenderableReplacedContentBox) box;
        final long computedHeight = ReplacedContentUtil.computeHeight(rpc, 0, box.getCachedWidth());
        box.setCachedHeight(computedHeight);
      }
      return true;
    }

    // No breakstate and not being suspended? Why this?
    if (breakState.isSuspended() == false)
    {
      throw new IllegalStateException("This cannot be: No breakstate and not being suspended? Why this?");
    }

    // this way or another - we are suspended now. So there is no need to look
    // at the children anymore ..
    return false;
  }

  protected void processCanvasLevelNode(final RenderNode node)
  {
    node.setCachedY(computeVerticalCanvasPosition(node));

    if (node.getNodeType() == LayoutNodeTypes.TYPE_NODE_FINISHEDNODE)
    {
      final FinishedRenderNode fnode = (FinishedRenderNode) node;
      node.setCachedHeight(fnode.getLayoutedHeight());
    }
    else
    {
      node.setCachedHeight(0);
    }
  }

  /**
   * Finishes up a canvas level box. This updates/affects the height of the parent, as the canvas model defines that the
   * parent always fully encloses all of its childs.
   * <p/>
   * When no preferred height is defined, the height of an element is the maximum of its minimum-height and the absolute
   * height of all of its direct children.
   * <p/>
   * To resolve the value of percentages, the system uses the maximum of the parent's height and the maximum of all (y +
   * height) of all children.)
   *
   * @param box the box.
   */
  protected void finishCanvasLevelBox(final RenderBox box)
  {
    if (checkCacheValid(box))
    {
      return;
    }

    performFinishTable(box);

    final int layoutNodeType = box.getLayoutNodeType();
    if ((layoutNodeType & LayoutNodeTypes.MASK_BOX_BLOCK) == LayoutNodeTypes.MASK_BOX_BLOCK)
    {
      box.setCachedHeight(computeBlockHeightAndAlign(box));
    }
    else if ((layoutNodeType & LayoutNodeTypes.MASK_BOX_ROW) == LayoutNodeTypes.MASK_BOX_ROW)
    {
      box.setCachedHeight(computeRowHeight(box, 0));
    }
    else if (layoutNodeType == LayoutNodeTypes.TYPE_BOX_CONTENT)
    {
      // ignored ...
    }
    else
    {
      box.setCachedHeight(computeCanvasHeight(box));
    }

    if (breakState.isActive())
    {
      final Object suspender = breakState.getSuspendItem();
      if (box.getInstanceId() == suspender)
      {
        breakState.setSuspendItem(null);
        return;
      }
      if (suspender != null)
      {
        return;
      }

      if (layoutNodeType == LayoutNodeTypes.TYPE_BOX_PARAGRAPH)
      {
        breakState.deinit();
      }
    }
  }

  private long computeVerticalCanvasPosition(final RenderNode node)
  {
    final RenderBox parent = node.getParent();
    final long parentPosition;
    if (parent == null)
    {
      parentPosition = 0;
    }
    else
    {
      final StaticBoxLayoutProperties blp = parent.getStaticBoxLayoutProperties();
      final BoxDefinition bdef = parent.getBoxDefinition();
      final long insetsTop = (blp.getBorderTop() + bdef.getPaddingTop());
      parentPosition = parent.getCachedY() + insetsTop;
    }

    final double posY = node.getNodeLayoutProperties().getPosY();
    if (node.isSizeSpecifiesBorderBox())
    {
      return (parentPosition + RenderLength.resolveLength(0, posY));
    }
    else
    {
      final long insetsTop;
      if ((node.getLayoutNodeType() & LayoutNodeTypes.MASK_BOX) == LayoutNodeTypes.MASK_BOX)
      {
        final RenderBox box = (RenderBox) node;
        final StaticBoxLayoutProperties blp = box.getStaticBoxLayoutProperties();
        final BoxDefinition bdef = box.getBoxDefinition();
        insetsTop = (blp.getBorderTop() + bdef.getPaddingTop());
      }
      else
      {
        insetsTop = 0;
      }
      return (parentPosition + RenderLength.resolveLength(0, posY) - insetsTop);
    }
  }

  private static long computeCanvasHeight(final RenderBox box)
  {
    final StaticBoxLayoutProperties blp = box.getStaticBoxLayoutProperties();
    final BoxDefinition bdef = box.getBoxDefinition();

    final BoxDefinition boxDefinition = box.getBoxDefinition();
    final RenderLength minHeight = boxDefinition.getMinimumHeight();
    final RenderLength preferredHeight = boxDefinition.getPreferredHeight();
    final RenderLength maxHeight = boxDefinition.getMaximumHeight();

    final long insetsTop = (blp.getBorderTop() + bdef.getPaddingTop());
    final long insetsBottom = blp.getBorderBottom() + bdef.getPaddingBottom();
    final long insets = insetsTop + insetsBottom;

    // find the maximum of the used height (for all childs) and the specified min-height.
    final long minHeightResolved = minHeight.resolve(0);
    long consumedHeight;
    if (box.isSizeSpecifiesBorderBox())
    {
      consumedHeight = Math.max(minHeightResolved, insets) - insetsBottom;
    }
    else
    {
      consumedHeight = minHeightResolved + insetsTop;
    }

    final long boxY = box.getCachedY();

    RenderNode node = box.getFirstChild();
    while (node != null)
    {
      final long childY2 = (node.getCachedY() + node.getCachedHeight());
      final long childLocalY2 = childY2 - boxY;
      if (childLocalY2 > consumedHeight)
      {
        consumedHeight = childLocalY2;
      }
      node = node.getNext();
    }

    consumedHeight += insetsBottom;

    // The consumed height computed above specifies the size at the border-edge.
    // However, depending on the box-sizing property, we may have to resolve them against the
    // content-edge instead.

    final long maxHeightResolved = maxHeight.resolve(0, InfiniteMajorAxisLayoutStep.MAX_AUTO);
    if (box.isSizeSpecifiesBorderBox())
    {
      final long prefHeightResolved;
      if (RenderLength.AUTO.equals(preferredHeight))
      {
        prefHeightResolved = consumedHeight;
      }
      else if (preferredHeight.isPercentage() == false)
      {
        prefHeightResolved = preferredHeight.resolve(0);
      }
      else
      {
        prefHeightResolved = consumedHeight;
      }

      final long height = ProcessUtility.computeLength(minHeightResolved, maxHeightResolved, prefHeightResolved);
      return (height);
    }
    else
    {
      consumedHeight = Math.max(0, consumedHeight - insets);
      final long prefHeightResolved;
      if (RenderLength.AUTO.equals(preferredHeight))
      {
        prefHeightResolved = consumedHeight;
      }
      else if (preferredHeight.isPercentage() == false)
      {
        prefHeightResolved = preferredHeight.resolve(0);
      }
      else
      {
        prefHeightResolved = consumedHeight;
      }

      final long height = ProcessUtility.computeLength(minHeightResolved, maxHeightResolved,
          prefHeightResolved);
      return (height + insets);
    }
  }

  protected boolean startRowLevelBox(final RenderBox box)
  {
    if (checkCacheValid(box))
    {
      return false;
    }

    performStartTable(box);

    // Compute the block-position of the box. The box is positioned relative to the previous sibling or
    // relative to the parent.
    box.setCachedY(computeVerticalRowPosition(box));

    if (breakState.isActive())
    {
      // No breakstate and not being suspended? Why this?
      if (breakState.isSuspended() == false)
      {
        throw new IllegalStateException("This cannot be.");
      }

      // this way or another - we are suspended now. So there is no need to look
      // at the children anymore ..

      // This code is only executed for inline-block elements. Inline-block elements are not part of
      // the 0.8.9 or 1.0 engine layouting.
      return false;
    }

    final int layoutNodeType = box.getLayoutNodeType();
    if (layoutNodeType == LayoutNodeTypes.TYPE_BOX_PARAGRAPH)
    {
      final ParagraphRenderBox paragraphBox = (ParagraphRenderBox) box;
      // We cant cache that ... the shift operations later would misbehave
      // One way around would be to at least store the layouted offsets
      // (which should be immutable as long as the line did not change its
      // contents) and to reapply them on each run. This is cheaper than
      // having to compute the whole v-align for the whole line.
      breakState.init(paragraphBox);
    }
    else if (layoutNodeType == LayoutNodeTypes.TYPE_BOX_CONTENT)
    {
      final RenderableReplacedContentBox rpc = (RenderableReplacedContentBox) box;
      box.setCachedHeight(ReplacedContentUtil.computeHeight(rpc, 0, box.getCachedWidth()));
    }


    return true;
  }

  protected void processRowLevelNode(final RenderNode node)
  {
    node.setCachedY(computeVerticalRowPosition(node));

    final int nodeType = node.getLayoutNodeType();
    if (nodeType == LayoutNodeTypes.TYPE_NODE_FINISHEDNODE)
    {
      final FinishedRenderNode fnode = (FinishedRenderNode) node;
      node.setCachedHeight(fnode.getLayoutedHeight());
    }
    else if ((nodeType & LayoutNodeTypes.MASK_BOX_INLINE) == LayoutNodeTypes.MASK_BOX_INLINE)
    {
      throw new IllegalStateException("A Inline-Box must be contained in a paragraph.");
    }
  }

  protected void finishRowLevelBox(final RenderBox box)
  {
    if (checkCacheValid(box))
    {
      return;
    }

    performFinishTable(box);

    final int layoutNodeType = box.getLayoutNodeType();
    if ((layoutNodeType & LayoutNodeTypes.MASK_BOX_BLOCK) == LayoutNodeTypes.MASK_BOX_BLOCK)
    {
      box.setCachedHeight(computeBlockHeightAndAlign(box));
    }
    else if ((layoutNodeType & LayoutNodeTypes.MASK_BOX_ROW) == LayoutNodeTypes.MASK_BOX_ROW)
    {
      box.setCachedHeight(computeRowHeight(box, 0));
    }
    else if (layoutNodeType == LayoutNodeTypes.TYPE_BOX_CONTENT)
    {
      // ignored ..
    }
    else
    {
      box.setCachedHeight(computeCanvasHeight(box));
    }

    if (breakState.isActive())
    {
      final Object suspender = breakState.getSuspendItem();
      if (box.getInstanceId() == suspender)
      {
        breakState.setSuspendItem(null);
        return;
      }
      if (suspender != null)
      {
        return;
      }

      if (layoutNodeType == LayoutNodeTypes.TYPE_BOX_PARAGRAPH)
      {
        breakState.deinit();
      }
    }
  }

  private long computeVerticalRowPosition(final RenderNode node)
  {
    final RenderBox parent = node.getParent();

    if (parent != null)
    {
      // the computed position of an inline-element must be the same as the position of the parent element.
      // A inline-box always has an other inline-box as parent (the paragraph-pool-box is the only exception;
      // and this one is handled elsewhere).

      // Top and bottom margins are not applied to inline-elements.
      final StaticBoxLayoutProperties blp = parent.getStaticBoxLayoutProperties();
      final BoxDefinition bdef = parent.getBoxDefinition();
      final long insetTop = (blp.getBorderTop() + bdef.getPaddingTop());

      return (insetTop + parent.getCachedY());
    }
    else
    {
      // there's no parent .. Should not happen, shouldn't it?
      return (0);
    }
  }

  protected boolean startTableCellLevelBox(final RenderBox box)
  {
    // table cells behave like block-level cells most of the time.
    return startBlockLevelBox(box);
  }

  protected void finishTableCellLevelBox(final RenderBox box)
  {
    // table cells behave like block-level cells most of the time.
    finishBlockLevelBox(box);
  }

  protected boolean startTableRowLevelBox(final RenderBox box)
  {
    box.setCachedY(computeVerticalRowPosition(box));
    if (box.getNodeType() == LayoutNodeTypes.TYPE_BOX_TABLE_CELL)
    {
      getTableRowHeightStep().startTableCell((TableCellRenderBox) box);
    }
    else
    {
      final long blockHeight = computeTableHeightAndAlign(box);
      box.setCachedHeight(blockHeight);
    }

    markAllChildsDirty(box);
    return true;
  }

  protected void finishTableRowLevelBox(final RenderBox box)
  {
    clearAllChildsDirtyMarker(box);
    if (box.getNodeType() == LayoutNodeTypes.TYPE_BOX_TABLE_CELL)
    {
      final long blockHeight = computeTableHeightAndAlign(box);
      getTableRowHeightStep().finishTableCell((TableCellRenderBox) box, blockHeight);
    }
    else
    {
      final long blockHeight = computeTableHeightAndAlign(box);
      box.setCachedHeight(blockHeight);
    }
  }

  protected boolean startTableLevelBox(final RenderBox box)
  {
    box.setCachedY(computeVerticalBlockPosition(box));

    if (box instanceof TableSectionRenderBox)
    {
      getTableRowHeightStep().startTableSection((TableSectionRenderBox) box);
    }
    return true;
  }

  protected void processTableLevelNode(final RenderNode node)
  {
    processBlockLevelNode(node);
  }

  protected void finishTableLevelBox(final RenderBox box)
  {
    box.setCachedHeight(computeTableHeightAndAlign(box));

    if (box instanceof TableSectionRenderBox)
    {
      getTableRowHeightStep().finishTableSection((TableSectionRenderBox) box);
    }
  }

  protected boolean startTableSectionLevelBox(final RenderBox box)
  {
    if (box instanceof TableRowRenderBox)
    {
      getTableRowHeightStep().startTableRow((TableRowRenderBox) box);
    }

    box.setCachedY(computeVerticalBlockPosition(box));
    return true;
  }

  protected void processTableSectionLevelNode(final RenderNode node)
  {
    processBlockLevelNode(node);
  }

  protected void finishTableSectionLevelBox(final RenderBox box)
  {
    box.setCachedHeight(0);
  }
}
TOP

Related Classes of org.pentaho.reporting.engine.classic.core.layout.process.InfiniteMajorAxisLayoutStep

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.