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

Source Code of org.pentaho.reporting.engine.classic.core.layout.AbstractRenderer

/*
* 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;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.pentaho.reporting.engine.classic.core.Band;
import org.pentaho.reporting.engine.classic.core.Group;
import org.pentaho.reporting.engine.classic.core.GroupBody;
import org.pentaho.reporting.engine.classic.core.InvalidReportStateException;
import org.pentaho.reporting.engine.classic.core.ReportAttributeMap;
import org.pentaho.reporting.engine.classic.core.ReportDefinition;
import org.pentaho.reporting.engine.classic.core.ReportProcessingException;
import org.pentaho.reporting.engine.classic.core.filter.types.AutoLayoutBoxType;
import org.pentaho.reporting.engine.classic.core.filter.types.bands.SubReportType;
import org.pentaho.reporting.engine.classic.core.function.ExpressionRuntime;
import org.pentaho.reporting.engine.classic.core.layout.model.BlockRenderBox;
import org.pentaho.reporting.engine.classic.core.layout.model.BreakMarkerRenderBox;
import org.pentaho.reporting.engine.classic.core.layout.model.InlineProgressMarkerRenderBox;
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.ProgressMarkerRenderBox;
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.SectionRenderBox;
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.BoxDefinitionFactory;
import org.pentaho.reporting.engine.classic.core.layout.output.ContentProcessingException;
import org.pentaho.reporting.engine.classic.core.layout.output.LayoutPagebreakHandler;
import org.pentaho.reporting.engine.classic.core.layout.output.OutputProcessor;
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.output.ValidateSafeToStoreStateStep;
import org.pentaho.reporting.engine.classic.core.layout.process.ApplyAutoCommitStep;
import org.pentaho.reporting.engine.classic.core.layout.process.ApplyCachedValuesStep;
import org.pentaho.reporting.engine.classic.core.layout.process.ApplyCommitStep;
import org.pentaho.reporting.engine.classic.core.layout.process.CanvasMajorAxisLayoutStep;
import org.pentaho.reporting.engine.classic.core.layout.process.CommitStep;
import org.pentaho.reporting.engine.classic.core.layout.process.ComputeStaticPropertiesProcessStep;
import org.pentaho.reporting.engine.classic.core.layout.process.InfiniteMajorAxisLayoutStep;
import org.pentaho.reporting.engine.classic.core.layout.process.InfiniteMinorAxisLayoutStep;
import org.pentaho.reporting.engine.classic.core.layout.process.ParagraphLineBreakStep;
import org.pentaho.reporting.engine.classic.core.layout.process.RevalidateAllAxisLayoutStep;
import org.pentaho.reporting.engine.classic.core.layout.process.RollbackStep;
import org.pentaho.reporting.engine.classic.core.layout.process.ValidateModelStep;
import org.pentaho.reporting.engine.classic.core.layout.style.ManualBreakIndicatorStyleSheet;
import org.pentaho.reporting.engine.classic.core.layout.style.SectionKeepTogetherStyleSheet;
import org.pentaho.reporting.engine.classic.core.layout.style.SimpleStyleSheet;
import org.pentaho.reporting.engine.classic.core.states.ReportStateKey;
import org.pentaho.reporting.engine.classic.core.style.BandDefaultStyleSheet;
import org.pentaho.reporting.engine.classic.core.style.ElementDefaultStyleSheet;
import org.pentaho.reporting.engine.classic.core.style.ElementStyleKeys;
import org.pentaho.reporting.engine.classic.core.style.StyleSheet;
import org.pentaho.reporting.engine.classic.core.util.InstanceID;
import org.pentaho.reporting.libraries.base.util.FastStack;

/**
* The LayoutSystem is a simplified version of the LibLayout-rendering system.
*
* @author Thomas Morgner
*/
public abstract class AbstractRenderer implements Renderer
{
  private static final Log logger = LogFactory.getLog(AbstractRenderer.class);

  private static class Section
  {
    private int type;
    private SectionRenderBox sectionBox;

    protected Section(final int type, final SectionRenderBox sectionBox)
    {
      this.type = type;
      this.sectionBox = sectionBox;
    }

    public int getType()
    {
      return type;
    }

    public SectionRenderBox getSectionBox()
    {
      return sectionBox;
    }
  }

  private static class GroupSection
  {
    private static final double COMMON_GROWTH = 0.5;
    private static final int INITIAL_COMMON_SIZE = 50;
    private static final int MAXIMUM_COMMON_SIZE = 5000;

    private RenderBox addBox;
    private RenderBox groupBox;
    private int childCount;
    private int nextBoxStart;
    private StyleSheet styleSheet;

    protected GroupSection(final RenderBox groupBox,
                           final StyleSheet styleSheet)
    {
      if (groupBox == null)
      {
        throw new NullPointerException();
      }
      this.styleSheet = styleSheet;
      this.groupBox = groupBox;
      this.childCount = 0;
      this.nextBoxStart = GroupSection.INITIAL_COMMON_SIZE;
      this.addBox = groupBox;
    }

    protected GroupSection(final RenderBox groupBox,
                           final RenderBox addBox,
                           final int childCount,
                           final int nextBoxStart,
                           final StyleSheet styleSheet)
    {
      if (groupBox == null)
      {
        throw new NullPointerException();
      }
      this.groupBox = groupBox;
      this.addBox = addBox;
      this.childCount = childCount;
      this.nextBoxStart = nextBoxStart;
      this.styleSheet = styleSheet;
    }


