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

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

/*
* 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 - 2009 Object Refinery Ltd, Pentaho Corporation and Contributors..  All rights reserved.
*/

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

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
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.PageGrid;
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.context.BoxDefinition;
import org.pentaho.reporting.engine.classic.core.layout.model.context.StaticBoxLayoutProperties;
import org.pentaho.reporting.engine.classic.core.layout.process.alignment.CenterAlignmentProcessor;
import org.pentaho.reporting.engine.classic.core.layout.process.alignment.LeftAlignmentProcessor;
import org.pentaho.reporting.engine.classic.core.layout.process.alignment.RightAlignmentProcessor;
import org.pentaho.reporting.engine.classic.core.layout.process.alignment.TextAlignmentProcessor;
import org.pentaho.reporting.engine.classic.core.layout.process.layoutrules.EndSequenceElement;
import org.pentaho.reporting.engine.classic.core.layout.process.layoutrules.InlineBoxSequenceElement;
import org.pentaho.reporting.engine.classic.core.layout.process.layoutrules.InlineNodeSequenceElement;
import org.pentaho.reporting.engine.classic.core.layout.process.layoutrules.ReplacedContentSequenceElement;
import org.pentaho.reporting.engine.classic.core.layout.process.layoutrules.SequenceList;
import org.pentaho.reporting.engine.classic.core.layout.process.layoutrules.SpacerSequenceElement;
import org.pentaho.reporting.engine.classic.core.layout.process.layoutrules.StartSequenceElement;
import org.pentaho.reporting.engine.classic.core.layout.process.layoutrules.TextSequenceElement;
import org.pentaho.reporting.engine.classic.core.layout.output.OutputProcessorMetaData;
import org.pentaho.reporting.engine.classic.core.style.StyleSheet;
import org.pentaho.reporting.engine.classic.core.style.TextStyleKeys;
import org.pentaho.reporting.engine.classic.core.style.WhitespaceCollapse;
import org.pentaho.reporting.engine.classic.core.style.ElementStyleKeys;


/**
* This process-step computes the effective layout, but it does not take horizontal pagebreaks into account. (It has to
* deal with vertical breaks, as they affect the text layout.)
* <p/>
* This processing step does not ajust anything on the vertical axis. Vertical alignment is handled in a second step.
* <p/>
* Please note: This layout model (unlike the default CSS model) uses the BOX-WIDTH as computed with. This means, the
* defined width specifies the sum of all borders, paddings and the content area width.
*
* @author Thomas Morgner
*/
public final class InfiniteMinorAxisLayoutStep extends IterateVisualProcessStep
{
  private static final Log logger = LogFactory.getLog(InfiniteMinorAxisLayoutStep.class);

  private MinorAxisParagraphBreakState breakState;
  private PageGrid pageGrid;
  private RenderBox continuedElement;

  private TextAlignmentProcessor centerProcessor;
  private TextAlignmentProcessor rightProcessor;
  private TextAlignmentProcessor leftProcessor;
  private OutputProcessorMetaData metaData;

  public InfiniteMinorAxisLayoutStep(final OutputProcessorMetaData metaData)
  {
    this.metaData = metaData;
    breakState = new MinorAxisParagraphBreakState();
  }

  public void compute(final LogicalPageBox root)
  {
    try
    {
      continuedElement = null;
      pageGrid = root.getPageGrid();
      startProcessing(root);
    }
    finally
    {
      continuedElement = null;
      pageGrid = null;
      breakState.deinit();
    }
  }

  /**
   * Continues processing. The renderbox must have a valid x-layout (that is: X, content-X1, content-X2 and Width)
   *
   * @param pageGrid
   * @param box
   */
  public void continueComputation(final PageGrid pageGrid,
                                  final RenderBox box)
  {
    if (box.getContentAreaX2() == 0 || box.getCachedWidth() == 0)
    {
      throw new IllegalStateException("Box must be layouted a bit ..");
    }

    try
    {
      this.pageGrid = pageGrid;
      this.breakState.deinit();
      this.continuedElement = box;
      startProcessing(box);
    }
    finally
    {
      this.continuedElement = null;
      this.pageGrid = null;
      this.breakState.deinit();
    }
  }

