/*!
* 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) 2002-2013 Pentaho Corporation.. All rights reserved.
*/
package org.pentaho.reporting.engine.classic.core.layout.build;
import java.awt.Shape;
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.ImageContainer;
import org.pentaho.reporting.engine.classic.core.ReportElement;
import org.pentaho.reporting.engine.classic.core.SubReport;
import org.pentaho.reporting.engine.classic.core.filter.types.bands.SubReportType;
import org.pentaho.reporting.engine.classic.core.function.ProcessingContext;
import org.pentaho.reporting.engine.classic.core.layout.InlineSubreportMarker;
import org.pentaho.reporting.engine.classic.core.layout.ModelPrinter;
import org.pentaho.reporting.engine.classic.core.layout.TextProducer;
import org.pentaho.reporting.engine.classic.core.layout.model.BlockRenderBox;
import org.pentaho.reporting.engine.classic.core.layout.model.LayoutNodeTypes;
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.RenderNode;
import org.pentaho.reporting.engine.classic.core.layout.model.RenderableReplacedContentBox;
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.output.OutputProcessorFeature;
import org.pentaho.reporting.engine.classic.core.layout.output.OutputProcessorMetaData;
import org.pentaho.reporting.engine.classic.core.layout.style.DynamicHeightWrapperStyleSheet;
import org.pentaho.reporting.engine.classic.core.layout.style.DynamicReplacedContentStyleSheet;
import org.pentaho.reporting.engine.classic.core.layout.style.NonDynamicHeightWrapperStyleSheet;
import org.pentaho.reporting.engine.classic.core.layout.style.NonDynamicReplacedContentStyleSheet;
import org.pentaho.reporting.engine.classic.core.layout.style.SimpleStyleSheet;
import org.pentaho.reporting.engine.classic.core.layout.style.SubReportStyleSheet;
import org.pentaho.reporting.engine.classic.core.states.ReportStateKey;
import org.pentaho.reporting.engine.classic.core.states.process.SubReportProcessType;
import org.pentaho.reporting.engine.classic.core.style.BandStyleKeys;
import org.pentaho.reporting.engine.classic.core.style.BorderStyle;
import org.pentaho.reporting.engine.classic.core.style.ElementStyleKeys;
import org.pentaho.reporting.engine.classic.core.style.StyleKey;
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.util.InstanceID;
import org.pentaho.reporting.engine.classic.core.util.ReportDrawable;
import org.pentaho.reporting.engine.classic.core.util.ShapeDrawable;
import org.pentaho.reporting.libraries.resourceloader.factory.drawable.DrawableWrapper;
public class DefaultLayoutModelBuilder implements LayoutModelBuilder, Cloneable
{
private static final Log logger = LogFactory.getLog(DefaultLayoutModelBuilder.class);
private OutputProcessorMetaData metaData;
private ReportStateKey stateKey;
private LayoutModelBuilderContext context;
private RenderNodeFactory renderNodeFactory;
private TextProducer textProducer;
private boolean strictLegacyMode;
private boolean limitedSubReports;
private boolean collapseProgressMarker;
private ProcessingContext processingContext;
private String legacySectionName;
private boolean designtime;
public DefaultLayoutModelBuilder()
{
this("Section-0");
}
public DefaultLayoutModelBuilder(final String legacySectionName)
{
this.collapseProgressMarker = true;
this.legacySectionName = legacySectionName;
}
protected boolean isAllowMergeSection()
{
return limitedSubReports == false;
}
public void initialize(final ProcessingContext processingContext,
final RenderBox parentBox,
final RenderNodeFactory renderNodeFactory)
{
if (parentBox == null)
{
throw new NullPointerException();
}
if (processingContext == null)
{
throw new NullPointerException();
}
if (this.processingContext != processingContext)
{
this.processingContext = processingContext;
this.metaData = processingContext.getOutputProcessorMetaData();
this.strictLegacyMode = metaData.isFeatureSupported(OutputProcessorFeature.STRICT_COMPATIBILITY);
this.designtime = metaData.isFeatureSupported(OutputProcessorFeature.DESIGNTIME);
this.renderNodeFactory = renderNodeFactory;
this.textProducer = createTextProducer();
}
this.context = new DefaultLayoutModelBuilderContext(null, parentBox);
}
protected TextProducer createTextProducer()
{
return new TextProducer(metaData);
}
protected ProcessingContext getProcessingContext()
{
return processingContext;
}
public OutputProcessorMetaData getMetaData()
{
return metaData;
}
public void updateState(final ReportStateKey stateKey)
{
this.stateKey = stateKey;
}
public InstanceID startBox(final ReportElement element)
{
final StyleSheet computedStyle = element.getComputedStyle();
final String layout = (String) computedStyle.getStyleProperty(BandStyleKeys.LAYOUT, BandStyleKeys.LAYOUT_CANVAS);
return startBox(element, computedStyle, layout, false);
}
private InstanceID startBox(final ReportElement element,
final StyleSheet styleSheet,
final String layout,
final boolean auto)
{
closeAutoGeneratedPostfixBoxes();
if (BandStyleKeys.LAYOUT_AUTO.equals(layout))
{
this.context = new DefaultLayoutModelBuilderContext
(this.context, renderNodeFactory.produceRenderBox(element, styleSheet, layout, stateKey));
}
else if (BandStyleKeys.LAYOUT_INLINE.equals(layout))
{
if (this.context.getRenderBox().isAcceptInlineBoxes() == false)
{
// parent context is not a inline-inside context.
// So we need to create a auto-paragraph wrapper to open a inline-context
this.context = new DefaultLayoutModelBuilderContext
(this.context, renderNodeFactory.createAutoParagraph(element, styleSheet, stateKey));
// PRD-3750 - A empty inline-band that creates a auto-paragraph reserves space on the vertical axis.
if (metaData.isFeatureSupported(OutputProcessorFeature.STRICT_COMPATIBILITY) ||
metaData.isFeatureSupported(OutputProcessorFeature.PRD_3750))
{
this.context.setAutoGeneratedWrapperBox(true);
this.context = new DefaultLayoutModelBuilderContext(this.context,
renderNodeFactory.produceRenderBox(element, styleSheet, DefaultRenderNodeFactory.LAYOUT_PARAGRAPH_LINEBOX, stateKey));
}
}
else
{
this.context = new DefaultLayoutModelBuilderContext
(this.context, renderNodeFactory.produceRenderBox(element, styleSheet, layout, stateKey));
}
}
else if (this.context.getRenderBox().isAcceptInlineBoxes())
{
// inline elements only accept inline element childs
this.context = new DefaultLayoutModelBuilderContext
(this.context, renderNodeFactory.produceRenderBox(element, styleSheet, BandStyleKeys.LAYOUT_INLINE, stateKey));
}
else if (BandStyleKeys.LAYOUT_TABLE_CELL.equals(layout))
{
// a table body always needs a table parent ..
if (LayoutNodeTypes.TYPE_BOX_TABLE_ROW != this.context.getRenderBox().getLayoutNodeType())
{
startBox(element, renderNodeFactory.createAutoGeneratedSectionStyleSheet(styleSheet),
BandStyleKeys.LAYOUT_TABLE_ROW, true);
}
this.context = new DefaultLayoutModelBuilderContext
(this.context, renderNodeFactory.produceRenderBox(element, styleSheet, layout, stateKey));
}
else if (BandStyleKeys.LAYOUT_TABLE_ROW.equals(layout))
{
// a table body always needs a table parent ..
if (LayoutNodeTypes.TYPE_BOX_TABLE_SECTION != this.context.getRenderBox().getLayoutNodeType())
{
startBox(element, renderNodeFactory.createAutoGeneratedSectionStyleSheet(styleSheet),
BandStyleKeys.LAYOUT_TABLE_BODY, true);
}
this.context = new DefaultLayoutModelBuilderContext
(this.context, renderNodeFactory.produceRenderBox(element, styleSheet, layout, stateKey));
}
else if (BandStyleKeys.LAYOUT_TABLE_BODY.equals(layout) ||
BandStyleKeys.LAYOUT_TABLE_FOOTER.equals(layout) ||
BandStyleKeys.LAYOUT_TABLE_HEADER.equals(layout))
{
// a table body always needs a table parent ..
if (LayoutNodeTypes.TYPE_BOX_TABLE != this.context.getRenderBox().getLayoutNodeType())
{
startBox(element, renderNodeFactory.createAutoGeneratedSectionStyleSheet(styleSheet),
BandStyleKeys.LAYOUT_TABLE, true);
}
this.context = new DefaultLayoutModelBuilderContext
(this.context, renderNodeFactory.produceRenderBox(element, styleSheet, layout, stateKey));
}
else
{
// handle ordinary elements, block, canvas, row ..
this.context = new DefaultLayoutModelBuilderContext
(this.context, renderNodeFactory.produceRenderBox(element, styleSheet, layout, stateKey));
}
this.context.setAutoGeneratedWrapperBox(auto);
this.context.setEmpty(isEmptyElement(element, styleSheet, metaData));
if (!auto)
{
if (isControlBand(styleSheet))
{
this.context.getRenderBox().getStaticBoxLayoutProperties().setPlaceholderBox
(StaticBoxLayoutProperties.PlaceholderType.SECTION);
}
else
{
this.context.getRenderBox().getStaticBoxLayoutProperties().setPlaceholderBox
(StaticBoxLayoutProperties.PlaceholderType.NONE);
}
}
this.textProducer.startText();
return this.context.getRenderBox().getInstanceId();
}
private static boolean isEmptyElement(final ReportElement band,
final StyleSheet style,
final OutputProcessorMetaData metaData)
{
if (isControlBand(style))
{
return false;
}
if (metaData.isFeatureSupported(OutputProcessorFeature.STRICT_COMPATIBILITY))
{
if (band instanceof Band)
{
final Band b = (Band) band;
if (b.getElementCount() > 0)
{
return false;
}
}
}
if (BandStyleKeys.LAYOUT_AUTO.equals(style.getStyleProperty(BandStyleKeys.LAYOUT)))
{
// A auto-band is considered empty.
return true;
}
// A band is not empty, if it has a defined minimum or preferred height
if (isLengthDefined(ElementStyleKeys.HEIGHT, style))
{
return false;
}
if (isLengthDefined(ElementStyleKeys.WIDTH, style))
{
return false;
}
if (isLengthDefined(ElementStyleKeys.POS_Y, style))
{
return false;
}
if (isLengthDefined(ElementStyleKeys.POS_X, style))
{
return false;
}
if (isLengthDefined(ElementStyleKeys.MIN_HEIGHT, style))
{
return false;
}
if (isLengthDefined(ElementStyleKeys.MIN_WIDTH, style))
{
return false;
}
if (isLengthDefined(ElementStyleKeys.PADDING_TOP, style))
{
return false;
}
if (isLengthDefined(ElementStyleKeys.PADDING_LEFT, style))
{
return false;
}
if (isLengthDefined(ElementStyleKeys.PADDING_BOTTOM, style))
{
return false;
}
if (isLengthDefined(ElementStyleKeys.PADDING_RIGHT, style))
{
return false;
}
if (BorderStyle.NONE.equals
(style.getStyleProperty(ElementStyleKeys.BORDER_BOTTOM_STYLE, BorderStyle.NONE)) == false)
{
return false;
}
if (BorderStyle.NONE.equals(style.getStyleProperty(ElementStyleKeys.BORDER_TOP_STYLE, BorderStyle.NONE)) == false)
{
return false;
}
if (BorderStyle.NONE.equals(style.getStyleProperty(ElementStyleKeys.BORDER_LEFT_STYLE, BorderStyle.NONE)) == false)
{
return false;
}
if (BorderStyle.NONE.equals
(style.getStyleProperty(ElementStyleKeys.BORDER_RIGHT_STYLE, BorderStyle.NONE)) == false)
{
return false;
}
if (style.getStyleProperty(ElementStyleKeys.BACKGROUND_COLOR) != null)
{
return false;
}
if (metaData.isExtraContentElement(band.getStyle(), band.getAttributes()))
{
return false;
}
return true;
}
public static boolean isControlBand(final StyleSheet style)
{
if (style.getStyleProperty(BandStyleKeys.COMPUTED_SHEETNAME) != null)
{
return true;
}
if (style.getStyleProperty(BandStyleKeys.BOOKMARK) != null)
{
return true;
}
if (BandStyleKeys.LAYOUT_INLINE.equals(style.getStyleProperty(BandStyleKeys.LAYOUT)) == false)
{
if (Boolean.TRUE.equals(style.getStyleProperty(BandStyleKeys.PAGEBREAK_AFTER)))
{
return true;
}
if (Boolean.TRUE.equals(style.getStyleProperty(BandStyleKeys.PAGEBREAK_BEFORE)))
{
return true;
}
}
return false;
}
private static boolean isLengthDefined(final StyleKey key, final StyleSheet styleSheet)
{
if (key.isInheritable())
{
if (styleSheet.isLocalKey(key) == false)
{
return false;
}
}
final Object o = styleSheet.getStyleProperty(key, null);
if (o == null)
{
return false;
}
if (o instanceof Number == false)
{
return false;
}
final Number n = (Number) o;
return n.doubleValue() != 0;
}
public void startSection()
{
final String layoutMode;
if (metaData.isFeatureSupported(OutputProcessorFeature.STRICT_COMPATIBILITY))
{
layoutMode = BandStyleKeys.LAYOUT_BLOCK;
}
else
{
layoutMode = BandStyleKeys.LAYOUT_AUTO;
}
final RenderBox renderBox = renderNodeFactory.produceSectionBox(layoutMode, null);
if (isAllowMergeSection())
{
this.context = new BandSectionLayoutModelBuilderContext(this.metaData, this.context, renderBox);
}
else
{
this.context = new DefaultLayoutModelBuilderContext(this.context, renderBox);
}
this.context.setEmpty(true);
if (legacySectionName != null)
{
this.context.getRenderBox().setName(legacySectionName);
}
this.textProducer.startText();
}
public void startSection(final ReportElement element, final int sectionSize)
{
final StyleSheet resolverStyleSheet = element.getComputedStyle();
final String layoutMode;
final boolean legacyMode = metaData.isFeatureSupported(OutputProcessorFeature.STRICT_COMPATIBILITY);
if (legacyMode)
{
layoutMode = BandStyleKeys.LAYOUT_BLOCK;
}
else
{
String layout = (String) resolverStyleSheet.getStyleProperty(BandStyleKeys.LAYOUT, BandStyleKeys.LAYOUT_AUTO);
if (BandStyleKeys.LAYOUT_INLINE.equals(layout) &&
!this.context.getRenderBox().isAcceptInlineBoxes()) {
layoutMode = BandStyleKeys.LAYOUT_BLOCK;
}
else {
layoutMode = layout;
}
}
final GroupSection groupSection = new GroupSection
(renderNodeFactory.produceRenderBox(element, resolverStyleSheet, layoutMode, null),
renderNodeFactory.createAutoGeneratedSectionStyleSheet(resolverStyleSheet), sectionSize, legacyMode);
this.context = new SectionLayoutModelBuilderContext(this.context, groupSection, legacyMode);
this.context.setEmpty(true);
if (element instanceof GroupBody ||
element instanceof Group)
{
// PRD-3154 - do we need to set placeholder to true?
// 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?
this.context.getRenderBox().getStaticBoxLayoutProperties().setPlaceholderBox
(StaticBoxLayoutProperties.PlaceholderType.SECTION);
}
this.textProducer.startText();
}
private void closeAutoGeneratedPostfixBoxes()
{
}
public boolean isEmptyElementsHaveSignificance()
{
if (designtime)
{
return true;
}
final RenderBox box = this.context.getRenderBox();
return box.isEmptyNodesHaveSignificance();
}
public boolean isEmptyElementsHaveSignificanceInParent()
{
final LayoutModelBuilderContext parent = this.context.getParent();
if (parent == null)
{
return false;
}
final RenderBox box = parent.getRenderBox();
return box.isEmptyNodesHaveSignificance();
}
private void ensureEmptyChildIsAdded(final RenderBox parent, final ReportElement element)
{
final StyleSheet resolverStyleSheet = element.getComputedStyle();
final RenderBox box;
if (parent.isAcceptInlineBoxes())
{
box = renderNodeFactory.produceRenderBox(element, resolverStyleSheet, BandStyleKeys.LAYOUT_INLINE, getStateKey());
}
else
{
box = renderNodeFactory.produceRenderBox(element, resolverStyleSheet, BandStyleKeys.LAYOUT_BLOCK, getStateKey());
}
box.getStaticBoxLayoutProperties().setPlaceholderBox(StaticBoxLayoutProperties.PlaceholderType.SECTION);
box.close();
parent.addChild(box);
}
public void processContent(final ReportElement element,
final Object computedValue,
final Object rawValue)
{
if (computedValue == null)
{
final StyleSheet resolvedStyle = element.getComputedStyle();
final RenderBox parentRenderBox = this.context.getRenderBox();
if (parentRenderBox.isEmptyNodesHaveSignificance() ||
metaData.isExtraContentElement(resolvedStyle, element.getAttributes()))
{
ensureEmptyChildIsAdded(parentRenderBox, element);
this.context.setEmpty(false);
}
return;
}
if (String.class.equals(computedValue.getClass()))
{
processText(element, (String) computedValue, rawValue);
}
else if (computedValue instanceof Shape)
{
final StyleSheet resolvedStyle = element.getComputedStyle();
final Shape shape = (Shape) computedValue;
final ReportDrawable reportDrawable = new ShapeDrawable
(shape, resolvedStyle.getBooleanStyleProperty(ElementStyleKeys.KEEP_ASPECT_RATIO));
processReportDrawable(element, reportDrawable, rawValue);
}
else if (computedValue instanceof ReportDrawable)
{
processReportDrawable(element, (ReportDrawable) computedValue, rawValue);
}
else if (computedValue instanceof ImageContainer ||
computedValue instanceof DrawableWrapper)
{
processReplacedContent(element, computedValue, rawValue);
}
else if (DrawableWrapper.isDrawable(computedValue))
{
processReplacedContent(element, new DrawableWrapper(computedValue), rawValue);
}
else
{
processText(element, String.valueOf(computedValue), rawValue);
}
}
private boolean isTableContext(RenderNode node)
{
while (node != null)
{
if ((node.getLayoutNodeType() & LayoutNodeTypes.MASK_BOX_TABLE) == LayoutNodeTypes.MASK_BOX_TABLE)
{
return true;
}
node = node.getParent();
}
return false;
}
protected void processText(final ReportElement element,
String computedValue,
final Object rawValue)
{
final SimpleStyleSheet resolverStyleSheet = element.getComputedStyle();
if (computedValue != null &&
resolverStyleSheet.getBooleanStyleProperty(TextStyleKeys.TRIM_TEXT_CONTENT))
{
computedValue = computedValue.trim();
}
if (this.context.getRenderBox().isAcceptInlineBoxes() == false)
{
final StyleSheet elementStyle;
final int parentNodeType = this.context.getRenderBox().getLayoutNodeType();
if (strictLegacyMode && (parentNodeType & LayoutNodeTypes.MASK_BOX_CANVAS) == LayoutNodeTypes.MASK_BOX_CANVAS)
{
// A table always claims all elements as dynamic. Use the max-height to limit the expansion of elements.
if (isTableContext(this.context.getRenderBox()) == false &&
resolverStyleSheet.getBooleanStyleProperty(ElementStyleKeys.DYNAMIC_HEIGHT) == false)
{
elementStyle = new NonDynamicHeightWrapperStyleSheet(resolverStyleSheet);
}
else
{
elementStyle = new DynamicHeightWrapperStyleSheet(resolverStyleSheet);
}
}
else
{
elementStyle = resolverStyleSheet;
}
this.textProducer.startText();
final RenderBox renderBox = renderNodeFactory.createAutoParagraph(element, elementStyle, stateKey);
final RenderNode[] renderNodes = textProducer.getRenderNodes(element, elementStyle, computedValue);
renderBox.addChilds(renderNodes);
renderBox.setRawValue(rawValue);
this.context = new DefaultLayoutModelBuilderContext(this.context, renderBox);
this.context.setEmpty(renderNodes.length == 0 && isEmptyElement(element, resolverStyleSheet, metaData));
this.context = this.context.close();
}
else
{
final StyleSheet safeElementStyle = renderNodeFactory.createStyle(resolverStyleSheet);
final RenderBox renderBox = renderNodeFactory.produceRenderBox(element, resolverStyleSheet, BandStyleKeys.LAYOUT_INLINE, stateKey);
final RenderNode[] renderNodes = textProducer.getRenderNodes(element, safeElementStyle, computedValue);
renderBox.addChilds(renderNodes);
renderBox.setRawValue(rawValue);
this.context = new DefaultLayoutModelBuilderContext(this.context, renderBox);
this.context.setEmpty(renderNodes.length == 0 && isEmptyElement(element, resolverStyleSheet, metaData));
this.context = this.context.close();
}
}
protected void processReportDrawable(final ReportElement element,
final ReportDrawable reportDrawable,
final Object rawValue)
{
final SimpleStyleSheet resolverStyleSheet = element.getComputedStyle();
reportDrawable.setStyleSheet(resolverStyleSheet);
reportDrawable.setConfiguration(processingContext.getConfiguration());
reportDrawable.setResourceBundleFactory(processingContext.getResourceBundleFactory());
if (reportDrawable instanceof DrawableWrapper)
{
processReplacedContent(element, reportDrawable, rawValue);
}
else
{
processReplacedContent(element, new DrawableWrapper(reportDrawable), rawValue);
}
}
protected void processReplacedContent(final ReportElement element,
final Object value,
final Object rawValue)
{
final RenderBox box = this.context.getRenderBox();
final SimpleStyleSheet resolverStyleSheet = element.getComputedStyle();
final StyleSheet elementStyle;
if (box.isAcceptInlineBoxes() == false)
{
if (isTableContext(box) == false &&
resolverStyleSheet.getBooleanStyleProperty(ElementStyleKeys.DYNAMIC_HEIGHT) == false)
{
elementStyle = new NonDynamicReplacedContentStyleSheet(resolverStyleSheet);
}
else
{
elementStyle = new DynamicReplacedContentStyleSheet(resolverStyleSheet);
}
}
else
{
elementStyle = resolverStyleSheet;
}
final RenderableReplacedContentBox child =
renderNodeFactory.createReplacedContent(element, elementStyle, value, rawValue, stateKey);
child.setName(element.getName());
this.context.addChild(child);
this.context.setEmpty(false);
}
public boolean finishBox()
{
boolean empty = this.context.isEmpty();
if (empty)
{
if (isEmptyElementsHaveSignificanceInParent())
{
this.context.setEmpty(false);
empty = false;
}
}
this.context = this.context.close();
while (this.context.isAutoGeneratedWrapperBox())
{
this.context = this.context.close();
}
return empty;
}
public void endSection()
{
this.context = this.context.close();
while (this.context.isAutoGeneratedWrapperBox())
{
this.context = this.context.close();
}
}
public InstanceID createSubflowPlaceholder(final ReportElement element)
{
final StyleSheet resolverStyleSheet = element.getComputedStyle();
final RenderBox subReportBox = renderNodeFactory.produceSubReportPlaceholder(element, resolverStyleSheet, stateKey);
this.context.addChild(subReportBox);
this.context.setEmpty(false);
return subReportBox.getInstanceId();
}
public InlineSubreportMarker processSubReport(final SubReport element)
{
if (isLimitedSubReports())
{
logger.debug("Not adding subreport: Subreports in header or footer area are not allowed.");
return null;
}
final RenderBox parentBox = this.context.getRenderBox();
if (parentBox.isAcceptInlineBoxes())
{
logger.warn("Not adding subreport: Subreports in inline-contexts are not supported.");
return null;
}
final StyleSheet resolverStyleSheet = element.getComputedStyle();
final RenderBox subReportBox = renderNodeFactory.produceSubReportPlaceholder(element, resolverStyleSheet, stateKey);
this.context.addChild(subReportBox);
this.context.setEmpty(false);
final InstanceID subReportBoxId = subReportBox.getInstanceId();
// the box will be closed
return new InlineSubreportMarker(element, subReportBoxId, SubReportProcessType.INLINE);
}
public boolean isEmpty()
{
return context.isEmpty();
}
public void print()
{
ModelPrinter.INSTANCE.print(context.getRenderBox());
}
protected LayoutModelBuilderContext getContext()
{
return context;
}
protected void setContext(final LayoutModelBuilderContext context)
{
this.context = context;
}
protected TextProducer getTextProducer()
{
return textProducer;
}
protected RenderNodeFactory getRenderNodeFactory()
{
return renderNodeFactory;
}
protected ReportStateKey getStateKey()
{
return stateKey;
}
public void startSubFlow(final InstanceID insertationPoint)
{
final RenderBox box;
if (insertationPoint == null)
{
throw new IllegalStateException();
}
final RenderBox rootBox = getLayoutRoot();
box = (RenderBox) rootBox.findNodeById(insertationPoint);
if (box == null)
{
dontPushBoxToContext();
} else {
pushBoxToContext(box, false);
}
}
protected void pushBoxToContext(final RenderBox box, final boolean empty)
{
this.context = new DefaultLayoutModelBuilderContext(this.context, box);
this.context.setEmpty(empty);
this.textProducer.startText();
}
protected void dontPushBoxToContext() {
this.context = new DummyLayoutModelBuilderContext(this.context);
this.context.setEmpty(true);
}
public void startSubFlow(final ReportElement element)
{
final StyleSheet resolverStyleSheet = element.getComputedStyle();
final RenderBox box;
if (metaData.isFeatureSupported(OutputProcessorFeature.STRICT_COMPATIBILITY))
{
final StyleSheet styleSheet = new SubReportStyleSheet
(resolverStyleSheet.getBooleanStyleProperty(BandStyleKeys.PAGEBREAK_BEFORE),
(resolverStyleSheet.getBooleanStyleProperty(BandStyleKeys.PAGEBREAK_AFTER)));
final SimpleStyleSheet reportStyle = new SimpleStyleSheet(styleSheet);
final BoxDefinition boxDefinition = renderNodeFactory.getBoxDefinition(reportStyle);
box = new BlockRenderBox
(reportStyle, element.getObjectID(), boxDefinition, SubReportType.INSTANCE, element.getAttributes(), null);
}
else
{
box = renderNodeFactory.produceRenderBox(element, resolverStyleSheet, BandStyleKeys.LAYOUT_BLOCK, stateKey);
}
box.getStaticBoxLayoutProperties().setPlaceholderBox(StaticBoxLayoutProperties.PlaceholderType.SECTION);
if (element.getName() != null)
{
box.setName("Banded-SubReport-Section" + ": name=" + element.getName());
}
else
{
box.setName("Banded-SubReport-Section");
}
pushBoxToContext(box, false);
}
private RenderBox getLayoutRoot()
{
LayoutModelBuilderContext context = this.context;
while (context != null)
{
if (context.getParent() == null)
{
return context.getRenderBox();
}
context = context.getParent();
}
throw new IllegalStateException();
}
public void suspendSubFlow()
{
this.context = this.context.getParent();
}
public void endSubFlow()
{
endSection();
}
public void addProgressMarkerBox()
{
final RenderBox parent = this.context.getRenderBox();
final RenderNode child = parent.getLastChild();
if (isCollapseProgressMarker() &&
child != null && child.getNodeType() == LayoutNodeTypes.TYPE_BOX_PROGRESS_MARKER)
{
final ProgressMarkerRenderBox markerRenderBox = (ProgressMarkerRenderBox) child;
markerRenderBox.setStateKey(stateKey);
}
else
{
final ProgressMarkerRenderBox markerBox = new ProgressMarkerRenderBox();
markerBox.setStateKey(stateKey);
this.context.addChild(markerBox);
markerBox.close();
}
this.context.setEmpty(false);
}
public void addManualPageBreakBox(final long range)
{
final RenderBox breakIndicatorBox = renderNodeFactory.createPageBreakIndicatorBox(stateKey, range);
this.context.addChild(breakIndicatorBox);
this.context.setEmpty(false);
}
public void setCollapseProgressMarker(final boolean collapseProgressMarker)
{
this.collapseProgressMarker = collapseProgressMarker;
}
public boolean isCollapseProgressMarker()
{
return collapseProgressMarker;
}
public void setLimitedSubReports(final boolean limitedSubReports)
{
this.limitedSubReports = limitedSubReports;
}
public boolean isLimitedSubReports()
{
return limitedSubReports;
}
public DefaultLayoutModelBuilder clone()
{
try
{
return (DefaultLayoutModelBuilder) super.clone();
}
catch (CloneNotSupportedException e)
{
throw new IllegalStateException(e);
}
}
public LayoutModelBuilder deriveForPageBreak()
{
final DefaultLayoutModelBuilder clone = clone();
clone.context = context.deriveForPagebreak();
return clone;
}
public LayoutModelBuilder deriveForStorage(final RenderBox clonedContent)
{
final DefaultLayoutModelBuilder clone = clone();
clone.context = context.deriveForStorage(clonedContent);
return clone;
}
public void restoreStateAfterRollback()
{
LayoutModelBuilderContext c = context;
while (c != null)
{
c.restoreStateAfterRollback();
c = c.getParent();
}
}
public void validateAfterCommit()
{
LayoutModelBuilderContext c = context;
while (c != null)
{
c.validateAfterCommit();
c = c.getParent();
}
}
public void performParanoidModelCheck(final RenderBox logicalPageBox)
{
LayoutModelBuilderContext c = context;
while (c != null)
{
c.performParanoidModelCheck();
final RenderBox renderBox = c.getRenderBox();
testIsLogicalPageParent(renderBox, logicalPageBox);
c = c.getParent();
}
}
private void testIsLogicalPageParent(RenderBox b, final RenderBox logicalPageBox)
{
while (b != null)
{
if (b == logicalPageBox)
{
return;
}
b = b.getParent();
}
throw new IllegalStateException();
}
public void legacyFlagNotEmpty()
{
context.setEmpty(false);
}
public void legacyAddPlaceholder(final ReportElement element)
{
final RenderBox parentRenderBox = this.context.getRenderBox();
ensureEmptyChildIsAdded(parentRenderBox, element);
this.context.setEmpty(false);
}
public RenderNode dangerousRawAccess()
{
return context.getRenderBox();
}
public void close()
{
}
}