    public RenderBox getAddBox()
    {
      return addBox;
    }

    public RenderBox getGroupBox()
    {
      return groupBox;
    }

    public boolean mergeSection(final ReportStateKey stateKey)
    {
      final RenderNode lastSection = addBox.getLastChild();
      if (lastSection == null)
      {
        return false;
      }
      if ((lastSection.getNodeType() & LayoutNodeTypes.MASK_BOX) != LayoutNodeTypes.MASK_BOX)
      {
        return false;
      }

      final RenderBox lastSectionBox = (RenderBox) lastSection;
      final RenderNode maybeMarker = lastSectionBox.getLastChild();
      if (maybeMarker == null)
      {
        return false;
      }
      final int nodeType = maybeMarker.getNodeType();
      if (nodeType == LayoutNodeTypes.TYPE_BOX_INLINE_PROGRESS_MARKER)
      {
        final InlineProgressMarkerRenderBox markerRenderBox = (InlineProgressMarkerRenderBox) maybeMarker;
        markerRenderBox.setStateKey(stateKey);
        return true;
      }
      else if (nodeType == LayoutNodeTypes.TYPE_BOX_PROGRESS_MARKER)
      {
        final ProgressMarkerRenderBox markerRenderBox = (ProgressMarkerRenderBox) maybeMarker;
        markerRenderBox.setStateKey(stateKey);
        return true;
      }
      return false;
    }

    public void addedSection(final RenderNode node)
    {
      childCount += 1;
      if (childCount == nextBoxStart)
      {
        if (addBox != groupBox)
        {
          addBox.close();
        }
        final BlockRenderBox commonBox = new BlockRenderBox
            (styleSheet, new InstanceID(), BoxDefinition.EMPTY, AutoLayoutBoxType.INSTANCE,
                ReportAttributeMap.EMPTY_MAP, null);
        commonBox.setName("Common-Section");
        groupBox.addChild(commonBox);
        addBox = commonBox;

        nextBoxStart += (int) Math.min(MAXIMUM_COMMON_SIZE, nextBoxStart * GroupSection.COMMON_GROWTH);
      }
      addBox.addChild(node);
    }

    public void close()
    {
      if (addBox != groupBox)
      {
        addBox.close();
      }
      groupBox.close();
    }

    public int getChildCount()
    {
      return childCount;
    }


    public int getNextBoxStart()
    {
      return nextBoxStart;
    }

    public StyleSheet getStyleSheet()
    {
      return styleSheet;
    }
  }

  private static class IgnoredContentIndicator
  {
    private IgnoredContentIndicator()
    {
    }
  }

  private LogicalPageBox pageBox;
  private DefaultLayoutBuilder normalFlowLayoutBuilder;
  private LayoutBuilder watermarkLayoutBuilder;
  private LayoutBuilder headerLayoutBuilder;
  private LayoutBuilder footerLayoutBuilder;

  private ValidateModelStep validateModelStep;
  private ComputeStaticPropertiesProcessStep staticPropertiesStep;
  private ParagraphLineBreakStep paragraphLineBreakStep;
  private InfiniteMinorAxisLayoutStep minorAxisLayoutStep;
  private InfiniteMajorAxisLayoutStep majorAxisLayoutStep;
  private CanvasMajorAxisLayoutStep canvasMajorAxisLayoutStep;
  private RevalidateAllAxisLayoutStep revalidateAllAxisLayoutStep;

  private ValidateSafeToStoreStateStep validateSafeToStoreStateStep;

  private CommitStep commitStep;
  private ApplyCommitStep applyCommitStep;
  private RollbackStep rollbackStep;
  private ApplyAutoCommitStep applyAutoCommitStep;

  private OutputProcessorMetaData metaData;
  private OutputProcessor outputProcessor;
  private Section section;
  private int pagebreaks;
  private boolean dirty;
  private ReportStateKey lastStateKey;
  private ApplyCachedValuesStep applyCachedValuesStep;
  private SimpleStyleSheet manualBreakBoxStyle;
  private StyleCache sectionStyleCache;

  private boolean readOnly;
  private FastStack groupStack;
  private Object stateKey;
  private boolean paranoidChecks;
  private BoxDefinitionFactory boxDefinitionFactory;
  private BoxDefinition watermarkBoxDefinition;

  private SimpleStyleSheet bandWithoutKeepTogetherStyle;
  private SimpleStyleSheet bandWithKeepTogetherStyle;
  private static final InlineSubreportMarker[] EMPTY_ARRAY = new InlineSubreportMarker[0];

  private SectionRenderBox[] sectionBoxes;
  private LayoutResult lastValidateResult;