  /**
   * The whole computation is only done for exactly one nesting level of paragraphs. If we encounter an inline-block or
   * inline-table, we handle them as a single element.
   *
   * @param box
   * @return
   */
  protected boolean startBlockLevelBox(final RenderBox box)
  {
    // first, compute the position. The position is global, not relative to a
    // parent or so. Therefore a child has no connection to the parent's
    // effective position, when it is painted.

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

      computeContentArea(box, computeBlockPosition(box));

      if (box.getNodeType() == LayoutNodeTypes.TYPE_BOX_PARAGRAPH)
      {

        final ParagraphRenderBox paragraphBox = (ParagraphRenderBox) box;

        if (continuedElement == null)
        {
          final long lineBoxChangeTracker = paragraphBox.getEffectiveLineboxContainer().getChangeTracker();
          final boolean unchanged = lineBoxChangeTracker == paragraphBox.getMinorLayoutAge();
          if (unchanged)
          {
            return false;
          }
        }

        paragraphBox.clearLayout();
        breakState.init(paragraphBox);
      }
      return true;
    }


    if (breakState.isSuspended() == false)
    {
      // The break-state exists only while we are inside of an paragraph
      // and suspend can only happen on inline elements.
      // A block-element inside a paragraph cannot be (and if it does, it is
      // a bug)
      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 ..
    return false;
  }

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

    applyComputedWidth(box);

    // find the maximum width of all childs and adjust this box's width to match that.
    // take the maximum and preferred size into account as well. If a preferred size
    // is set, then ignore the childs ..

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