  protected AbstractRenderer(final OutputProcessor outputProcessor)
  {
    final BoxDefinition boxDefinition = new BoxDefinition();
    boxDefinition.setPreferredHeight(new RenderLength(100000, true));
    this.watermarkBoxDefinition = boxDefinition.lock();

    this.outputProcessor = outputProcessor;
    this.metaData = outputProcessor.getMetaData();
    this.normalFlowLayoutBuilder = createNormalFlowLayoutBuilder(metaData);
    this.headerLayoutBuilder = new SlottedLayoutBuilder(metaData, false);
    this.watermarkLayoutBuilder = new SlottedLayoutBuilder(metaData, true);
    this.footerLayoutBuilder = new SlottedLayoutBuilder(metaData, true);
    this.paranoidChecks = "true".equals
        (metaData.getConfiguration().getConfigProperty
            ("org.pentaho.reporting.engine.classic.core.layout.ParanoidChecks"));
    this.validateModelStep = new ValidateModelStep();
    this.staticPropertiesStep = new ComputeStaticPropertiesProcessStep(metaData);
    this.paragraphLineBreakStep = new ParagraphLineBreakStep();
    this.minorAxisLayoutStep = new InfiniteMinorAxisLayoutStep(metaData);
    this.majorAxisLayoutStep = new InfiniteMajorAxisLayoutStep();
    this.canvasMajorAxisLayoutStep = new CanvasMajorAxisLayoutStep();
    this.revalidateAllAxisLayoutStep = new RevalidateAllAxisLayoutStep(metaData);
    this.validateSafeToStoreStateStep = new ValidateSafeToStoreStateStep();
    this.applyCachedValuesStep = new ApplyCachedValuesStep();
    this.commitStep = new CommitStep();
    this.applyAutoCommitStep = new ApplyAutoCommitStep();
    this.applyCommitStep = new ApplyCommitStep();
    this.rollbackStep = new RollbackStep();
    this.sectionBoxes = new SectionRenderBox[5];

    this.groupStack = new FastStack(50);

    bandWithKeepTogetherStyle = new SimpleStyleSheet(new SectionKeepTogetherStyleSheet(true));
    bandWithoutKeepTogetherStyle = new SimpleStyleSheet(new SectionKeepTogetherStyleSheet(false));

    final boolean paddingsDisabled = metaData.isFeatureSupported(OutputProcessorFeature.DISABLE_PADDING);
    this.sectionStyleCache = new StyleCache(paddingsDisabled);
    this.boxDefinitionFactory = new BoxDefinitionFactory();
  }

  public boolean isSafeToStore()
  {
    if (pageBox == null)
    {
      return true;
    }
    return validateSafeToStoreStateStep.isSafeToStore(pageBox);
  }

  protected DefaultLayoutBuilder createNormalFlowLayoutBuilder(final OutputProcessorMetaData metaData)
  {
    return new DefaultLayoutBuilder(metaData);
  }

  protected OutputProcessorMetaData getMetaData()
  {
    return metaData;
  }

  public Object getStateKey()
  {
    return stateKey;
  }

  public void setStateKey(final Object stateKey)
  {
    this.stateKey = stateKey;
  }

  public OutputProcessor getOutputProcessor()
  {
    return outputProcessor;
  }

  public void startReport(final ReportDefinition report)
  {
    if (report == null)
    {
      throw new NullPointerException();
    }

    if (readOnly)
    {
      throw new IllegalStateException();
    }

    final SimpleStyleSheet reportStyle = sectionStyleCache.getStyleSheet(report.getStyle());
    final BoxDefinition boxDefinition = boxDefinitionFactory.getBoxDefinition(reportStyle);
    this.pageBox = new LogicalPageBox(report, reportStyle, boxDefinition);

    if (reportStyle.getBooleanStyleProperty(ElementStyleKeys.AVOID_PAGEBREAK_INSIDE))
    {
      this.groupStack.push(new GroupSection(pageBox.getContentArea(), bandWithKeepTogetherStyle));
    }
    else
    {
      this.groupStack.push(new GroupSection(pageBox.getContentArea(), bandWithoutKeepTogetherStyle));
    }
    markDirty();
  }

  public void startSubReport(final ReportDefinition report, final InstanceID insertationPoint)
  {
    if (readOnly)
    {
      throw new IllegalStateException("Renderer is marked read-only");
    }

    if (isIgnoreContent())
    {
      groupStack.push(new IgnoredContentIndicator());
      return;
    }

    final StyleSheet styleSheet = ElementDefaultStyleSheet.getDefaultStyle();

    final RenderBox box;
    if (insertationPoint == null)
    {
      final SimpleStyleSheet reportStyle = sectionStyleCache.getStyleSheet(styleSheet);
      final BoxDefinition boxDefinition = boxDefinitionFactory.getBoxDefinition(reportStyle);
      box = new BlockRenderBox
          (reportStyle, report.getObjectID(), boxDefinition, SubReportType.INSTANCE, report.getAttributes(), null);
      if (report.getName() != null)
      {
        box.setName("SubReport-Section-" + groupStack.size() + ": name=" + report.getName());
      }
      else
      {
        box.setName("SubReport-Section-" + groupStack.size());
      }

      box.getStaticBoxLayoutProperties().setPlaceholderBox(true);
      addBox(box);
    }
    else
    {
      final RenderNode maybeBox = pageBox.findNodeById(insertationPoint);
      if (maybeBox == null || (maybeBox.getNodeType() & LayoutNodeTypes.MASK_BOX) != LayoutNodeTypes.MASK_BOX)
      {
        box = null;
      }
      else
      {
        box = (RenderBox) maybeBox;
      }
    }

    if (box == null)
    {
      this.groupStack.push(new IgnoredContentIndicator());
    }
    else if (styleSheet.getBooleanStyleProperty(ElementStyleKeys.AVOID_PAGEBREAK_INSIDE))
    {
      this.groupStack.push(new GroupSection(box, bandWithKeepTogetherStyle));
    }
    else
    {
      this.groupStack.push(new GroupSection(box, bandWithoutKeepTogetherStyle));
    }
  }

  public void startGroup(final Group group)
  {
    if (readOnly)
    {
      throw new IllegalStateException();
    }

    if (isIgnoreContent())
    {
      groupStack.push(new IgnoredContentIndicator());
      return;
    }

    final SimpleStyleSheet reportStyle = sectionStyleCache.getStyleSheet(group.getStyle());
    final BoxDefinition boxDefinition = boxDefinitionFactory.getBoxDefinition(reportStyle);
    final BlockRenderBox groupBox = new BlockRenderBox
        (reportStyle, group.getObjectID(), boxDefinition, group.getElementType(), group.getAttributes(), null);

    groupBox.getStaticBoxLayoutProperties().setPlaceholderBox(true);
    groupBox.setName(group.getElementTypeName() + "-Section-" + groupStack.size());
    addBox(groupBox);
    if (reportStyle.getBooleanStyleProperty(ElementStyleKeys.AVOID_PAGEBREAK_INSIDE))
    {
      this.groupStack.push(new GroupSection(groupBox, bandWithKeepTogetherStyle));
    }
    else
    {
      this.groupStack.push(new GroupSection(groupBox, bandWithoutKeepTogetherStyle));
    }
  }

  public void startGroupBody(final GroupBody groupBody)
  {
    if (isIgnoreContent())
    {
      groupStack.push(new IgnoredContentIndicator());
      return;
    }

    final SimpleStyleSheet reportStyle = sectionStyleCache.getStyleSheet(groupBody.getStyle());
    final BoxDefinition boxDefinition = boxDefinitionFactory.getBoxDefinition(reportStyle);
    final BlockRenderBox groupBox = new BlockRenderBox(reportStyle, groupBody.getObjectID(),
        boxDefinition, groupBody.getElementType(), groupBody.getAttributes(), null);

    // todo: PRD-3154: This is black magic, placeholder box true is evil.
    // Need to evaluate side-effects of this beast. Is it safe for keep-together boxes?
    groupBox.getStaticBoxLayoutProperties().setPlaceholderBox(true);
    groupBox.setName("Group-body-" + groupStack.size());
    addBox(groupBox);
    if (reportStyle.getBooleanStyleProperty(ElementStyleKeys.AVOID_PAGEBREAK_INSIDE))
    {
      this.groupStack.push(new GroupSection(groupBox, bandWithKeepTogetherStyle));
    }
    else
    {
      this.groupStack.push(new GroupSection(groupBox, bandWithoutKeepTogetherStyle));
    }
    markDirty();
  }

  private void addBox(final RenderNode node)
  {
    final GroupSection groupSection = (GroupSection) groupStack.peek();
    groupSection.addedSection(node);
  }

  private boolean mergeSection(final RenderNode node)
  {
    final GroupSection groupSection = (GroupSection) groupStack.peek();
    return groupSection.mergeSection(node.getStateKey());
  }

  private SectionRenderBox createSectionBox(final int type, final BoxDefinition boxDefinition)
  {
    if (type == Renderer.TYPE_NORMALFLOW && sectionBoxes[type] != null)
    {
      final SectionRenderBox renderBox = sectionBoxes[type];
      sectionBoxes[type] = null;
      return renderBox;
    }

    return new SectionRenderBox
        (bandWithoutKeepTogetherStyle, new InstanceID(), boxDefinition, AutoLayoutBoxType.INSTANCE,
            ReportAttributeMap.EMPTY_MAP, null);
  }

  public void startSection(final int type)
  {
    if (readOnly)
    {
      throw new IllegalStateException();
    }

    if (isIgnoreContent())
    {
      return;
    }

    final SectionRenderBox sectionBox;
    // todo: The group together should be cleanly inherited from the direct parent..
    if (type == Renderer.TYPE_WATERMARK)
    {
      pageBox.getFooterArea().clear();
      watermarkLayoutBuilder.startSection(pageBox.getWatermarkArea(), true);

      sectionBox = createSectionBox(type, watermarkBoxDefinition);
      sectionBox.setName("Watermark-Section");
      this.section = new Section(type, sectionBox);
    }
    else if (type == Renderer.TYPE_HEADER)
    {
      // when a header starts, we have to clear the page-footer too (to get rid of all the slotted bands).
      pageBox.getFooterArea().clear();
      headerLayoutBuilder.startSection(pageBox.getHeaderArea(), true);

      sectionBox = createSectionBox(type, BoxDefinition.EMPTY);
      sectionBox.setName("Header-" + type);
      this.section = new Section(type, sectionBox);
    }
    else if (type == Renderer.TYPE_FOOTER)
    {
      footerLayoutBuilder.startSection(pageBox.getFooterArea(), true);

      sectionBox = createSectionBox(type, BoxDefinition.EMPTY);
      sectionBox.setName("Footer-" + type);
      this.section = new Section(type, sectionBox);
    }
    else
    {
      normalFlowLayoutBuilder.startSection(pageBox, false);

      sectionBox = createSectionBox(TYPE_NORMALFLOW, BoxDefinition.EMPTY);
      sectionBox.setName("Section-" + type);
      this.section = new Section(type, sectionBox);
    }

  }