      if (box.getNodeType() == LayoutNodeTypes.TYPE_BOX_PARAGRAPH)
      {
        // finally update the change tracker ..
        final ParagraphRenderBox paraBox = (ParagraphRenderBox) box;
        paraBox.setMinorLayoutAge(paraBox.getEffectiveLineboxContainer().getChangeTracker());

        breakState.deinit();
      }
    }

  }

  /**
   * Computes the effective content area. The content area is the space that can be used by any of the childs of the
   * given box.
   * <p/>
   * InlineBoxes get computed in the alignment processor.
   *
   * @param box the block render box for which we compute the content area
   */
  private void computeContentArea(final RenderBox box, final long x)
  {
    if (box == continuedElement)
    {
      return;
    }

    final BoxDefinition bdef = box.getBoxDefinition();
    final StaticBoxLayoutProperties blp = box.getStaticBoxLayoutProperties();
    box.setCachedX(x);
    // next, compute the width ...

    final long leftPadding = blp.getBorderLeft() + bdef.getPaddingLeft();
    final long rightPadding = blp.getBorderRight() + bdef.getPaddingRight();

    // computed width is box-size, so it contains the paddings already
    final long computedWidth = box.getComputedWidth();

    final long bcw = ProcessUtility.computeBlockContextWidth(box);
    final BoxDefinition boxDef = box.getBoxDefinition();
    final RenderLength minLength = boxDef.getMinimumWidth();
    final RenderLength prefLength = boxDef.getPreferredWidth();
    final RenderLength maxLength = boxDef.getMaximumWidth();
    final long min = minLength.resolve(bcw, 0);
    final long max = maxLength.resolve(bcw, ComputeStaticPropertiesProcessStep.MAX_AUTO);

    final long contentAreaX1 = x + leftPadding;
    box.setContentAreaX1(contentAreaX1);

    if (box.getBoxDefinition().isSizeSpecifiesBorderBox())
    {
      final long minChunkWidth;
      if (box.getStyleSheet().getBooleanStyleProperty(ElementStyleKeys.USE_MIN_CHUNKWIDTH))
      {
        minChunkWidth = box.getMinimumChunkWidth() + leftPadding + rightPadding;
      }
      else
      {
        minChunkWidth = leftPadding + rightPadding;
      }
      final long pref = prefLength.resolve(bcw, Math.max(computedWidth, minChunkWidth));

      final long width = Math.max(0, ProcessUtility.computeLength(min, max, pref));
      final long contentAreaX2 = x + width - rightPadding;
      if (contentAreaX1 < contentAreaX2)
      {
        box.setContentAreaX2(contentAreaX2);
      }
      else
      {
        // autocorrect to a zero-width box.
        box.setContentAreaX2(contentAreaX1);
      }
    }
    else
    {
      final long minChunkWidth;
      if (box.getStyleSheet().getBooleanStyleProperty(ElementStyleKeys.USE_MIN_CHUNKWIDTH))
      {
        minChunkWidth = box.getMinimumChunkWidth();
      }
      else
      {
        minChunkWidth = 0;
      }
      final long computedContentWidth = Math.max(0, computedWidth - leftPadding - rightPadding);
      final long pref = prefLength.resolve(bcw, Math.max(computedContentWidth, minChunkWidth));
      final long width = Math.max(0, ProcessUtility.computeLength(min, max, pref));
      box.setContentAreaX2(contentAreaX1 + width);
    }
  }

  private void computeNodeWidth(final RenderNode box)
  {
    if (box == continuedElement)
    {
      return;
    }

    // next, compute the width ...
    box.setCachedWidth(box.getComputedWidth());
  }

  /**
   * Ensures that the width of the box does not exceed the computed width. For canvas level boxes, we have to stick with
   * the minimum width as defined by the user or the layout would become unpredictable.
   *
   * @param box
   */
  private void applyStrictComputedWidth(final RenderBox box)
  {
    final BoxDefinition bdef = box.getBoxDefinition();
    final StaticBoxLayoutProperties blp = box.getStaticBoxLayoutProperties();
    // next, compute the width ...

    final long leftPadding = blp.getBorderLeft() + bdef.getPaddingLeft();
    final long rightPadding = blp.getBorderRight() + bdef.getPaddingRight();

    // computed width is box-size, so it contains the paddings already
    final long computedWidth = box.getComputedWidth();
    final long contentAreaX1 = box.getContentAreaX1();
    long usedX2 = box.getContentAreaX2();
    if (box.getStyleSheet().getBooleanStyleProperty(ElementStyleKeys.USE_MIN_CHUNKWIDTH))
    {
      RenderNode node = box.getFirstChild();
      while (node != null)
      {
        final long x2 = node.getCachedX() + node.getCachedWidth();
        if (x2 > usedX2)
        {
          usedX2 = x2;
        }
        node = node.getNext();
      }
    }
    final long usedContentWidth = (usedX2 - contentAreaX1);

    final long bcw = ProcessUtility.computeBlockContextWidth(box);
    final BoxDefinition boxDef = box.getBoxDefinition();
    final RenderLength minLength = boxDef.getMinimumWidth();
    final RenderLength prefLength = boxDef.getPreferredWidth();
    final RenderLength maxLength = boxDef.getMaximumWidth();

    final long min = minLength.resolve(bcw, 0);
    final long max = maxLength.resolve(bcw, ComputeStaticPropertiesProcessStep.MAX_AUTO);

    if (box.getBoxDefinition().isSizeSpecifiesBorderBox())
    {
      final long minChunkWidth = usedContentWidth + leftPadding + rightPadding;
      final long pref = prefLength.resolve(bcw, Math.max(computedWidth, minChunkWidth));
      final long width = Math.max(0, ProcessUtility.computeLength(min, max, pref));
      final long contentAreaX2 = box.getCachedX() + width - rightPadding;
      if (contentAreaX1 < contentAreaX2)
      {
        box.setContentAreaX2(contentAreaX2);
      }
      else
      {
        // autocorrect to a zero-width box.
        box.setContentAreaX2(contentAreaX1);
      }
    }
    else
    {
      final long computedContentWidth = Math.max(0, computedWidth - leftPadding - rightPadding);
      final long pref = prefLength.resolve(bcw, Math.max(computedContentWidth, usedContentWidth));
      final long width = Math.max(0, ProcessUtility.computeLength(min, max, pref));
      box.setContentAreaX2(contentAreaX1 + width);
    }
    box.setCachedWidth((box.getContentAreaX2() + rightPadding) - box.getCachedX());
  }

  /**
   * Verifies the content width and produces the effective box width. This expands the box as necessary, and is a
   * requirement for row-layout and block-layout.
   *
   * @param box
   */
  private void applyComputedWidth(final RenderBox box)
  {
    if (RenderLength.AUTO.equals(box.getBoxDefinition().getPreferredWidth()) == false)
    {
      // if an explicit preferred width was set, we accept it unchallenged.

      final long x = box.getCachedX();
      final long contentEnd = box.getContentAreaX2();
      final StaticBoxLayoutProperties blp = box.getStaticBoxLayoutProperties();
      final BoxDefinition bdef = box.getBoxDefinition();
      final long boxEnd = contentEnd + blp.getBorderRight() + bdef.getPaddingRight();
      box.setCachedWidth(boxEnd - x);
      return;
    }


    long nodeX2 = box.getContentAreaX2();
    if (box.getStyleSheet().getBooleanStyleProperty(ElementStyleKeys.USE_MIN_CHUNKWIDTH))
    {
      RenderNode node = box.getFirstChild();
      while (node != null)
      {
        final long nodeWidth = node.getCachedWidth() + node.getCachedX();
        if (nodeWidth > nodeX2)
        {
          nodeX2 = nodeWidth;
        }
        node = node.getNext();
      }
    }

    // now apply the maximum X2 we just computed ..

    final long x = box.getCachedX();
    final StaticBoxLayoutProperties blp = box.getStaticBoxLayoutProperties();
    final BoxDefinition bdef = box.getBoxDefinition();
    final long boxEnd = nodeX2 + blp.getBorderRight() + bdef.getPaddingRight();
    box.setCachedWidth(boxEnd - x);
  }

  protected boolean startInlineLevelBox(final RenderBox box)
  {
    if (breakState.isActive() == false)
    {
      // ignore .. should not happen anyway ..
      if (box.isCacheValid())
      {
        return false;
      }
      return true;
    }

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

    if (box.getNodeType() == LayoutNodeTypes.TYPE_BOX_CONTENT)
    {
      breakState.add(ReplacedContentSequenceElement.INSTANCE, box);
      return false;
    }

    if ((box.getNodeType() & LayoutNodeTypes.MASK_BOX_INLINE) == LayoutNodeTypes.MASK_BOX_INLINE)
    {
      breakState.add(StartSequenceElement.INSTANCE, box);
      return true;
    }

    computeContentArea(box, 0);

    breakState.add(InlineBoxSequenceElement.INSTANCE, box);
    breakState.setSuspendItem(box.getInstanceId());
    return false;
  }

  protected void finishInlineLevelBox(final RenderBox box)
  {
    if (breakState.isActive() == false)
    {
      return;
    }
    if (breakState.getSuspendItem() == box.getInstanceId())
    {
      // stop being suspended.
      breakState.setSuspendItem(null);
      return;
    }

    if ((box.getNodeType() & LayoutNodeTypes.MASK_BOX_INLINE) == LayoutNodeTypes.MASK_BOX_INLINE)
    {
      breakState.add(EndSequenceElement.INSTANCE, box);
      return;
    }

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

    if (suspender != null)
    {
      return;
    }

    if (box.getNodeType() == LayoutNodeTypes.TYPE_BOX_PARAGRAPH)
    {
      throw new IllegalStateException("This cannot be: Why is there a paragrah inside a inline-box?");
    }

  }

  protected void processInlineLevelNode(final RenderNode node)
  {
    if (breakState.isActive() == false || breakState.isSuspended())
    {
      return;
    }

    if (node.getNodeType() == LayoutNodeTypes.TYPE_NODE_FINISHEDNODE)
    {
      final FinishedRenderNode finNode = (FinishedRenderNode) node;
      node.setCachedWidth(finNode.getLayoutedWidth());
      return;
    }

    if (node.getNodeType() == LayoutNodeTypes.TYPE_NODE_TEXT)
    {
      breakState.add(TextSequenceElement.INSTANCE, node);
    }
    else if (node.getNodeType() == LayoutNodeTypes.TYPE_NODE_SPACER)
    {
      final StyleSheet styleSheet = node.getStyleSheet();
      if (WhitespaceCollapse.PRESERVE.equals(styleSheet.getStyleProperty(TextStyleKeys.WHITE_SPACE_COLLAPSE)) &&
          styleSheet.getBooleanStyleProperty(TextStyleKeys.TRIM_TEXT_CONTENT) == false)
      {
        breakState.add(SpacerSequenceElement.INSTANCE, node);
      }
      else if (breakState.isContainsContent())
      {
        breakState.add(SpacerSequenceElement.INSTANCE, node);
      }
    }
    else
    {
      breakState.add(InlineNodeSequenceElement.INSTANCE, node);
    }
  }

  protected void processBlockLevelNode(final RenderNode node)
  {
    // This could be anything, text, or an image.
    node.setCachedX(computeBlockPosition(node));
    if (node.getNodeType() == LayoutNodeTypes.TYPE_NODE_FINISHEDNODE)
    {
      final FinishedRenderNode finNode = (FinishedRenderNode) node;
      node.setCachedWidth(finNode.getLayoutedWidth());
    }
    else
    {
      computeNodeWidth(node);
    }
  }

  protected void processCanvasLevelNode(final RenderNode node)
  {
    // next, compute the width ...
    node.setCachedX(computeCanvasPosition(node));

    if (node.getNodeType() == LayoutNodeTypes.TYPE_NODE_FINISHEDNODE)
    {
      final FinishedRenderNode finNode = (FinishedRenderNode) node;
      node.setCachedWidth(finNode.getLayoutedWidth());
    }
    else
    {
      computeNodeWidth(node);
    }
  }

  protected void processParagraphChilds(final ParagraphRenderBox box)
  {
    if (box.isComplexParagraph())
    {
      final RenderBox lineboxContainer = box.getLineboxContainer();
      RenderNode node = lineboxContainer.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
        if (node.getNodeType() != LayoutNodeTypes.TYPE_BOX_LINEBOX)
        {
          throw new IllegalStateException("Expected ParagraphPoolBox elements.");
        }

        final ParagraphPoolBox inlineRenderBox = (ParagraphPoolBox) node;
        if (startLine(inlineRenderBox))
        {
          processBoxChilds(inlineRenderBox);
          finishLine(inlineRenderBox);
        }

        node = node.getNext();
      }
    }
    else
    {
      final ParagraphPoolBox node = box.getPool();
      // 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
      if (startLine(node))
      {
        processBoxChilds(node);
        finishLine(node);
      }
    }
  }

  private boolean startLine(final RenderBox inlineRenderBox)
  {
    if (breakState.isActive() == false)
    {
      return false;
    }

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

    breakState.clear();
    breakState.add(StartSequenceElement.INSTANCE, inlineRenderBox);
    return true;
  }

  private void finishLine(final RenderBox inlineRenderBox)
  {
    if (breakState.isActive() == false || breakState.isSuspended())
    {
      throw new IllegalStateException("No active breakstate, finish-line cannot continue.");
    }

    breakState.add(EndSequenceElement.INSTANCE, inlineRenderBox);

    final ParagraphRenderBox paragraph = breakState.getParagraph();

    final ElementAlignment textAlignment = paragraph.getTextAlignment();
    final long textIndent = paragraph.getTextIndent();
    final long firstLineIndent = paragraph.getFirstLineIndent();
    // 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 TextAlignmentProcessor processor = create(textAlignment);

    final SequenceList sequence = breakState.getSequence();

    final long x2;
    final boolean overflowX = paragraph.getStaticBoxLayoutProperties().isOverflowX();
    if (overflowX)
    {
      x2 = Integer.MAX_VALUE;
    }
    else
    {
      x2 = paragraph.getContentAreaX2();
    }
    final long x1 = paragraph.getContentAreaX1();
    final long lineStart = Math.min(x2, x1 + firstLineIndent);
    final long lineEnd = x2;
    if (lineEnd - lineStart <= 0)
    {
      final long minimumChunkWidth = paragraph.getPool().getMinimumChunkWidth();
      processor.initialize(metaData, sequence, lineStart, lineStart + minimumChunkWidth, pageGrid, overflowX);
      InfiniteMinorAxisLayoutStep.logger.warn("Auto-Corrected zero-width first-line on paragraph " + paragraph.getName());
    }
    else
    {
      processor.initialize(metaData, sequence, lineStart, lineEnd, pageGrid, overflowX);
    }

    while (processor.hasNext())
    {
      final RenderNode linebox = processor.next();
      if (linebox.getNodeType() != LayoutNodeTypes.TYPE_BOX_LINEBOX)
      {
        throw new IllegalStateException("Line must not be null");
      }

      paragraph.addGeneratedChild(linebox);

      if (processor.hasNext())
      {
        final long innerLineStart = Math.min(x2, x1 + textIndent);
        final long innerLineEnd = x2;
        if (innerLineEnd - innerLineStart <= 0)
        {
          final long minimumChunkWidth = paragraph.getPool().getMinimumChunkWidth();
          processor.updateLineSize(innerLineStart, innerLineStart + minimumChunkWidth);
          InfiniteMinorAxisLayoutStep.logger.warn("Auto-Corrected zero-width text-line on paragraph " + paragraph.getName());
        }
        else
        {
          processor.updateLineSize(innerLineStart, innerLineEnd);
        }
      }
    }

    processor.deinitialize();
  }

  /**
   * Reuse the processors ..
   *
   * @param alignment
   * @return
   */
  private TextAlignmentProcessor create(final ElementAlignment alignment)
  {
    if (ElementAlignment.CENTER.equals(alignment))
    {
      if (centerProcessor == null)
      {
        centerProcessor = new CenterAlignmentProcessor();
      }
      return centerProcessor;
    }
    else if (ElementAlignment.RIGHT.equals(alignment))
    {
      if (rightProcessor == null)
      {
        rightProcessor = new RightAlignmentProcessor();
      }
      return rightProcessor;
    }

    if (leftProcessor == null)
    {
      leftProcessor = new LeftAlignmentProcessor();
    }
    return leftProcessor;
  }

  private long computeBlockPosition(final RenderNode node)
  {
    final RenderBox parent = node.getParent();
    if (parent == null)
    {
      return 0;
    }
    return parent.getContentAreaX1();
  }

  private long computeCanvasPosition(final RenderNode node)
  {
    final RenderBox parent = node.getParent();
    if (parent == null)
    {
      return 0;
    }

    final long contentAreaX1 = parent.getContentAreaX1();
    final long bcw = ProcessUtility.computeBlockContextWidth(node);
    final double posX = node.getNodeLayoutProperties().getPosX();
    final long position = RenderLength.resolveLength(bcw, posX);
    return (contentAreaX1 + position);
  }

  protected boolean startCanvasLevelBox(final RenderBox box)
  {
    // first, compute the position. The position is global, not relative to a
    // parent or so. Therefore a child has no connection to the parent's
    // effective position, when it is painted.

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

      if (box != continuedElement)
      {
        computeContentArea(box, computeCanvasPosition(box));
      }

      if (box.getNodeType() == LayoutNodeTypes.TYPE_BOX_PARAGRAPH)
      {
        final ParagraphRenderBox paragraphBox = (ParagraphRenderBox) box;
        if (continuedElement == null)
        {
          final long lineBoxChangeTracker = paragraphBox.getEffectiveLineboxContainer().getChangeTracker();
          final boolean unchanged = lineBoxChangeTracker == paragraphBox.getMinorLayoutAge();
          if (unchanged)
          {
            return false;
          }
        }

        paragraphBox.clearLayout();
        breakState.init(paragraphBox);
      }
      return true;
    }

    if (breakState.isSuspended() == false)
    {
      // The break-state exists only while we are inside of an paragraph
      // and suspend can only happen on inline elements.
      // A block-element inside a paragraph cannot be (and if it does, it is
      // a bug)
      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 ..
    return false;
  }

  protected void finishCanvasLevelBox(final RenderBox box)
  {
    if (box.isCacheValid())
    {
      return;
    }

    // make sure that the width takes all the borders and paddings into account.
    applyStrictComputedWidth(box);

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

      if (box.getNodeType() == LayoutNodeTypes.TYPE_BOX_PARAGRAPH)
      {
        // finally update the change tracker ..
        final ParagraphRenderBox paraBox = (ParagraphRenderBox) box;
        paraBox.setMinorLayoutAge(paraBox.getEffectiveLineboxContainer().getChangeTracker());

        breakState.deinit();
      }
    }
  }

  protected boolean startRowLevelBox(final RenderBox box)
  {
    if (breakState.isActive() == false)
    {
      if (box.isCacheValid())
      {
        return false;
      }

      if (box != continuedElement)
      {
        computeContentArea(box, computeRowPosition(box));
      }

      if (box.getNodeType() == LayoutNodeTypes.TYPE_BOX_PARAGRAPH)
      {
        final ParagraphRenderBox paragraphBox = (ParagraphRenderBox) box;
        if (continuedElement == null)
        {
          final long lineBoxChangeTracker = paragraphBox.getEffectiveLineboxContainer().getChangeTracker();
          final boolean unchanged = lineBoxChangeTracker == paragraphBox.getMinorLayoutAge();
          if (unchanged)
          {
            return false;
          }
        }

        paragraphBox.clearLayout();
        breakState.init(paragraphBox);
      }
      return true;
    }

    if (breakState.isSuspended() == false)
    {
      // The break-state exists only while we are inside of an paragraph
      // and suspend can only happen on inline elements.
      // A block-element inside a paragraph cannot be (and if it does, it is
      // a bug)
      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 ..
    return false;
  }

  protected void processRowLevelNode(final RenderNode node)
  {

    // This could be anything, text, or an image.
    node.setCachedX(computeRowPosition(node));

    if (node.getNodeType() == LayoutNodeTypes.TYPE_NODE_FINISHEDNODE)
    {
      final FinishedRenderNode finNode = (FinishedRenderNode) node;
      node.setCachedWidth(finNode.getLayoutedWidth());
    }
    else
    {
      computeNodeWidth(node);
    }
  }

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

    applyComputedWidth(box);

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

      if (box.getNodeType() == LayoutNodeTypes.TYPE_BOX_PARAGRAPH)
      {
        // finally update the change tracker ..
        final ParagraphRenderBox paraBox = (ParagraphRenderBox) box;
        paraBox.setMinorLayoutAge(paraBox.getEffectiveLineboxContainer().getChangeTracker());
        breakState.deinit();
      }
    }
  }


  private long computeRowPosition(final RenderNode node)
  {
    // we have no margins yet ..
    final long marginLeft = 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)
      {
        // we have a silbling. Position yourself directly below your silbling ..
        return (marginLeft + prev.getCachedX() + prev.getCachedWidth());
      }
      else
      {
        final StaticBoxLayoutProperties blp = parent.getStaticBoxLayoutProperties();
        final BoxDefinition bdef = parent.getBoxDefinition();
        final long insetLeft = (blp.getBorderLeft() + bdef.getPaddingLeft());

        return (marginLeft + insetLeft + parent.getCachedX());
      }
    }
    else
    {
      // there's no parent ..
      return (marginLeft);
    }
  }
}
TOP

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

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.