  public InlineSubreportMarker[] endSection()
  {
    if (readOnly)
    {
      throw new IllegalStateException();
    }

    if (isIgnoreContent())
    {
      return EMPTY_ARRAY;
    }

    final Section section = this.section;
    this.section = null;
    final SectionRenderBox sectionBox = section.getSectionBox();
    sectionBox.close();

    final int sectionType = section.getType();
    switch (sectionType)
    {
      case Renderer.TYPE_NORMALFLOW:
      {
        final RenderNode firstChild = sectionBox.getFirstChild();
        if (firstChild == null)
        {
          // the whole section is empty; therefore we can ignore it.
          sectionBox.makeReusable();
          sectionBoxes[sectionType] = sectionBox;
          return normalFlowLayoutBuilder.endSection(getPageBox(), sectionBox);
        }

        final int type = firstChild.getNodeType();
        if (sectionBox.getLastChild() == firstChild &&
            type == LayoutNodeTypes.TYPE_BOX_INLINE_PROGRESS_MARKER ||
            type == LayoutNodeTypes.TYPE_BOX_PROGRESS_MARKER)
        {
          if (mergeSection(sectionBox))
          {
            sectionBox.makeReusable();
            sectionBoxes[sectionType] = sectionBox;
            return normalFlowLayoutBuilder.endSection(getPageBox(), sectionBox);
          }
        }

        addBox(sectionBox);
        markDirty();
        return normalFlowLayoutBuilder.endSection(getPageBox(), sectionBox);
      }
      case Renderer.TYPE_FOOTER:
      {
        final BlockRenderBox footerArea = pageBox.getFooterArea();
        if (sectionBox.getFirstChild() == sectionBox.getLastChild() &&
            isEmptyOrMarker(footerArea.getFirstChild()) &&
            isEmptyOrMarker(sectionBox.getFirstChild()))
        {
          // both boxes are empty, so we can ignore it ...
          return footerLayoutBuilder.endSection(footerArea, sectionBox);
        }

        markDirty();
        return footerLayoutBuilder.endSection(footerArea, sectionBox);
      }
      case Renderer.TYPE_HEADER:
      {
        final BlockRenderBox headerArea = pageBox.getHeaderArea();
        if (sectionBox.getFirstChild() == sectionBox.getLastChild() &&
            isEmptyOrMarker(headerArea.getFirstChild()) &&
            isEmptyOrMarker(sectionBox.getFirstChild()))
        {
          // both boxes are empty, so we can ignore it ...
          return headerLayoutBuilder.endSection(headerArea, sectionBox);
        }
        markDirty();
        return headerLayoutBuilder.endSection(headerArea, sectionBox);
      }
      case Renderer.TYPE_WATERMARK:
      {
        // ignore for now.
        final WatermarkAreaBox watermarkArea = pageBox.getWatermarkArea();
        if (sectionBox.getFirstChild() == sectionBox.getLastChild() &&
            isEmptyOrMarker(watermarkArea.getFirstChild()) &&
            isEmptyOrMarker(sectionBox.getFirstChild()))
        {
          // both boxes are empty, so we can ignore it ...
          return watermarkLayoutBuilder.endSection(watermarkArea, sectionBox);
        }
        markDirty();
        return watermarkLayoutBuilder.endSection(watermarkArea, sectionBox);
      }
      default:
        throw new IllegalStateException("Type " + sectionType + " not recognized");
    }
  }

  private boolean isEmptyOrMarker(final RenderNode box)
  {
    if (box == null)
    {
      return true;
    }
    final int type = box.getNodeType();
    if (type == LayoutNodeTypes.TYPE_BOX_INLINE_PROGRESS_MARKER ||
        type == LayoutNodeTypes.TYPE_BOX_PROGRESS_MARKER)
    {
      return true;
    }
    return false;
  }

  private boolean isIgnoreContent()
  {
    return groupStack.isEmpty() == false &&
        groupStack.peek() instanceof AbstractRenderer.IgnoredContentIndicator;
  }

  public void endGroupBody()
  {
    if (readOnly)
    {
      throw new IllegalStateException();
    }

    final Object o = groupStack.pop();
    if (o instanceof IgnoredContentIndicator)
    {
      return;
    }

    final GroupSection groupSection = (GroupSection) o;
    if (groupSection.getChildCount() == 0)
    {
      final RenderBox groupBox = groupSection.getGroupBox();
      groupBox.getParent().remove(groupBox);
    }
    groupSection.close();
  }

  public void endGroup()
  {
    if (readOnly)
    {
      throw new IllegalStateException();
    }

    final Object o = groupStack.pop();
    if (o instanceof IgnoredContentIndicator)
    {
      return;
    }
    final GroupSection groupSection = (GroupSection) o;
    groupSection.close();
  }

  protected LogicalPageBox getPageBox()
  {
    return pageBox;
  }

  public void endSubReport()
  {
    if (readOnly)
    {
      throw new IllegalStateException();
    }
    final Object o = groupStack.pop();
    if (o instanceof IgnoredContentIndicator)
    {
      return;
    }

    final GroupSection groupSection = (GroupSection) o;
    groupSection.close();
  }

  public void endReport()
  {
    if (readOnly)
    {
      throw new IllegalStateException();
    }
    final GroupSection groupSection = (GroupSection) groupStack.pop();
    groupSection.close();

    pageBox.close();
    markDirty();
  }

  public void addEmptyRootLevelBand(final ReportStateKey stateKey)
      throws ReportProcessingException
  {
    if (readOnly)
    {
      throw new IllegalStateException();
    }
    if (isIgnoreContent())
    {
      return;
    }

    final RenderBox sectionBox = section.getSectionBox();
    final int type = section.getType();
    if (type == TYPE_FOOTER)
    {
      footerLayoutBuilder.addEmptyRootLevelBand(sectionBox, stateKey);
    }
    else if (type == TYPE_HEADER)
    {
      headerLayoutBuilder.addEmptyRootLevelBand(sectionBox, stateKey);
    }
    else if (type == TYPE_WATERMARK)
    {
      watermarkLayoutBuilder.addEmptyRootLevelBand(sectionBox, stateKey);
    }
    else
    {
      normalFlowLayoutBuilder.addEmptyRootLevelBand(sectionBox, stateKey);
    }
  }

  public void add(final Band band, final ExpressionRuntime runtime, final ReportStateKey stateKey)
      throws ReportProcessingException
  {
    if (readOnly)
    {
      throw new IllegalStateException();
    }
    if (isIgnoreContent())
    {
      return;
    }

    final RenderBox sectionBox = section.getSectionBox();
    final int type = section.getType();
    if (type == TYPE_FOOTER)
    {
      footerLayoutBuilder.add(sectionBox, band, runtime, stateKey);
    }
    else if (type == TYPE_HEADER)
    {
      headerLayoutBuilder.add(sectionBox, band, runtime, stateKey);
    }
    else if (type == TYPE_WATERMARK)
    {
      watermarkLayoutBuilder.add(sectionBox, band, runtime, stateKey);
    }
    else
    {
      normalFlowLayoutBuilder.add(sectionBox, band, runtime, stateKey);
    }
  }

  public void add(final RenderBox box)
  {
    if (readOnly)
    {
      throw new IllegalStateException();
    }
    if (isIgnoreContent())
    {
      return;
    }

    if (box.isOpen())
    {
      throw new IllegalStateException();
    }
    final RenderBox sectionBox = section.getSectionBox();
    sectionBox.addChild(box);
  }

  public LayoutResult validatePages()
      throws ContentProcessingException
  {
    if (readOnly)
    {
      throw new IllegalStateException();
    }
    // Pagination time without dirty-flag: 875067
    if (pageBox == null)
    {
      // StartReport has not been called yet ..
      lastValidateResult = LayoutResult.LAYOUT_UNVALIDATABLE;
      return LayoutResult.LAYOUT_UNVALIDATABLE;
    }

    if (!dirty && lastValidateResult != null)
    {
      return lastValidateResult;
    }

    setLastStateKey(null);
    setPagebreaks(0);
    if (validateModelStep.isLayoutable(pageBox) == false)
    {
      if (logger.isDebugEnabled())
      {
        logger.debug("Content-Ref# " + pageBox.getContentRefCount());
      }
      lastValidateResult = LayoutResult.LAYOUT_UNVALIDATABLE;
      return LayoutResult.LAYOUT_UNVALIDATABLE;
    }

    // These structural processors will skip old nodes. These beasts cannot be cached otherwise.
    staticPropertiesStep.compute(pageBox);
    paragraphLineBreakStep.compute(pageBox);

    minorAxisLayoutStep.compute(pageBox);
    majorAxisLayoutStep.compute(pageBox);
    canvasMajorAxisLayoutStep.compute(pageBox);
    revalidateAllAxisLayoutStep.compute(pageBox);

    applyCachedValuesStep.compute(pageBox);
    if (isPageFinished())
    {
      lastValidateResult = LayoutResult.LAYOUT_PAGEBREAK;
      return LayoutResult.LAYOUT_PAGEBREAK;
    }
    else
    {
      lastValidateResult = LayoutResult.LAYOUT_NO_PAGEBREAK;
      return LayoutResult.LAYOUT_NO_PAGEBREAK;
    }
  }

  protected void clearDirty()
  {
    dirty = false;
  }

  protected abstract boolean isPageFinished();

  public void processIncrementalUpdate(final boolean performOutput) throws ContentProcessingException
  {
//    dirty = false;
  }

  public boolean processPage(final LayoutPagebreakHandler handler,
                             final Object commitMarker,
                             final boolean performOutput) throws ContentProcessingException
  {
    if (readOnly)
    {
      throw new IllegalStateException();
    }
    // Pagination time without dirty-flag: 875067
    if (pageBox == null)
    {
      // StartReport has not been called yet ..
//      Log.debug ("PageBox null");
      return false;
    }

    if (dirty == false)
    {
//      Log.debug ("Not dirty");
      return false;
    }

    setLastStateKey(null);
    setPagebreaks(0);
    if (validateModelStep.isLayoutable(pageBox) == false)
    {
//      Log.debug ("Not layoutable");
      return false;
    }

    // processes the current page
    boolean repeat = true;
    while (repeat)
    {
      if (handler != null)
      {
        // make sure we generate an up-to-date page-footer. This also implies that there
        // are more page-finished than page-started events generated during the report processing.
        handler.pageFinished();
      }

      if (outputProcessor.getMetaData().isFeatureSupported(OutputProcessorFeature.PAGEBREAKS))
      {
        createRollbackInformation();
        applyRollbackInformation();
        performParanoidModelCheck();
      }

      staticPropertiesStep.compute(pageBox);
      paragraphLineBreakStep.compute(pageBox);

      minorAxisLayoutStep.compute(pageBox);
      majorAxisLayoutStep.compute(pageBox);
      canvasMajorAxisLayoutStep.compute(pageBox);
      revalidateAllAxisLayoutStep.compute(pageBox);

      applyCachedValuesStep.compute(pageBox);

      repeat = performPagination(handler, performOutput);
    }
    clearDirty();
    return (pagebreaks > 0);
  }

  protected abstract boolean performPagination(LayoutPagebreakHandler handler,
                                               final boolean performOutput)
      throws ContentProcessingException;

  /**
   * A hook to allow easier debugging.
   *
   * @param pageBox the current page box.
   * @noinspection NoopMethodInAbstractClass
   */
  protected void debugPrint(final LogicalPageBox pageBox)
  {

  }

  public ReportStateKey getLastStateKey()
  {
    return lastStateKey;
  }

  public void setLastStateKey(final ReportStateKey lastStateKey)
  {
    this.lastStateKey = lastStateKey;
  }

  protected void setPagebreaks(final int pagebreaks)
  {
    this.pagebreaks = pagebreaks;
  }

  public int getPagebreaks()
  {
    return pagebreaks;
  }

  public boolean isOpen()
  {
    if (pageBox == null)
    {
      return false;
    }
    return pageBox.isOpen();
  }

  public boolean isValid()
  {
    return readOnly == false;
  }

  public Renderer deriveForStorage()
  {
    try
    {
      final AbstractRenderer renderer = (AbstractRenderer) clone();
      renderer.readOnly = false;
      renderer.sectionBoxes = new SectionRenderBox[5];
      if (pageBox != null)
      {
        renderer.pageBox = (LogicalPageBox) pageBox.derive(true);
        if (section != null)
        {
          final RenderNode nodeById = renderer.pageBox.findNodeById(section.getSectionBox().getInstanceId());
          renderer.section = new Section(section.getType(), (SectionRenderBox) nodeById);
        }
      }

      final int stackSize = groupStack.size();
      final Object[] tempList = new Object[stackSize];


      renderer.groupStack = (FastStack) groupStack.clone();
      final int tempListLength = tempList.length;
      for (int i = 0; i < tempListLength; i++)
      {
        tempList[i] = renderer.groupStack.pop();
      }

      // the stack is empty now ..
      // lets fill it again ..
      for (int i = tempListLength - 1; i >= 0; i--)
      {
        if (tempList[i] instanceof IgnoredContentIndicator)
        {
          renderer.groupStack.push(tempList[i]);
          continue;
        }

        final GroupSection section = (GroupSection) tempList[i];

        final RenderBox groupBox = section.getGroupBox();
        final InstanceID groupBoxInstanceId = groupBox.getInstanceId();
        final RenderBox groupBoxClone = (RenderBox) renderer.pageBox.findNodeById(groupBoxInstanceId);
        if (groupBoxClone == null)
        {
          throw new IllegalStateException("The pagebox did no longer contain the stored node.");
        }
        if (groupBoxClone == groupBox)
        {
          throw new IllegalStateException("Thought you wanted a groupBoxClone");
        }

        final RenderBox addBox = section.getAddBox();
        final RenderBox addBoxClone;
        if (addBox == groupBox)
        {
          addBoxClone = groupBoxClone;
        }
        else
        {
          final InstanceID addBoxInstanceId = addBox.getInstanceId();
          addBoxClone = (RenderBox) renderer.pageBox.findNodeById(addBoxInstanceId);
          if (addBoxClone == null)
          {
            throw new IllegalStateException("The pagebox did no longer contain the stored node.");
          }
          if (addBoxClone == addBox)
          {
            throw new IllegalStateException("Thought you wanted a groupBoxClone");
          }
        }
        renderer.groupStack.push(new GroupSection(groupBoxClone, addBoxClone,
            section.getChildCount(), section.getNextBoxStart(), section.getStyleSheet()));
      }
      return renderer;
    }
    catch (CloneNotSupportedException cne)
    {
      throw new InvalidReportStateException("Failed to derive Renderer", cne);
    }
  }

  public Renderer deriveForPagebreak()
  {
    try
    {
      final AbstractRenderer renderer = (AbstractRenderer) clone();
      renderer.readOnly = true;
      if (pageBox != null)
      {
        if (section != null)
        {
          renderer.section = new Section(section.getType(), section.getSectionBox());
        }
      }

      final int stackSize = groupStack.size();
      final Object[] tempList = new Object[stackSize];
      renderer.groupStack = (FastStack) groupStack.clone();
      final int tempListLength = tempList.length;
      for (int i = 0; i < tempListLength; i++)
      {
        tempList[i] = renderer.groupStack.pop();
      }

      // the stack is empty now ..
      // lets fill it again ..
      for (int i = tempListLength - 1; i >= 0; i--)
      {
        if (tempList[i] instanceof IgnoredContentIndicator)
        {
          renderer.groupStack.push(tempList[i]);
          continue;
        }

        final GroupSection section = (GroupSection) tempList[i];

        final RenderBox groupBox = section.getGroupBox();
        final RenderBox addBox = section.getAddBox();

//        validate(addBox, groupBox);
        renderer.groupStack.push(new GroupSection(groupBox, addBox,
            section.getChildCount(), section.getNextBoxStart(), section.getStyleSheet()));
      }
      return renderer;
    }
    catch (CloneNotSupportedException cne)
    {
      throw new InvalidReportStateException("Failed to derive Renderer", cne);
    }
  }

  public void performParanoidModelCheck()
  {
    if (paranoidChecks)
    {
      final int stackSize = groupStack.size();

      // the stack is empty now ..
      // lets fill it again ..
      for (int i = 0; i < stackSize; i++)
      {
        final Object o = groupStack.get(i);
        if (o instanceof IgnoredContentIndicator)
        {
          continue;
        }

        final GroupSection section = (GroupSection) o;

        final RenderBox groupBox = section.getGroupBox();
        final RenderBox addBox = section.getAddBox();

        // step 1: Check whether addbox is a child of groupbox
        RenderBox c = addBox;
        while (c != groupBox)
        {
          c = c.getParent();
          if (c == null)
          {
            throw new IllegalStateException("Failed to locate parent");
          }
        }

        c = addBox;
        while (c != null)
        {
          if (c.isOpen() == false)
          {
            throw new IllegalStateException(
                "Add-Box is not open: " + c.isMarkedOpen() + ' ' + c.isMarkedSeen() + ' ' + c);
          }
          c = c.getParent();
        }
      }
    }
  }

  public Object clone() throws CloneNotSupportedException
  {
    return super.clone();
  }

  public void addPagebreak(final ReportStateKey stateKey)
  {
    if (readOnly)
    {
      throw new IllegalStateException();
    }

    if (isIgnoreContent())
    {
      return;
    }

    if (this.manualBreakBoxStyle == null)
    {
      final ManualBreakIndicatorStyleSheet mbis =
          new ManualBreakIndicatorStyleSheet(BandDefaultStyleSheet.getBandDefaultStyle());
      this.manualBreakBoxStyle = new SimpleStyleSheet(mbis);
    }

    final RenderBox sectionBox = new BreakMarkerRenderBox
        (manualBreakBoxStyle, new InstanceID(), BoxDefinition.EMPTY, AutoLayoutBoxType.INSTANCE,
            ReportAttributeMap.EMPTY_MAP, stateKey);
    sectionBox.setName("pagebreak");
    sectionBox.close();
    addBox(sectionBox);
  }

  public boolean clearPendingPageStart(final LayoutPagebreakHandler layoutPagebreakHandler)
  {
    // intentionally left empty.
    return false;
  }

  public boolean isCurrentPageEmpty()
  {
    return false;
  }

  public boolean isPageStartPending()
  {
    return false;
  }

  public boolean isDirty()
  {
    return dirty;
  }

  public void createRollbackInformation()
  {
    if (pageBox != null)
    {
      commitStep.compute(pageBox);
    }
  }

  public void applyRollbackInformation()
  {
    if (pageBox != null)
    {
      applyCommitStep.compute(pageBox);
    }
  }

  public void validateAfterCommit()
  {
    if (paranoidChecks)
    {
      final int stackSize = groupStack.size();
      for (int i = 0; i < stackSize; i++)
      {
        final Object o = groupStack.get(i);
        if (o instanceof IgnoredContentIndicator)
        {
          continue;
        }
        final GroupSection section = (GroupSection) o;

        final RenderBox groupBox = section.getGroupBox();
        final RenderBox addBox = section.getAddBox();
        if (addBox.getParent() == null)
        {
          throw new IllegalStateException("No longer there");
        }
        if (groupBox.isMarkedSeen() == false)
        {
          throw new IllegalStateException("No seen-marker at " + groupBox);
        }
        if (addBox.isMarkedSeen() == false)
        {
          throw new IllegalStateException("No seen-marker at add-box " + addBox);
        }
        if (addBox.isMarkedOpen() == false)
        {
          throw new IllegalStateException("No open-marker at " + addBox);
        }
      }
    }
  }

  public void rollback()
  {
    readOnly = false;
    if (pageBox != null)
    {
      rollbackStep.compute(pageBox);
      validateAfterCommit();
    }
  }


  public void applyAutoCommit()
  {
    if (pageBox != null)
    {
      applyAutoCommitStep.compute(pageBox);
    }
  }

  public LayoutBuilder createBufferedLayoutBuilder()
  {
    return normalFlowLayoutBuilder.createBufferedLayoutBuilder();
  }

  public boolean isPendingPageHack()
  {
    return false;
  }

  protected void markDirty()
  {
    dirty = true;
    lastValidateResult = null;
  }

  public void print()
  {
//    ModelPrinter.print(pageBox);
  }
}
TOP

Related Classes of org.pentaho.reporting.engine.classic.core.layout.AbstractRenderer

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.