From b7a115d5d6efa7ed7e67c9126fc617ddcd665db8 Mon Sep 17 00:00:00 2001 From: Yoseph Phillips Date: Wed, 6 Dec 2023 14:58:58 +1100 Subject: [PATCH 1/2] Reduce memory usage and improve performance. --- .../engine/fill/JRFillElement.java | 3871 ++++++++--------- .../engine/fill/JRFillElementContainer.java | 2509 ++++++----- 2 files changed, 3183 insertions(+), 3197 deletions(-) diff --git a/jasperreports/src/net/sf/jasperreports/engine/fill/JRFillElement.java b/jasperreports/src/net/sf/jasperreports/engine/fill/JRFillElement.java index 1ea58c57dd..df89ee0dc5 100644 --- a/jasperreports/src/net/sf/jasperreports/engine/fill/JRFillElement.java +++ b/jasperreports/src/net/sf/jasperreports/engine/fill/JRFillElement.java @@ -74,1944 +74,1935 @@ */ public abstract class JRFillElement implements JRElement, JRFillCloneable, JRStyleSetter, DynamicPropertiesHolder { - /** - * - */ - public static final String EXCEPTION_MESSAGE_KEY_INVALID_BOOKMARK_LEVEL = "fill.anchor.bookmark.level.invalid"; - - - /** - * - */ - protected JRElement parent; - protected List propertyExpressions; - protected List dynamicTransferProperties; - protected JRStyle providerStyle; - protected Map templates = new HashMap<>(); - protected List styleProviders; - - /** - * - */ - protected JRBaseFiller filler; - protected JRFillExpressionEvaluator expressionEvaluator; - - protected JRDefaultStyleProvider defaultStyleProvider; - - /** - * - */ - protected JRGroup printWhenGroupChanges; - protected JRFillElementGroup elementGroup; - - /** - * - */ - protected JRFillBand band; - - protected JROriginProvider originProvider; - - protected PrintElementOriginator printElementOriginator; - - /** - * - */ - private boolean isPrintWhenExpressionNull = true; - private boolean isPrintWhenTrue = true; - private boolean isToPrint = true; - private boolean isReprinted; - private boolean isAlreadyPrinted; - private Collection dependantElements = new ArrayList<>(); - private int relativeY; - private int collapsedHeightAbove; - private int collapsedHeightBelow; - /** - * Keeps total stretch height, including forced stretch after honoring the stretchType property of the element. - */ - private int stretchHeight; - /** - * Keeps the natural stretch height calculated during element prepare. - */ - private int prepareHeight; - - private int x; - private int y; - private int width; - private int height; - - private boolean isValueRepeating; - - protected byte currentEvaluation; - - // used by elements that support evaluationTime=Auto - protected Map delayedEvaluationsMap; - - protected JRFillElementContainer conditionalStylesContainer; - protected FillContainerContext fillContainerContext; - - protected JRStyle initStyle; - protected JRStyle exprStyle; - protected JRStyle currentStyle; - - /** - * Flag indicating whether the element is shrinkable. - * @see #setShrinkable(boolean) - */ - private boolean shrinkable; - - protected JRPropertiesMap staticProperties; - protected JRPropertiesMap staticTransferProperties; - protected JRPropertiesMap dynamicProperties; - protected JRPropertiesMap mergedProperties; - - protected boolean hasDynamicPopulateTemplateStyle; - protected Boolean defaultPopulateTemplateStyle; - - /** - * - * - private JRElement topElementInGroup; - private JRElement bottomElementInGroup; - - - /** - * - */ - protected JRFillElement( - JRBaseFiller filler, - JRElement element, - JRFillObjectFactory factory - ) - { - factory.put(element, this); - - this.parent = element; - this.filler = filler; - this.expressionEvaluator = factory.getExpressionEvaluator(); - this.defaultStyleProvider = factory.getDefaultStyleProvider(); - - printElementOriginator = filler.assignElementId(this); - - /* */ - printWhenGroupChanges = factory.getGroup(element.getPrintWhenGroupChanges()); - elementGroup = (JRFillElementGroup)factory.getVisitResult(element.getElementGroup()); - - x = element.getX(); - y = element.getY(); - width = element.getWidth(); - height = element.getHeight(); - - staticProperties = element.hasProperties() ? element.getPropertiesMap().cloneProperties() : null; - staticTransferProperties = findStaticTransferProperties(); - mergedProperties = staticProperties; - - JRPropertyExpression[] elementPropertyExpressions = element.getPropertyExpressions(); - propertyExpressions = elementPropertyExpressions == null ? new ArrayList<>(0) - : new ArrayList<>(Arrays.asList(elementPropertyExpressions)); - - dynamicTransferProperties = findDynamicTransferProperties(); - - factory.registerDelayedStyleSetter(this, parent); - - initStyleProviders(); - lookForPartProperty(staticProperties); - - hasDynamicPopulateTemplateStyle = hasDynamicProperty(PROPERTY_ELEMENT_TEMPLATE_POPULATE_STYLE); - } - - - protected JRFillElement(JRFillElement element, JRFillCloneFactory factory) - { - factory.put(element, this); - - this.parent = element.parent; - this.filler = element.filler; - this.expressionEvaluator = element.expressionEvaluator; - this.defaultStyleProvider = element.defaultStyleProvider; - this.originProvider = element.originProvider; - - printElementOriginator = element.printElementOriginator; - - /* */ - printWhenGroupChanges = element.printWhenGroupChanges; - elementGroup = (JRFillElementGroup) factory.getClone((JRFillElementGroup) element.getElementGroup()); - - x = element.getX(); - y = element.getY(); - width = element.getWidth(); - height = element.getHeight(); - - templates = element.templates; - - initStyle = element.initStyle; - - shrinkable = element.shrinkable; - - staticProperties = element.staticProperties == null ? null : element.staticProperties.cloneProperties(); - staticTransferProperties = element.staticTransferProperties; - mergedProperties = staticProperties; - this.propertyExpressions = new ArrayList<>(element.propertyExpressions); - this.dynamicTransferProperties = element.dynamicTransferProperties; - - // we need a style provider context for this element instance - initStyleProviders(); - - this.hasDynamicPopulateTemplateStyle = element.hasDynamicPopulateTemplateStyle; - this.defaultPopulateTemplateStyle = element.defaultPopulateTemplateStyle; - } - - private JRPropertiesMap findStaticTransferProperties() - { - if (staticProperties == null) - { - return null; - } - - String[] propertyNames = staticProperties.getPropertyNames(); - List prefixes = filler.getPrintTransferPropertyPrefixes(); - JRPropertiesMap transferProperties = new JRPropertiesMap(); - for (int i = 0; i < propertyNames.length; i++) - { - for (String prefix : prefixes) - { - String prop = propertyNames[i]; - if (prop.startsWith(prefix)) - { - transferProperties.setProperty(prop, staticProperties.getProperty(prop)); - break; - } - } - } - return transferProperties.isEmpty() ? null : transferProperties; - } - - private List findDynamicTransferProperties() - { - if (propertyExpressions.isEmpty()) - { - return null; - } - - List prefixes = filler.getPrintTransferPropertyPrefixes(); - List transferProperties = new ArrayList<>(propertyExpressions.size()); - for (JRPropertyExpression propertyExpression : propertyExpressions) - { - String propertyName = propertyExpression.getName(); - for (String prefix : prefixes) - { - if (propertyName.startsWith(prefix)) - { - transferProperties.add(propertyName); - break; - } - } - } - return transferProperties; - } - - private void lookForPartProperty(JRPropertiesMap properties) - { - if (properties != null && properties.getProperty(PrintPart.ELEMENT_PROPERTY_PART_NAME) != null) - { - filler.getFillContext().setDetectParts(true); - } - } - - @Override - public JRDefaultStyleProvider getDefaultStyleProvider() - { - return defaultStyleProvider; - } - - /** - * - */ - protected StyleResolver getStyleResolver() - { - return getDefaultStyleProvider().getStyleResolver(); - } - - @Override - public UUID getUUID() - { - return parent.getUUID(); - } - - @Override - public String getKey() - { - return parent.getKey(); - } - - @Override - public PositionTypeEnum getPositionTypeValue() - { - return parent.getPositionTypeValue();//FIXME optimize this by consolidating style properties - } - - @Override - public void setPositionType(PositionTypeEnum positionType) - { - throw new UnsupportedOperationException(); - } - - @Override - public StretchTypeEnum getStretchTypeValue() - { - return parent.getStretchTypeValue(); - } - - @Override - public void setStretchType(StretchTypeEnum stretchType) - { - throw new UnsupportedOperationException(); - } - - @Override - public boolean isPrintRepeatedValues() - { - return parent.isPrintRepeatedValues(); - } - - @Override - public void setPrintRepeatedValues(boolean isPrintRepeatedValues) - { - } - - @Override - public ModeEnum getModeValue() - { - return getStyleResolver().getMode(this, ModeEnum.OPAQUE); - } - - @Override - public ModeEnum getOwnModeValue() - { - return providerStyle == null || providerStyle.getOwnModeValue() == null ? parent.getOwnModeValue() : providerStyle.getOwnModeValue(); - } - - @Override - public void setMode(ModeEnum modeValue) - { - } - - @Override - public int getX() - { - return x; - } - - @Override - public void setX(int x) - { - this.x = x; - } - - /** - * - */ - public void setY(int y) - { - this.y = y; - } - - @Override - public int getY() - { - return y; - } - - @Override - public int getWidth() - { - return width; - } - - @Override - public void setWidth(int width) - { - this.width = width; - } - - /** - * - */ - public void setHeight(int height) - { - this.height = height; - } - - @Override - public int getHeight() - { - return height; - } - - @Override - public boolean isRemoveLineWhenBlank() - { - return parent.isRemoveLineWhenBlank(); - } - - @Override - public void setRemoveLineWhenBlank(boolean isRemoveLine) - { - } - - @Override - public boolean isPrintInFirstWholeBand() - { - return parent.isPrintInFirstWholeBand(); - } - - @Override - public void setPrintInFirstWholeBand(boolean isPrint) - { - } - - @Override - public boolean isPrintWhenDetailOverflows() - { - return parent.isPrintWhenDetailOverflows(); - } - - @Override - public void setPrintWhenDetailOverflows(boolean isPrint) - { - } - - @Override - public Color getForecolor() - { - return getStyleResolver().getForecolor(this); - } - - @Override - public Color getOwnForecolor() - { - return providerStyle == null || providerStyle.getOwnForecolor() == null ? parent.getOwnForecolor() : providerStyle.getOwnForecolor(); - } - - @Override - public void setForecolor(Color forecolor) - { - } - - @Override - public Color getBackcolor() - { - return getStyleResolver().getBackcolor(this); - } - - @Override - public Color getOwnBackcolor() - { - return providerStyle == null || providerStyle.getOwnBackcolor() == null ? parent.getOwnBackcolor() : providerStyle.getOwnBackcolor(); - } - - @Override - public void setBackcolor(Color backcolor) - { - } - - @Override - public JRExpression getStyleExpression() - { - return parent.getStyleExpression(); - } - - @Override - public JRExpression getPrintWhenExpression() - { - return parent.getPrintWhenExpression(); - } - - @Override - public JRGroup getPrintWhenGroupChanges() - { - return printWhenGroupChanges; - } - - @Override - public JRElementGroup getElementGroup() - { - return elementGroup; - } - - /** - * - */ - protected boolean isPrintWhenExpressionNull() - { - return isPrintWhenExpressionNull; - } - - /** - * - */ - protected void setPrintWhenExpressionNull(boolean isPrintWhenExpressionNull) - { - this.isPrintWhenExpressionNull = isPrintWhenExpressionNull; - } - - /** - * - */ - protected boolean isPrintWhenTrue() - { - return isPrintWhenTrue; - } - - /** - * - */ - protected void setPrintWhenTrue(boolean isPrintWhenTrue) - { - this.isPrintWhenTrue = isPrintWhenTrue; - } - - /** - * - */ - public boolean isToPrint() - { - return isToPrint; - } - - /** - * - */ - protected void setToPrint(boolean isToPrint) - { - this.isToPrint = isToPrint; - } - - /** - * - */ - protected boolean isReprinted() - { - return isReprinted; - } - - /** - * - */ - protected void setReprinted(boolean isReprinted) - { - this.isReprinted = isReprinted; - } - - /** - * - */ - public boolean isAlreadyPrinted() - { - return isAlreadyPrinted; - } - - /** - * - */ - public void setAlreadyPrinted(boolean isAlreadyPrinted) - { - this.isAlreadyPrinted = isAlreadyPrinted; - } - - /** - * - */ - protected JRElement[] getGroupElements() - { - JRElement[] groupElements = null; - - if (elementGroup != null) - { - groupElements = elementGroup.getElements(); - } - - return groupElements; - } - - /** - * - */ - protected Collection getDependantElements() - { - return dependantElements; - } - - /** - * - */ - protected void addDependantElement(JRFillElement element) - { - dependantElements.add(element); - } - - /** - * - */ - protected int getRelativeY() - { - return relativeY; - } - - /** - * - */ - protected void setRelativeY(int relativeY) - { - this.relativeY = relativeY; - } - - /** - * - */ - protected int getCollapsedHeightAbove() - { - return collapsedHeightAbove; - } - - /** - * - */ - protected void setCollapsedHeightAbove(int collapsedHeightAbove) - { - this.collapsedHeightAbove = collapsedHeightAbove; - } - - /** - * - */ - protected int getCollapsedHeightBelow() - { - return collapsedHeightBelow; - } - - /** - * - */ - protected void setCollapsedHeightBelow(int collapsedHeightBelow) - { - this.collapsedHeightBelow = collapsedHeightBelow; - } - - /** - * - */ - public int getStretchHeight() - { - return stretchHeight; - } - - /** - * - */ - protected void setStretchHeight(int stretchHeight) - { - if (stretchHeight > getHeight() || (shrinkable && isRemoveLineWhenBlank())) - { - this.stretchHeight = stretchHeight; - } - else - { - this.stretchHeight = getHeight(); - } - } - - /** - * - */ - public int getPrepareHeight() - { - return prepareHeight; - } - - /** - * Element height is calculated in two phases. - * First, the element stretches on its own during prepare, to accommodate all its content. - * This is the natural stretch and we keep track of the calculated height in prepareHeight. - * Secondly, the element stretches further in accordance with its stretchType property. - * This forced stretch occurs at a later time and the amount of stretch is kept in stretchHeight. - */ - protected void setPrepareHeight(int prepareHeight) - { - this.prepareHeight = prepareHeight; - - setStretchHeight(prepareHeight); - } - - /** - * - */ - protected JRFillBand getBand() - { - return band; - } - - /** - * - */ - protected void setBand(JRFillBand band) - { - this.band = band; - - if (this.originProvider == null) - { - setOriginProvider(band); - } - } - - - /** - * - */ - protected void initStyleProviders() - { - List styleProviderFactories = filler.getJasperReportsContext().getExtensions(StyleProviderFactory.class); - if (styleProviderFactories != null && styleProviderFactories.size() > 0) - { - FillStyleProviderContext styleProviderContext = new FillStyleProviderContext(this); - for (StyleProviderFactory styleProviderFactory : styleProviderFactories) - { - StyleProvider styleProvider = styleProviderFactory.getStyleProvider(styleProviderContext, filler.getJasperReportsContext()); - if (styleProvider != null) - { - if (styleProviders == null) - { - styleProviders = new ArrayList<>(); - } - styleProviders.add(styleProvider); - } - } - } - } - - - /** - * - */ - protected void reset() - { - relativeY = y; - collapsedHeightAbove = 0; - collapsedHeightBelow = 0; - stretchHeight = height; - prepareHeight = height; - - if (elementGroup != null) - { - elementGroup.reset(); - } - } - - protected void setCurrentEvaluation(byte evaluation) - { - currentEvaluation = evaluation; - } - - /** - * - */ - protected abstract void evaluate( - byte evaluation - ) throws JRException; - - - /** - * - */ - protected void evaluateStyle( - byte evaluation - ) throws JRException - { - exprStyle = null; - - JRExpression styleExpression = getStyleExpression(); - if (styleExpression != null) - { - String styleName = (String)evaluateExpression(styleExpression, evaluation); - if (styleName != null) - { - exprStyle = getFiller().factory.stylesMap.getStyle(styleName); - if (exprStyle == null) - { - throw - new JRRuntimeException( - JRFillObjectFactory.EXCEPTION_MESSAGE_KEY_STYLE_NOT_FOUND, - new Object[]{styleName} - ); - } - else - { - conditionalStylesContainer.collectConditionalStyle(exprStyle); - } - } - } - - if (exprStyle != null && isEvaluateNow()) - { - conditionalStylesContainer.evaluateConditionalStyle(exprStyle, evaluation); - initStyle = exprStyle; - } - - providerStyle = null; - - if (styleProviders != null && styleProviders.size() > 0) - { - for (StyleProvider styleProvider : styleProviders) - { - JRStyle style = styleProvider.getStyle(evaluation); - if (style != null) - { - if (providerStyle == null) - { - providerStyle = new JRBaseStyle(); - } - StyleUtil.appendStyle(providerStyle, style); - } - } - } - } - - protected TimeZone getTimeZone() - { - return filler.getTimeZone(); - } - - /** - * - */ - protected void evaluatePrintWhenExpression( - byte evaluation - ) throws JRException - { - boolean isExprNull = true; - boolean isExprTrue = false; - - JRExpression expression = getPrintWhenExpression(); - if (expression != null) - { - isExprNull = false; - Boolean printWhenExpressionValue = (Boolean) evaluateExpression(expression, evaluation); - if (printWhenExpressionValue == null) - { - isExprTrue = false; - } - else - { - isExprTrue = printWhenExpressionValue; - } - } - - setPrintWhenExpressionNull(isExprNull); - setPrintWhenTrue(isExprTrue); - } - - - /** - * - */ - protected abstract void rewind() throws JRException; - - - /** - * - */ - protected abstract JRPrintElement fill() throws JRException; - - protected JRTemplateElement getElementTemplate() - { - JRTemplateElement template = null; - JRStyle style = null; - - if (providerStyle == null) - { - // no style provider has been used so we can use cache template per style below - style = getStyle(); - template = getTemplate(style); - } - - if (template == null) - { - template = createElementTemplate(); - transferProperties(template); - - if (toPopulateTemplateStyle()) - { - template.populateStyle(); - } - - // deduplicate to previously created identical objects - template = filler.fillContext.deduplicate(template); - - if (providerStyle == null) - { - registerTemplate(style, template); - } - } - return template; - } - - protected abstract JRTemplateElement createElementTemplate(); - - protected boolean toPopulateTemplateStyle() - { - if (defaultPopulateTemplateStyle == null) - { - defaultPopulateTemplateStyle = filler.getPropertiesUtil().getBooleanProperty( - PROPERTY_ELEMENT_TEMPLATE_POPULATE_STYLE, false, - parent, filler.getMainDataset()); - } - - boolean populate = defaultPopulateTemplateStyle; - if (hasDynamicPopulateTemplateStyle) - { - String populateProp = getDynamicProperties().getProperty( - PROPERTY_ELEMENT_TEMPLATE_POPULATE_STYLE); - if (populateProp != null) - { - populate = JRPropertiesUtil.asBoolean(populateProp); - } - } - return populate; - } - - /** - * - */ - protected boolean prepare( - int availableHeight, - boolean isOverflow - ) throws JRException - { - if ( - isPrintWhenExpressionNull() || - ( !isPrintWhenExpressionNull() && - isPrintWhenTrue() ) - ) - { - setToPrint(true); - } - else - { - setToPrint(false); - } - - setReprinted(false); - - return false; - } - - - /** - * @deprecated To be removed. - */ - protected void _stretchElement(int bandStretch) - { - switch (getStretchTypeValue()) - { - case RELATIVE_TO_BAND_HEIGHT : - case CONTAINER_HEIGHT : - case CONTAINER_BOTTOM : - { - _stretchElementToHeight(getHeight() + bandStretch); - break; - } - case RELATIVE_TO_TALLEST_OBJECT : - case ELEMENT_GROUP_HEIGHT : - case ELEMENT_GROUP_BOTTOM : - { - if (elementGroup != null) - { - //setStretchHeight(getHeight() + getStretchHeightDiff()); - _stretchElementToHeight(getHeight() + elementGroup.getStretchHeightDiff()); - } - - break; - } - case NO_STRETCH : - default : - { - break; - } - } - } - - /** - * @deprecated To be removed. - */ - protected void _stretchElementToHeight(int stretchHeight) - { - if (stretchHeight > getStretchHeight()) - { - setStretchHeight(stretchHeight); - } - } - - - /** - * - */ - @SuppressWarnings("deprecation") - protected boolean stretchElement(int containerStretch) - { - boolean applied = false; - switch (getStretchTypeValue()) - { - case RELATIVE_TO_BAND_HEIGHT : - case CONTAINER_HEIGHT : - case CONTAINER_BOTTOM : - { - applied = stretchElementToContainer(containerStretch); - break; - } - case RELATIVE_TO_TALLEST_OBJECT : - case ELEMENT_GROUP_HEIGHT : - case ELEMENT_GROUP_BOTTOM : - { - applied = stretchElementToElementGroup(); - break; - } - case NO_STRETCH : - default : - { - break; - } - } - return applied; - } - - - /** - * - */ - @SuppressWarnings("deprecation") - protected boolean stretchElementToContainer(int containerStretch)//TODO subtract firstY? - { - boolean applied = false; - switch (getStretchTypeValue()) - { - case RELATIVE_TO_BAND_HEIGHT : - case CONTAINER_HEIGHT : - { - applied = stretchElementToHeight(getHeight() + containerStretch); - break; - } - case CONTAINER_BOTTOM : - { - applied = stretchElementToHeight(getY() - getRelativeY() + getHeight() + containerStretch); - break; - } - default : - } - return applied; - } - - - /** - * - */ - @SuppressWarnings("deprecation") - protected boolean stretchElementToElementGroup() - { - boolean applied = false; - if (elementGroup != null) - { - switch (getStretchTypeValue()) - { - case RELATIVE_TO_TALLEST_OBJECT : - case ELEMENT_GROUP_HEIGHT : - { - applied = stretchElementToHeight(getHeight() + elementGroup.getStretchHeightDiff()); - break; - } - case ELEMENT_GROUP_BOTTOM : - { - applied = stretchElementToHeight(getY() - getRelativeY() + getHeight() + elementGroup.getStretchHeightDiff()); - break; - } - default : - } - } - return applied; - } - - - /** - * This method returns a boolean signaling if any stretch change occurred. - * It does not say which amount of stretch was applied, but that is OK, because the only place where this is checked - * is during frame cascading stretch, where the stretchHeight field of the frame (set here) is used directly. - */ - protected boolean stretchElementToHeight(int stretchHeight) - { - // cannot force the element to shrink below its natural stretch calculated during prepare; - // such situation could occur when the container breaks and overflows - boolean applied = false; - if (stretchHeight > getPrepareHeight()) - { - // any new stretchHeight that is greater than element's natural growth is fine - setStretchHeight(stretchHeight); - applied = true; - } - return applied; - } - - - /** - * @deprecated To be removed. - */ - protected void _moveDependantElements() - { - Collection elements = getDependantElements(); - if (elements != null && elements.size() > 0) - { - for (JRFillElement element : elements) - { - int newRelativeY = - getRelativeY() + getStretchHeight() //pusher element current bottom edge - + (element.getY() - (getY() + getHeight())); //design time distance between elements; difference between float element top edge and pusher element bottom edge - - if (newRelativeY > element.getRelativeY()) - { - element.setRelativeY(newRelativeY); - } - } - } - } - - - /** - * - */ - protected void moveDependantElements() - { - Collection elements = getDependantElements(); - if (elements != null && elements.size() > 0) - { - for (JRFillElement element : elements) - { - int newRelativeY = - getRelativeY() + getStretchHeight() //pusher element current bottom edge - + (element.getY() - (getY() + getHeight())) //design time distance between elements; difference between float element top edge and pusher element bottom edge - - (element.getCollapsedHeightAbove() - getCollapsedHeightAbove()); //difference in collapsedY amount, meaning the elements could only have become closer together due to blank element removal - - if (newRelativeY > element.getRelativeY()) - { - element.setRelativeY(newRelativeY); - - element.moveDependantElements(); - } - } - } - } - - - /** - * Resolves an element. - * - * @param element the element - * @param evaluation the evaluation type - */ - protected abstract void resolveElement(JRPrintElement element, byte evaluation) throws JRException; - - protected void performDelayedEvaluation(JRPrintElement element, byte evaluation) - throws JRException - { - evaluateProperties(evaluation); - evaluateStyle(evaluation); - - boolean updateTemplate = false; - - JRStyle printStyle = element.getStyle(); - if (isDelayedStyleEvaluation() || exprStyle != null) - { - if (exprStyle != null) - { - initStyle = exprStyle; - } - - JRStyle elementStyle = initStyle; - if (elementStyle == null) - { - elementStyle = filler.getDefaultStyle(); - } - - if (elementStyle != null) - { - JRStyle evaluatedStyle = conditionalStylesContainer.evaluateConditionalStyle( - elementStyle, evaluation); - // if the evaluated style differs from the existing style - if (evaluatedStyle != printStyle) - { - // set the evaluated style as element style - printStyle = evaluatedStyle; - - updateTemplate = true; - } - } - } - - // set the current element style - this.currentStyle = printStyle; - - resolveElement(element, evaluation); - - if (updateTemplate || providerStyle != null - || delayedEvaluationUpdatesTemplate()) - { - // get/create an element template that corresponds to the - // current style - JRTemplateElement newTemplate = getElementTemplate(); - ((JRTemplatePrintElement) element).updateElementTemplate( - newTemplate); - } - - // reset the current style - this.currentStyle = null; - //this.providerStyle = null; - } - - protected boolean delayedEvaluationUpdatesTemplate() - { - return false; - } - - - /** - * Evaluates an expression. - * - * @param expression the expression - * @param evaluation the evaluation type - * @return the evaluation result - * @throws JRException - */ - public final Object evaluateExpression(JRExpression expression, byte evaluation) throws JRException - { - return expressionEvaluator.evaluate(expression, evaluation); - } - - - /** - * Decides whether the value for this element is repeating. - *

- * Dynamic elements should call {@link #setValueRepeating(boolean) setValueRepeating(boolean)} on - * {@link #evaluate(byte) evaluate(byte)}. Static elements don't have to do anything, this method - * will return true by default. - * - * @return whether the value for this element is repeating - * @see #setValueRepeating(boolean) - */ - protected boolean isValueRepeating() - { - return isValueRepeating; - } - - - /** - * Sets the repeating flag for this element. - *

- * This method should be called by dynamic elements on {@link #evaluate(byte) evaluate(byte)}. - * - * @param isValueRepeating whether the value of the element is repeating - * @see #isValueRepeating() - */ - protected void setValueRepeating(boolean isValueRepeating) - { - this.isValueRepeating = isValueRepeating; - } - - - protected JRFillVariable getVariable(String variableName) - { - return filler.getVariable(variableName); - } - - - protected JRFillField getField(String fieldName) - { - return filler.getField(fieldName); - } - - // default for elements not supporting evaluationTime - protected EvaluationTimeEnum getEvaluationTimeValue() - { - return EvaluationTimeEnum.NOW; - } - - /** - * Resolves an element. - * - * @param element the element - * @param evaluation the evaluation type - * @param evaluationTime the current evaluation time - */ - protected void resolveElement(JRPrintElement element, byte evaluation, JREvaluationTime evaluationTime) throws JRException - { - EvaluationTimeEnum evaluationTimeType = getEvaluationTimeValue(); - switch (evaluationTimeType) - { - case NOW: - break; - case AUTO: - delayedEvaluate((JRRecordedValuesPrintElement) element, evaluationTime, evaluation); - break; - default: - performDelayedEvaluation(element, evaluation); - break; - } - } - - private static class DelayedEvaluations implements Serializable - { - private static final long serialVersionUID = JRConstants.SERIAL_VERSION_UID; - - final Set fields; - final Set variables; - - DelayedEvaluations() - { - fields = new HashSet<>(); - variables = new HashSet<>(); - } - } - - protected void initDelayedEvaluations() - { - if (getEvaluationTimeValue() == EvaluationTimeEnum.AUTO && delayedEvaluationsMap == null) - { - delayedEvaluationsMap = new HashMap<>(); - collectDelayedEvaluations(); - } - } - - protected void collectDelayedEvaluations() - { - if (isDelayedStyleEvaluation()) - { - collectStyleDelayedEvaluations(); - collectStyleProviderDelayedEvaluations(); - } - } - - protected void collectStyleDelayedEvaluations() - { - JRStyle elementStyle = initStyle; - if (elementStyle == null) - { - elementStyle = filler.getDefaultStyle(); - } - - if (elementStyle != null) - { - JRStyle style = elementStyle; - while (style != null) - { - collectDelayedEvaluations(style); - - // proceed to the parent style - style = style.getStyle(); - } - } - } - - protected void collectDelayedEvaluations(JRStyle style) - { - JRConditionalStyle[] conditionalStyles = style.getConditionalStyles(); - // collect delayed evaluations from conditional style expressions - if (conditionalStyles != null && conditionalStyles.length > 0) - { - for (int i = 0; i < conditionalStyles.length; i++) - { - collectDelayedEvaluations( - conditionalStyles[i].getConditionExpression()); - } - } - } - - - protected void collectDelayedEvaluations(JRExpression expression) - { - if (expression != null) - { - JRExpressionChunk[] chunks = expression.getChunks(); - if (chunks != null) - { - for (int i = 0; i < chunks.length; i++) - { - JRExpressionChunk chunk = chunks[i]; - switch (chunk.getType()) - { - case JRExpressionChunk.TYPE_FIELD: - { - DelayedEvaluations delayedEvaluations = getDelayedEvaluations(JREvaluationTime.EVALUATION_TIME_NOW); - delayedEvaluations.fields.add(chunk.getText()); - break; - } - case JRExpressionChunk.TYPE_VARIABLE: - { - JREvaluationTime time = autogetVariableEvaluationTime(chunk.getText()); - DelayedEvaluations delayedEvaluations = getDelayedEvaluations(time); - delayedEvaluations.variables.add(chunk.getText()); - break; - } - default: - } - } - } - } - } - - - protected void collectStyleProviderDelayedEvaluations() - { - if (styleProviders != null && styleProviders.size() > 0) - { - for (StyleProvider styleProvider : styleProviders) - { - String[] fields = styleProvider.getFields(); - if (fields != null && fields.length > 0) - { - DelayedEvaluations delayedEvaluations = getDelayedEvaluations(JREvaluationTime.EVALUATION_TIME_NOW); - for (String field : fields) - { - delayedEvaluations.fields.add(field); - } - } - String[] variables = styleProvider.getVariables(); - if (variables != null && variables.length > 0) - { - for (String variable : variables) - { - JREvaluationTime time = autogetVariableEvaluationTime(variable); - DelayedEvaluations delayedEvaluations = getDelayedEvaluations(time); - delayedEvaluations.variables.add(variable); - } - } - } - } - } - - - private DelayedEvaluations getDelayedEvaluations(JREvaluationTime time) - { - DelayedEvaluations delayedEvaluations = delayedEvaluationsMap.get(time); - if (delayedEvaluations == null) - { - delayedEvaluations = new DelayedEvaluations(); - delayedEvaluationsMap.put(time, delayedEvaluations); - } - return delayedEvaluations; - } - - - private JREvaluationTime autogetVariableEvaluationTime(String variableName) - { - JRFillVariable variable = getVariable(variableName); - JREvaluationTime evaluationTime; - switch (variable.getResetTypeValue()) - { - case REPORT: - evaluationTime = JREvaluationTime.EVALUATION_TIME_REPORT; - break; - case MASTER: - evaluationTime = JREvaluationTime.EVALUATION_TIME_MASTER; - break; - case PAGE: - evaluationTime = JREvaluationTime.EVALUATION_TIME_PAGE; - break; - case COLUMN: - evaluationTime = JREvaluationTime.EVALUATION_TIME_COLUMN; - break; - case GROUP: - evaluationTime = JREvaluationTime.getGroupEvaluationTime(variable.getResetGroup().getName()); - break; - default: - evaluationTime = JREvaluationTime.EVALUATION_TIME_NOW; - break; - } - - if (!evaluationTime.equals(JREvaluationTime.EVALUATION_TIME_NOW) && - band.isNowEvaluationTime(evaluationTime)) - { - evaluationTime = JREvaluationTime.EVALUATION_TIME_NOW; - } - - if (variable.getCalculationValue() == CalculationEnum.SYSTEM && - evaluationTime.equals(JREvaluationTime.EVALUATION_TIME_NOW) && - band.isVariableUsedInReturns(variableName)) - { - evaluationTime = JREvaluationTime.getBandEvaluationTime(band); - } - - return evaluationTime; - } - - - protected void initDelayedEvaluationPrint(JRRecordedValuesPrintElement printElement) throws JRException - { - for (Iterator it = delayedEvaluationsMap.keySet().iterator(); it.hasNext();) - { - JREvaluationTime evaluationTime = it.next(); - if (!evaluationTime.equals(JREvaluationTime.EVALUATION_TIME_NOW)) - { - filler.addBoundElement(this, printElement, evaluationTime); - } - } - - printElement.initRecordedValues(delayedEvaluationsMap.keySet()); - - if (delayedEvaluationsMap.containsKey(JREvaluationTime.EVALUATION_TIME_NOW)) - { - delayedEvaluate(printElement, JREvaluationTime.EVALUATION_TIME_NOW, currentEvaluation); - } - } - - - protected void delayedEvaluate(JRRecordedValuesPrintElement printElement, JREvaluationTime evaluationTime, byte evaluation) throws JRException - { - JRRecordedValues recordedValues = printElement.getRecordedValues(); - if (!recordedValues.lastEvaluationTime()) - { - DelayedEvaluations delayedEvaluations = delayedEvaluationsMap.get(evaluationTime); - - for (Iterator it = delayedEvaluations.fields.iterator(); it.hasNext();) - { - String fieldName = it.next(); - JRFillField field = getField(fieldName); - recordedValues.recordFieldValue(fieldName, field.getValue(evaluation)); - } - - for (Iterator it = delayedEvaluations.variables.iterator(); it.hasNext();) - { - String variableName = it.next(); - JRFillVariable variable = getVariable(variableName); - recordedValues.recordVariableValue(variableName, variable.getValue(evaluation)); - } - } - - recordedValues.doneEvaluation(evaluationTime); - - if (recordedValues.finishedEvaluations()) - { - overwriteWithRecordedValues(recordedValues, evaluation); - performDelayedEvaluation(printElement, evaluation); - restoreValues(recordedValues, evaluation); - printElement.deleteRecordedValues(); - } - } - - - private void overwriteWithRecordedValues(JRRecordedValues recordedValues, byte evaluation) - { - Map fieldValues = recordedValues.getRecordedFieldValues(); - if (fieldValues != null) - { - for (Iterator> it = fieldValues.entrySet().iterator(); it.hasNext();) - { - Map.Entry entry = it.next(); - String fieldName = entry.getKey(); - Object fieldValue = entry.getValue(); - JRFillField field = getField(fieldName); - field.overwriteValue(fieldValue, evaluation); - } - } - - Map variableValues = recordedValues.getRecordedVariableValues(); - if (variableValues != null) - { - for (Iterator> it = variableValues.entrySet().iterator(); it.hasNext();) - { - Map.Entry entry = it.next(); - String variableName = entry.getKey(); - Object variableValue = entry.getValue(); - JRFillVariable variable = getVariable(variableName); - variable.overwriteValue(variableValue, evaluation); - } - } - } - - private void restoreValues(JRRecordedValues recordedValues, byte evaluation) - { - Map fieldValues = recordedValues.getRecordedFieldValues(); - if (fieldValues != null) - { - for (Iterator it = fieldValues.keySet().iterator(); it.hasNext();) - { - String fieldName = it.next(); - JRFillField field = getField(fieldName); - field.restoreValue(evaluation); - } - } - - Map variableValues = recordedValues.getRecordedVariableValues(); - if (variableValues != null) - { - for (Iterator it = variableValues.keySet().iterator(); it.hasNext();) - { - String variableName = it.next(); - JRFillVariable variable = getVariable(variableName); - variable.restoreValue(evaluation); - } - } - } - - /** - * - */ - public void setConditionalStylesContainer(JRFillElementContainer conditionalStylesContainer) - { - this.conditionalStylesContainer = conditionalStylesContainer; - if (fillContainerContext == null) - { - fillContainerContext = conditionalStylesContainer; - } - } - - /** - * - */ - public JRFillElementContainer getConditionalStylesContainer() - { - return conditionalStylesContainer; - } - - @Override - public JRStyle getStyle() - { - // the current style overrides other style objects - if (currentStyle != null) - { - return currentStyle; - } - - JRStyle crtStyle = initStyle; - - boolean isUsingDefaultStyle = false; - - if (crtStyle == null) - { - crtStyle = filler.getDefaultStyle(); - isUsingDefaultStyle = true; - } - - JRStyle evalStyle = crtStyle; - - if (conditionalStylesContainer != null) - { - evalStyle = conditionalStylesContainer.getEvaluatedConditionalStyle(crtStyle); - } - if (isUsingDefaultStyle && evalStyle == crtStyle) - { - evalStyle = null; - } - - return evalStyle; - } - - /** - * - */ - protected JRTemplateElement getTemplate(JRStyle style) - { - return templates.get(style); - } - - /** - * - */ - protected void registerTemplate(JRStyle style, JRTemplateElement template) - { - templates.put(style, template); - } - - - /** - * Indicates whether an element is shrinkable. - *

- * This flag is only effective when {@link #isRemoveLineWhenBlank() isRemoveLineWhenBlank} is also set. - * - * @param shrinkable whether the element is shrinkable - */ - protected final void setShrinkable(boolean shrinkable) - { - this.shrinkable = shrinkable; - } - - - /** - * Called when the stretch height of an element is final so that - * the element can perform any adjustments. - * @deprecated To be removed. - */ - protected void stretchHeightFinal() - { - // nothing - } - - - protected boolean isEvaluateNow() - { - boolean evaluateNow; - switch (getEvaluationTimeValue()) - { - case NOW: - evaluateNow = true; - break; - - case AUTO: - evaluateNow = isAutoEvaluateNow(); - break; - - default: - evaluateNow = false; - break; - } - return evaluateNow; - } - - - protected boolean isAutoEvaluateNow() - { - return delayedEvaluationsMap == null || delayedEvaluationsMap.isEmpty() - || (delayedEvaluationsMap.size() == 1 - && delayedEvaluationsMap.containsKey(JREvaluationTime.EVALUATION_TIME_NOW)); - } - - - protected boolean isEvaluateAuto() - { - return getEvaluationTimeValue() == EvaluationTimeEnum.AUTO && !isAutoEvaluateNow(); - } - - @Override - public String getStyleNameReference() - { - return null; - } - - @Override - public void setStyle(JRStyle style) - { - initStyle = style; - if (conditionalStylesContainer != null) - { - conditionalStylesContainer.collectConditionalStyle(style); - } - } - - @Override - public void setStyleNameReference(String name) - { - throw new UnsupportedOperationException("Style name references not allowed at fill time"); - } - - @Override - public Object clone() - { - throw new UnsupportedOperationException(); - } - - @Override - public Object clone(JRElementGroup parentGroup) - { - throw new UnsupportedOperationException(); - } - - @Override - public JRElement clone(JRElementGroup parentGroup, int y) - { - throw new UnsupportedOperationException(); - } - - @Override - public boolean hasProperties() - { - return mergedProperties != null && mergedProperties.hasProperties(); - } - - @Override - public JRPropertiesMap getPropertiesMap() - { - return mergedProperties; - } - - @Override - public JRPropertiesHolder getParentProperties() - { - //element properties default to report properties - return filler.getMainDataset(); - } - - - @Override - public JRPropertyExpression[] getPropertyExpressions() - { - return propertyExpressions.toArray(new JRPropertyExpression[propertyExpressions.size()]); - } - - protected void transferProperties(JRTemplateElement template) - { - if (staticTransferProperties != null) - { - template.getPropertiesMap().copyOwnProperties(staticTransferProperties); - } - } - - protected void transferProperties(JRPrintElement element) - { - filler.getPropertiesUtil().transferProperties(dynamicProperties, element, - dynamicTransferProperties); - } - - protected JRPropertiesMap getEvaluatedProperties() - { - return mergedProperties; - } - - protected void evaluateProperties(byte evaluation) throws JRException - { - if (propertyExpressions.isEmpty()) - { - dynamicProperties = null; - mergedProperties = staticProperties; - } - else - { - dynamicProperties = new JRPropertiesMap(); - - for (JRPropertyExpression prop : propertyExpressions) - { - String value = (String) evaluateExpression(prop.getValueExpression(), evaluation); - //if (value != null) //for some properties such as data properties in metadata exporters, the null value is significant - { - dynamicProperties.setProperty(prop.getName(), value); - } - } - - mergedProperties = dynamicProperties.cloneProperties(); - mergedProperties.setBaseProperties(staticProperties); - - lookForPartProperty(dynamicProperties); - } - } - - protected void setOriginProvider(JROriginProvider originProvider) - { - this.originProvider = originProvider; - } - - protected JROrigin getElementOrigin() - { - JROrigin elementOrigin = null; - if (originProvider != null) - { - elementOrigin = originProvider.getOrigin(); - } - return elementOrigin; - } - - protected boolean isDelayedStyleEvaluation() - { - return filler.getPropertiesUtil().getBooleanProperty(this, - JRStyle.PROPERTY_EVALUATION_TIME_ENABLED, false); - } - - public JRBaseFiller getFiller() - { - return filler; - } - - - @Override - public boolean hasDynamicProperties() - { - return !propertyExpressions.isEmpty(); - } - - @Override - public boolean hasDynamicProperty(String name) - { - // not called very often for now so doing linear search in array - for (JRPropertyExpression prop : propertyExpressions) - { - if (prop.getName().equals(name)) - { - return true; - } - } - return false; - } - - @Override - public JRPropertiesMap getDynamicProperties() - { - return dynamicProperties; - } - - protected JRStyle getInitStyle() - { - return initStyle; - } - - protected JRElement getParent() - { - return parent; - } - - protected void addDynamicProperty(String name, JRExpression expression) - { - JRDesignPropertyExpression prop = new JRDesignPropertyExpression(); - prop.setName(name); - prop.setValueExpression(expression); - - propertyExpressions.add(prop); - // recomputing - dynamicTransferProperties = findDynamicTransferProperties(); - } - - protected void setExpressionEvaluator(JRFillExpressionEvaluator expressionEvaluator) - { - this.expressionEvaluator = expressionEvaluator; - } - - - /** - * - */ - public static Integer getBookmarkLevel(Object value) throws JRException - { - Integer level = null; - - if (value != null) - { - if (value instanceof Number) - { - level = ((Number)value).intValue(); - } - else - { - try - { - level = Integer.parseInt(value.toString()); - } - catch (NumberFormatException e) - { - //do nothing - } - } - - if (level == null || level < 0) - { - throw - new JRException( - EXCEPTION_MESSAGE_KEY_INVALID_BOOKMARK_LEVEL, - new Object[] {value} - ); - } - } - - return level; - } + /** + * + */ + public static final String EXCEPTION_MESSAGE_KEY_INVALID_BOOKMARK_LEVEL = "fill.anchor.bookmark.level.invalid"; + + + /** + * + */ + protected JRElement parent; + protected List propertyExpressions; + protected List dynamicTransferProperties; + protected JRStyle providerStyle; + protected Map templates = new HashMap<>(); + protected List styleProviders; + + /** + * + */ + protected JRBaseFiller filler; + protected JRFillExpressionEvaluator expressionEvaluator; + + protected JRDefaultStyleProvider defaultStyleProvider; + + /** + * + */ + protected JRGroup printWhenGroupChanges; + protected JRFillElementGroup elementGroup; + + /** + * + */ + protected JRFillBand band; + + protected JROriginProvider originProvider; + + protected PrintElementOriginator printElementOriginator; + + /** + * + */ + private boolean isPrintWhenExpressionNull = true; + private boolean isPrintWhenTrue = true; + private boolean isToPrint = true; + private boolean isReprinted; + private boolean isAlreadyPrinted; + private Collection dependantElements = new ArrayList<>(); + private int relativeY; + private int collapsedHeightAbove; + private int collapsedHeightBelow; + /** + * Keeps total stretch height, including forced stretch after honoring the stretchType property of the element. + */ + private int stretchHeight; + /** + * Keeps the natural stretch height calculated during element prepare. + */ + private int prepareHeight; + + private int x; + private int y; + private int width; + private int height; + + private boolean isValueRepeating; + + protected byte currentEvaluation; + + // used by elements that support evaluationTime=Auto + protected Map delayedEvaluationsMap; + + protected JRFillElementContainer conditionalStylesContainer; + protected FillContainerContext fillContainerContext; + + protected JRStyle initStyle; + protected JRStyle exprStyle; + protected JRStyle currentStyle; + + /** + * Flag indicating whether the element is shrinkable. + * @see #setShrinkable(boolean) + */ + private boolean shrinkable; + + protected JRPropertiesMap staticProperties; + protected JRPropertiesMap staticTransferProperties; + protected JRPropertiesMap dynamicProperties; + protected JRPropertiesMap mergedProperties; + + protected boolean hasDynamicPopulateTemplateStyle; + protected Boolean defaultPopulateTemplateStyle; + + /** + * + * + private JRElement topElementInGroup; + private JRElement bottomElementInGroup; + + + /** + * + */ + protected JRFillElement( + JRBaseFiller filler, + JRElement element, + JRFillObjectFactory factory + ) + { + factory.put(element, this); + + this.parent = element; + this.filler = filler; + this.expressionEvaluator = factory.getExpressionEvaluator(); + this.defaultStyleProvider = factory.getDefaultStyleProvider(); + + printElementOriginator = filler.assignElementId(this); + + /* */ + printWhenGroupChanges = factory.getGroup(element.getPrintWhenGroupChanges()); + elementGroup = (JRFillElementGroup)factory.getVisitResult(element.getElementGroup()); + + x = element.getX(); + y = element.getY(); + width = element.getWidth(); + height = element.getHeight(); + + staticProperties = element.hasProperties() ? element.getPropertiesMap().cloneProperties() : null; + staticTransferProperties = findStaticTransferProperties(); + mergedProperties = staticProperties; + + JRPropertyExpression[] elementPropertyExpressions = element.getPropertyExpressions(); + propertyExpressions = elementPropertyExpressions == null ? new ArrayList<>(0) + : new ArrayList<>(Arrays.asList(elementPropertyExpressions)); + + dynamicTransferProperties = findDynamicTransferProperties(); + + factory.registerDelayedStyleSetter(this, parent); + + initStyleProviders(); + lookForPartProperty(staticProperties); + + hasDynamicPopulateTemplateStyle = hasDynamicProperty(PROPERTY_ELEMENT_TEMPLATE_POPULATE_STYLE); + } + + + protected JRFillElement(JRFillElement element, JRFillCloneFactory factory) + { + factory.put(element, this); + + this.parent = element.parent; + this.filler = element.filler; + this.expressionEvaluator = element.expressionEvaluator; + this.defaultStyleProvider = element.defaultStyleProvider; + this.originProvider = element.originProvider; + + printElementOriginator = element.printElementOriginator; + + /* */ + printWhenGroupChanges = element.printWhenGroupChanges; + elementGroup = (JRFillElementGroup) factory.getClone((JRFillElementGroup) element.getElementGroup()); + + x = element.getX(); + y = element.getY(); + width = element.getWidth(); + height = element.getHeight(); + + templates = element.templates; + + initStyle = element.initStyle; + + shrinkable = element.shrinkable; + + staticProperties = element.staticProperties == null ? null : element.staticProperties.cloneProperties(); + staticTransferProperties = element.staticTransferProperties; + mergedProperties = staticProperties; + this.propertyExpressions = new ArrayList<>(element.propertyExpressions); + this.dynamicTransferProperties = element.dynamicTransferProperties; + + // we need a style provider context for this element instance + initStyleProviders(); + + this.hasDynamicPopulateTemplateStyle = element.hasDynamicPopulateTemplateStyle; + this.defaultPopulateTemplateStyle = element.defaultPopulateTemplateStyle; + } + + private JRPropertiesMap findStaticTransferProperties() + { + if (staticProperties == null) + { + return null; + } + + String[] propertyNames = staticProperties.getPropertyNames(); + List prefixes = filler.getPrintTransferPropertyPrefixes(); + JRPropertiesMap transferProperties = new JRPropertiesMap(); + for (int i = 0; i < propertyNames.length; i++) + { + for (String prefix : prefixes) + { + String prop = propertyNames[i]; + if (prop.startsWith(prefix)) + { + transferProperties.setProperty(prop, staticProperties.getProperty(prop)); + break; + } + } + } + return transferProperties.isEmpty() ? null : transferProperties; + } + + private List findDynamicTransferProperties() + { + if (propertyExpressions.isEmpty()) + { + return null; + } + + List prefixes = filler.getPrintTransferPropertyPrefixes(); + List transferProperties = new ArrayList<>(propertyExpressions.size()); + for (JRPropertyExpression propertyExpression : propertyExpressions) + { + String propertyName = propertyExpression.getName(); + for (String prefix : prefixes) + { + if (propertyName.startsWith(prefix)) + { + transferProperties.add(propertyName); + break; + } + } + } + return transferProperties; + } + + private void lookForPartProperty(JRPropertiesMap properties) + { + if (properties != null && properties.getProperty(PrintPart.ELEMENT_PROPERTY_PART_NAME) != null) + { + filler.getFillContext().setDetectParts(true); + } + } + + @Override + public JRDefaultStyleProvider getDefaultStyleProvider() + { + return defaultStyleProvider; + } + + /** + * + */ + protected StyleResolver getStyleResolver() + { + return getDefaultStyleProvider().getStyleResolver(); + } + + @Override + public UUID getUUID() + { + return parent.getUUID(); + } + + @Override + public String getKey() + { + return parent.getKey(); + } + + @Override + public PositionTypeEnum getPositionTypeValue() + { + return parent.getPositionTypeValue();//FIXME optimize this by consolidating style properties + } + + @Override + public void setPositionType(PositionTypeEnum positionType) + { + throw new UnsupportedOperationException(); + } + + @Override + public StretchTypeEnum getStretchTypeValue() + { + return parent.getStretchTypeValue(); + } + + @Override + public void setStretchType(StretchTypeEnum stretchType) + { + throw new UnsupportedOperationException(); + } + + @Override + public boolean isPrintRepeatedValues() + { + return parent.isPrintRepeatedValues(); + } + + @Override + public void setPrintRepeatedValues(boolean isPrintRepeatedValues) + { + } + + @Override + public ModeEnum getModeValue() + { + return getStyleResolver().getMode(this, ModeEnum.OPAQUE); + } + + @Override + public ModeEnum getOwnModeValue() + { + return providerStyle == null || providerStyle.getOwnModeValue() == null ? parent.getOwnModeValue() : providerStyle.getOwnModeValue(); + } + + @Override + public void setMode(ModeEnum modeValue) + { + } + + @Override + public int getX() + { + return x; + } + + @Override + public void setX(int x) + { + this.x = x; + } + + /** + * + */ + public void setY(int y) + { + this.y = y; + } + + @Override + public int getY() + { + return y; + } + + @Override + public int getWidth() + { + return width; + } + + @Override + public void setWidth(int width) + { + this.width = width; + } + + /** + * + */ + public void setHeight(int height) + { + this.height = height; + } + + @Override + public int getHeight() + { + return height; + } + + @Override + public boolean isRemoveLineWhenBlank() + { + return parent.isRemoveLineWhenBlank(); + } + + @Override + public void setRemoveLineWhenBlank(boolean isRemoveLine) + { + } + + @Override + public boolean isPrintInFirstWholeBand() + { + return parent.isPrintInFirstWholeBand(); + } + + @Override + public void setPrintInFirstWholeBand(boolean isPrint) + { + } + + @Override + public boolean isPrintWhenDetailOverflows() + { + return parent.isPrintWhenDetailOverflows(); + } + + @Override + public void setPrintWhenDetailOverflows(boolean isPrint) + { + } + + @Override + public Color getForecolor() + { + return getStyleResolver().getForecolor(this); + } + + @Override + public Color getOwnForecolor() + { + return providerStyle == null || providerStyle.getOwnForecolor() == null ? parent.getOwnForecolor() : providerStyle.getOwnForecolor(); + } + + @Override + public void setForecolor(Color forecolor) + { + } + + @Override + public Color getBackcolor() + { + return getStyleResolver().getBackcolor(this); + } + + @Override + public Color getOwnBackcolor() + { + return providerStyle == null || providerStyle.getOwnBackcolor() == null ? parent.getOwnBackcolor() : providerStyle.getOwnBackcolor(); + } + + @Override + public void setBackcolor(Color backcolor) + { + } + + @Override + public JRExpression getStyleExpression() + { + return parent.getStyleExpression(); + } + + @Override + public JRExpression getPrintWhenExpression() + { + return parent.getPrintWhenExpression(); + } + + @Override + public JRGroup getPrintWhenGroupChanges() + { + return printWhenGroupChanges; + } + + @Override + public JRElementGroup getElementGroup() + { + return elementGroup; + } + + /** + * + */ + protected boolean isPrintWhenExpressionNull() + { + return isPrintWhenExpressionNull; + } + + /** + * + */ + protected void setPrintWhenExpressionNull(boolean isPrintWhenExpressionNull) + { + this.isPrintWhenExpressionNull = isPrintWhenExpressionNull; + } + + /** + * + */ + protected boolean isPrintWhenTrue() + { + return isPrintWhenTrue; + } + + /** + * + */ + protected void setPrintWhenTrue(boolean isPrintWhenTrue) + { + this.isPrintWhenTrue = isPrintWhenTrue; + } + + /** + * + */ + public boolean isToPrint() + { + return isToPrint; + } + + /** + * + */ + protected void setToPrint(boolean isToPrint) + { + this.isToPrint = isToPrint; + } + + /** + * + */ + protected boolean isReprinted() + { + return isReprinted; + } + + /** + * + */ + protected void setReprinted(boolean isReprinted) + { + this.isReprinted = isReprinted; + } + + /** + * + */ + public boolean isAlreadyPrinted() + { + return isAlreadyPrinted; + } + + /** + * + */ + public void setAlreadyPrinted(boolean isAlreadyPrinted) + { + this.isAlreadyPrinted = isAlreadyPrinted; + } + + /** + * + */ + protected JRElement[] getGroupElements() + { + JRElement[] groupElements = null; + + if (elementGroup != null) + { + groupElements = elementGroup.getElements(); + } + + return groupElements; + } + + /** + * + */ + protected Collection getDependantElements() + { + return dependantElements; + } + + /** + * + */ + protected void addDependantElement(JRFillElement element) + { + dependantElements.add(element); + } + + /** + * + */ + protected int getRelativeY() + { + return relativeY; + } + + /** + * + */ + protected void setRelativeY(int relativeY) + { + this.relativeY = relativeY; + } + + /** + * + */ + protected int getCollapsedHeightAbove() + { + return collapsedHeightAbove; + } + + /** + * + */ + protected void setCollapsedHeightAbove(int collapsedHeightAbove) + { + this.collapsedHeightAbove = collapsedHeightAbove; + } + + /** + * + */ + protected int getCollapsedHeightBelow() + { + return collapsedHeightBelow; + } + + /** + * + */ + protected void setCollapsedHeightBelow(int collapsedHeightBelow) + { + this.collapsedHeightBelow = collapsedHeightBelow; + } + + /** + * + */ + public int getStretchHeight() + { + return stretchHeight; + } + + /** + * + */ + protected void setStretchHeight(int stretchHeight) + { + if (stretchHeight > getHeight() || (shrinkable && isRemoveLineWhenBlank())) + { + this.stretchHeight = stretchHeight; + } + else + { + this.stretchHeight = getHeight(); + } + } + + /** + * + */ + public int getPrepareHeight() + { + return prepareHeight; + } + + /** + * Element height is calculated in two phases. + * First, the element stretches on its own during prepare, to accommodate all its content. + * This is the natural stretch and we keep track of the calculated height in prepareHeight. + * Secondly, the element stretches further in accordance with its stretchType property. + * This forced stretch occurs at a later time and the amount of stretch is kept in stretchHeight. + */ + protected void setPrepareHeight(int prepareHeight) + { + this.prepareHeight = prepareHeight; + + setStretchHeight(prepareHeight); + } + + /** + * + */ + protected JRFillBand getBand() + { + return band; + } + + /** + * + */ + protected void setBand(JRFillBand band) + { + this.band = band; + + if (this.originProvider == null) + { + setOriginProvider(band); + } + } + + + /** + * + */ + protected void initStyleProviders() + { + List styleProviderFactories = filler.getJasperReportsContext().getExtensions(StyleProviderFactory.class); + if (styleProviderFactories != null && styleProviderFactories.size() > 0) + { + FillStyleProviderContext styleProviderContext = new FillStyleProviderContext(this); + for (StyleProviderFactory styleProviderFactory : styleProviderFactories) + { + StyleProvider styleProvider = styleProviderFactory.getStyleProvider(styleProviderContext, filler.getJasperReportsContext()); + if (styleProvider != null) + { + if (styleProviders == null) + { + styleProviders = new ArrayList<>(); + } + styleProviders.add(styleProvider); + } + } + } + } + + + /** + * + */ + protected void reset() + { + relativeY = y; + collapsedHeightAbove = 0; + collapsedHeightBelow = 0; + stretchHeight = height; + prepareHeight = height; + + if (elementGroup != null) + { + elementGroup.reset(); + } + } + + protected void setCurrentEvaluation(byte evaluation) + { + currentEvaluation = evaluation; + } + + /** + * + */ + protected abstract void evaluate( + byte evaluation + ) throws JRException; + + + /** + * + */ + protected void evaluateStyle( + byte evaluation + ) throws JRException + { + exprStyle = null; + + JRExpression styleExpression = getStyleExpression(); + if (styleExpression != null) + { + String styleName = (String)evaluateExpression(styleExpression, evaluation); + if (styleName != null) + { + exprStyle = getFiller().factory.stylesMap.getStyle(styleName); + if (exprStyle == null) + { + throw + new JRRuntimeException( + JRFillObjectFactory.EXCEPTION_MESSAGE_KEY_STYLE_NOT_FOUND, + new Object[]{styleName} + ); + } + else + { + conditionalStylesContainer.collectConditionalStyle(exprStyle); + } + } + } + + if (exprStyle != null && isEvaluateNow()) + { + conditionalStylesContainer.evaluateConditionalStyle(exprStyle, evaluation); + initStyle = exprStyle; + } + + providerStyle = null; + + if (styleProviders != null && styleProviders.size() > 0) + { + for (StyleProvider styleProvider : styleProviders) + { + JRStyle style = styleProvider.getStyle(evaluation); + if (style != null) + { + if (providerStyle == null) + { + providerStyle = new JRBaseStyle(); + } + StyleUtil.appendStyle(providerStyle, style); + } + } + } + } + + protected TimeZone getTimeZone() + { + return filler.getTimeZone(); + } + + /** + * + */ + protected void evaluatePrintWhenExpression( + byte evaluation + ) throws JRException + { + boolean isExprNull = true; + boolean isExprTrue = false; + + JRExpression expression = getPrintWhenExpression(); + if (expression != null) + { + isExprNull = false; + Boolean printWhenExpressionValue = (Boolean) evaluateExpression(expression, evaluation); + if (printWhenExpressionValue == null) + { + isExprTrue = false; + } + else + { + isExprTrue = printWhenExpressionValue; + } + } + + setPrintWhenExpressionNull(isExprNull); + setPrintWhenTrue(isExprTrue); + } + + + /** + * + */ + protected abstract void rewind() throws JRException; + + + /** + * + */ + protected abstract JRPrintElement fill() throws JRException; + + protected JRTemplateElement getElementTemplate() + { + JRTemplateElement template = null; + JRStyle style = null; + + if (providerStyle == null) + { + // no style provider has been used so we can use cache template per style below + style = getStyle(); + template = getTemplate(style); + } + + if (template == null) + { + template = createElementTemplate(); + transferProperties(template); + + if (toPopulateTemplateStyle()) + { + template.populateStyle(); + } + + // deduplicate to previously created identical objects + template = filler.fillContext.deduplicate(template); + + if (providerStyle == null) + { + registerTemplate(style, template); + } + } + return template; + } + + protected abstract JRTemplateElement createElementTemplate(); + + protected boolean toPopulateTemplateStyle() + { + if (defaultPopulateTemplateStyle == null) + { + defaultPopulateTemplateStyle = filler.getPropertiesUtil().getBooleanProperty( + PROPERTY_ELEMENT_TEMPLATE_POPULATE_STYLE, false, + parent, filler.getMainDataset()); + } + + boolean populate = defaultPopulateTemplateStyle; + if (hasDynamicPopulateTemplateStyle) + { + String populateProp = getDynamicProperties().getProperty( + PROPERTY_ELEMENT_TEMPLATE_POPULATE_STYLE); + if (populateProp != null) + { + populate = JRPropertiesUtil.asBoolean(populateProp); + } + } + return populate; + } + + /** + * + */ + protected boolean prepare( + int availableHeight, + boolean isOverflow + ) throws JRException + { + if ( + isPrintWhenExpressionNull() || + ( !isPrintWhenExpressionNull() && + isPrintWhenTrue() ) + ) + { + setToPrint(true); + } + else + { + setToPrint(false); + } + + setReprinted(false); + + return false; + } + + + /** + * @deprecated To be removed. + */ + protected void _stretchElement(int bandStretch) + { + switch (getStretchTypeValue()) + { + case RELATIVE_TO_BAND_HEIGHT : + case CONTAINER_HEIGHT : + case CONTAINER_BOTTOM : + { + _stretchElementToHeight(getHeight() + bandStretch); + break; + } + case RELATIVE_TO_TALLEST_OBJECT : + case ELEMENT_GROUP_HEIGHT : + case ELEMENT_GROUP_BOTTOM : + { + if (elementGroup != null) + { + //setStretchHeight(getHeight() + getStretchHeightDiff()); + _stretchElementToHeight(getHeight() + elementGroup.getStretchHeightDiff()); + } + + break; + } + case NO_STRETCH : + default : + { + break; + } + } + } + + /** + * @deprecated To be removed. + */ + protected void _stretchElementToHeight(int stretchHeight) + { + if (stretchHeight > getStretchHeight()) + { + setStretchHeight(stretchHeight); + } + } + + + /** + * + */ + @SuppressWarnings("deprecation") + protected boolean stretchElement(int containerStretch) + { + boolean applied = false; + switch (getStretchTypeValue()) + { + case RELATIVE_TO_BAND_HEIGHT : + case CONTAINER_HEIGHT : + case CONTAINER_BOTTOM : + { + applied = stretchElementToContainer(containerStretch); + break; + } + case RELATIVE_TO_TALLEST_OBJECT : + case ELEMENT_GROUP_HEIGHT : + case ELEMENT_GROUP_BOTTOM : + { + applied = stretchElementToElementGroup(); + break; + } + case NO_STRETCH : + default : + { + break; + } + } + return applied; + } + + + /** + * + */ + @SuppressWarnings("deprecation") + protected boolean stretchElementToContainer(int containerStretch)//TODO subtract firstY? + { + boolean applied = false; + switch (getStretchTypeValue()) + { + case RELATIVE_TO_BAND_HEIGHT : + case CONTAINER_HEIGHT : + { + applied = stretchElementToHeight(getHeight() + containerStretch); + break; + } + case CONTAINER_BOTTOM : + { + applied = stretchElementToHeight(getY() - getRelativeY() + getHeight() + containerStretch); + break; + } + default : + } + return applied; + } + + + /** + * + */ + @SuppressWarnings("deprecation") + protected boolean stretchElementToElementGroup() + { + boolean applied = false; + if (elementGroup != null) + { + switch (getStretchTypeValue()) + { + case RELATIVE_TO_TALLEST_OBJECT : + case ELEMENT_GROUP_HEIGHT : + { + applied = stretchElementToHeight(getHeight() + elementGroup.getStretchHeightDiff()); + break; + } + case ELEMENT_GROUP_BOTTOM : + { + applied = stretchElementToHeight(getY() - getRelativeY() + getHeight() + elementGroup.getStretchHeightDiff()); + break; + } + default : + } + } + return applied; + } + + + /** + * This method returns a boolean signaling if any stretch change occurred. + * It does not say which amount of stretch was applied, but that is OK, because the only place where this is checked + * is during frame cascading stretch, where the stretchHeight field of the frame (set here) is used directly. + */ + protected boolean stretchElementToHeight(int stretchHeight) + { + // cannot force the element to shrink below its natural stretch calculated during prepare; + // such situation could occur when the container breaks and overflows + boolean applied = false; + if (stretchHeight > getPrepareHeight()) + { + // any new stretchHeight that is greater than element's natural growth is fine + setStretchHeight(stretchHeight); + applied = true; + } + return applied; + } + + + /** + * @deprecated To be removed. + */ + protected void _moveDependantElements() + { + Collection elements = getDependantElements(); + if (elements != null && elements.size() > 0) + { + for (JRFillElement element : elements) + { + int newRelativeY = + getRelativeY() + getStretchHeight() //pusher element current bottom edge + + (element.getY() - (getY() + getHeight())); //design time distance between elements; difference between float element top edge and pusher element bottom edge + + if (newRelativeY > element.getRelativeY()) + { + element.setRelativeY(newRelativeY); + } + } + } + } + + + /** + * + */ + protected void moveDependantElements() { + Collection elements = getDependantElements(); + if (elements != null && !elements.isEmpty()) { + int offset = getRelativeY() + getStretchHeight() - getY() - getHeight() + getCollapsedHeightAbove(); + for (JRFillElement element : elements) { + int newRelativeY = offset + element.getY() - element.getCollapsedHeightAbove(); + if (newRelativeY > element.getRelativeY()) { + element.setRelativeY(newRelativeY); + } + } + } + } + + + /** + * Resolves an element. + * + * @param element the element + * @param evaluation the evaluation type + */ + protected abstract void resolveElement(JRPrintElement element, byte evaluation) throws JRException; + + protected void performDelayedEvaluation(JRPrintElement element, byte evaluation) + throws JRException + { + evaluateProperties(evaluation); + evaluateStyle(evaluation); + + boolean updateTemplate = false; + + JRStyle printStyle = element.getStyle(); + if (isDelayedStyleEvaluation() || exprStyle != null) + { + if (exprStyle != null) + { + initStyle = exprStyle; + } + + JRStyle elementStyle = initStyle; + if (elementStyle == null) + { + elementStyle = filler.getDefaultStyle(); + } + + if (elementStyle != null) + { + JRStyle evaluatedStyle = conditionalStylesContainer.evaluateConditionalStyle( + elementStyle, evaluation); + // if the evaluated style differs from the existing style + if (evaluatedStyle != printStyle) + { + // set the evaluated style as element style + printStyle = evaluatedStyle; + + updateTemplate = true; + } + } + } + + // set the current element style + this.currentStyle = printStyle; + + resolveElement(element, evaluation); + + if (updateTemplate || providerStyle != null + || delayedEvaluationUpdatesTemplate()) + { + // get/create an element template that corresponds to the + // current style + JRTemplateElement newTemplate = getElementTemplate(); + ((JRTemplatePrintElement) element).updateElementTemplate( + newTemplate); + } + + // reset the current style + this.currentStyle = null; + //this.providerStyle = null; + } + + protected boolean delayedEvaluationUpdatesTemplate() + { + return false; + } + + + /** + * Evaluates an expression. + * + * @param expression the expression + * @param evaluation the evaluation type + * @return the evaluation result + * @throws JRException + */ + public final Object evaluateExpression(JRExpression expression, byte evaluation) throws JRException + { + return expressionEvaluator.evaluate(expression, evaluation); + } + + + /** + * Decides whether the value for this element is repeating. + *

+ * Dynamic elements should call {@link #setValueRepeating(boolean) setValueRepeating(boolean)} on + * {@link #evaluate(byte) evaluate(byte)}. Static elements don't have to do anything, this method + * will return true by default. + * + * @return whether the value for this element is repeating + * @see #setValueRepeating(boolean) + */ + protected boolean isValueRepeating() + { + return isValueRepeating; + } + + + /** + * Sets the repeating flag for this element. + *

+ * This method should be called by dynamic elements on {@link #evaluate(byte) evaluate(byte)}. + * + * @param isValueRepeating whether the value of the element is repeating + * @see #isValueRepeating() + */ + protected void setValueRepeating(boolean isValueRepeating) + { + this.isValueRepeating = isValueRepeating; + } + + + protected JRFillVariable getVariable(String variableName) + { + return filler.getVariable(variableName); + } + + + protected JRFillField getField(String fieldName) + { + return filler.getField(fieldName); + } + + // default for elements not supporting evaluationTime + protected EvaluationTimeEnum getEvaluationTimeValue() + { + return EvaluationTimeEnum.NOW; + } + + /** + * Resolves an element. + * + * @param element the element + * @param evaluation the evaluation type + * @param evaluationTime the current evaluation time + */ + protected void resolveElement(JRPrintElement element, byte evaluation, JREvaluationTime evaluationTime) throws JRException + { + EvaluationTimeEnum evaluationTimeType = getEvaluationTimeValue(); + switch (evaluationTimeType) + { + case NOW: + break; + case AUTO: + delayedEvaluate((JRRecordedValuesPrintElement) element, evaluationTime, evaluation); + break; + default: + performDelayedEvaluation(element, evaluation); + break; + } + } + + private static class DelayedEvaluations implements Serializable + { + private static final long serialVersionUID = JRConstants.SERIAL_VERSION_UID; + + final Set fields; + final Set variables; + + DelayedEvaluations() + { + fields = new HashSet<>(); + variables = new HashSet<>(); + } + } + + protected void initDelayedEvaluations() + { + if (getEvaluationTimeValue() == EvaluationTimeEnum.AUTO && delayedEvaluationsMap == null) + { + delayedEvaluationsMap = new HashMap<>(); + collectDelayedEvaluations(); + } + } + + protected void collectDelayedEvaluations() + { + if (isDelayedStyleEvaluation()) + { + collectStyleDelayedEvaluations(); + collectStyleProviderDelayedEvaluations(); + } + } + + protected void collectStyleDelayedEvaluations() + { + JRStyle elementStyle = initStyle; + if (elementStyle == null) + { + elementStyle = filler.getDefaultStyle(); + } + + if (elementStyle != null) + { + JRStyle style = elementStyle; + while (style != null) + { + collectDelayedEvaluations(style); + + // proceed to the parent style + style = style.getStyle(); + } + } + } + + protected void collectDelayedEvaluations(JRStyle style) + { + JRConditionalStyle[] conditionalStyles = style.getConditionalStyles(); + // collect delayed evaluations from conditional style expressions + if (conditionalStyles != null && conditionalStyles.length > 0) + { + for (int i = 0; i < conditionalStyles.length; i++) + { + collectDelayedEvaluations( + conditionalStyles[i].getConditionExpression()); + } + } + } + + + protected void collectDelayedEvaluations(JRExpression expression) + { + if (expression != null) + { + JRExpressionChunk[] chunks = expression.getChunks(); + if (chunks != null) + { + for (int i = 0; i < chunks.length; i++) + { + JRExpressionChunk chunk = chunks[i]; + switch (chunk.getType()) + { + case JRExpressionChunk.TYPE_FIELD: + { + DelayedEvaluations delayedEvaluations = getDelayedEvaluations(JREvaluationTime.EVALUATION_TIME_NOW); + delayedEvaluations.fields.add(chunk.getText()); + break; + } + case JRExpressionChunk.TYPE_VARIABLE: + { + JREvaluationTime time = autogetVariableEvaluationTime(chunk.getText()); + DelayedEvaluations delayedEvaluations = getDelayedEvaluations(time); + delayedEvaluations.variables.add(chunk.getText()); + break; + } + default: + } + } + } + } + } + + + protected void collectStyleProviderDelayedEvaluations() + { + if (styleProviders != null && styleProviders.size() > 0) + { + for (StyleProvider styleProvider : styleProviders) + { + String[] fields = styleProvider.getFields(); + if (fields != null && fields.length > 0) + { + DelayedEvaluations delayedEvaluations = getDelayedEvaluations(JREvaluationTime.EVALUATION_TIME_NOW); + for (String field : fields) + { + delayedEvaluations.fields.add(field); + } + } + String[] variables = styleProvider.getVariables(); + if (variables != null && variables.length > 0) + { + for (String variable : variables) + { + JREvaluationTime time = autogetVariableEvaluationTime(variable); + DelayedEvaluations delayedEvaluations = getDelayedEvaluations(time); + delayedEvaluations.variables.add(variable); + } + } + } + } + } + + + private DelayedEvaluations getDelayedEvaluations(JREvaluationTime time) + { + DelayedEvaluations delayedEvaluations = delayedEvaluationsMap.get(time); + if (delayedEvaluations == null) + { + delayedEvaluations = new DelayedEvaluations(); + delayedEvaluationsMap.put(time, delayedEvaluations); + } + return delayedEvaluations; + } + + + private JREvaluationTime autogetVariableEvaluationTime(String variableName) + { + JRFillVariable variable = getVariable(variableName); + JREvaluationTime evaluationTime; + switch (variable.getResetTypeValue()) + { + case REPORT: + evaluationTime = JREvaluationTime.EVALUATION_TIME_REPORT; + break; + case MASTER: + evaluationTime = JREvaluationTime.EVALUATION_TIME_MASTER; + break; + case PAGE: + evaluationTime = JREvaluationTime.EVALUATION_TIME_PAGE; + break; + case COLUMN: + evaluationTime = JREvaluationTime.EVALUATION_TIME_COLUMN; + break; + case GROUP: + evaluationTime = JREvaluationTime.getGroupEvaluationTime(variable.getResetGroup().getName()); + break; + default: + evaluationTime = JREvaluationTime.EVALUATION_TIME_NOW; + break; + } + + if (!evaluationTime.equals(JREvaluationTime.EVALUATION_TIME_NOW) && + band.isNowEvaluationTime(evaluationTime)) + { + evaluationTime = JREvaluationTime.EVALUATION_TIME_NOW; + } + + if (variable.getCalculationValue() == CalculationEnum.SYSTEM && + evaluationTime.equals(JREvaluationTime.EVALUATION_TIME_NOW) && + band.isVariableUsedInReturns(variableName)) + { + evaluationTime = JREvaluationTime.getBandEvaluationTime(band); + } + + return evaluationTime; + } + + + protected void initDelayedEvaluationPrint(JRRecordedValuesPrintElement printElement) throws JRException + { + for (Iterator it = delayedEvaluationsMap.keySet().iterator(); it.hasNext();) + { + JREvaluationTime evaluationTime = it.next(); + if (!evaluationTime.equals(JREvaluationTime.EVALUATION_TIME_NOW)) + { + filler.addBoundElement(this, printElement, evaluationTime); + } + } + + printElement.initRecordedValues(delayedEvaluationsMap.keySet()); + + if (delayedEvaluationsMap.containsKey(JREvaluationTime.EVALUATION_TIME_NOW)) + { + delayedEvaluate(printElement, JREvaluationTime.EVALUATION_TIME_NOW, currentEvaluation); + } + } + + + protected void delayedEvaluate(JRRecordedValuesPrintElement printElement, JREvaluationTime evaluationTime, byte evaluation) throws JRException + { + JRRecordedValues recordedValues = printElement.getRecordedValues(); + if (!recordedValues.lastEvaluationTime()) + { + DelayedEvaluations delayedEvaluations = delayedEvaluationsMap.get(evaluationTime); + + for (Iterator it = delayedEvaluations.fields.iterator(); it.hasNext();) + { + String fieldName = it.next(); + JRFillField field = getField(fieldName); + recordedValues.recordFieldValue(fieldName, field.getValue(evaluation)); + } + + for (Iterator it = delayedEvaluations.variables.iterator(); it.hasNext();) + { + String variableName = it.next(); + JRFillVariable variable = getVariable(variableName); + recordedValues.recordVariableValue(variableName, variable.getValue(evaluation)); + } + } + + recordedValues.doneEvaluation(evaluationTime); + + if (recordedValues.finishedEvaluations()) + { + overwriteWithRecordedValues(recordedValues, evaluation); + performDelayedEvaluation(printElement, evaluation); + restoreValues(recordedValues, evaluation); + printElement.deleteRecordedValues(); + } + } + + + private void overwriteWithRecordedValues(JRRecordedValues recordedValues, byte evaluation) + { + Map fieldValues = recordedValues.getRecordedFieldValues(); + if (fieldValues != null) + { + for (Iterator> it = fieldValues.entrySet().iterator(); it.hasNext();) + { + Map.Entry entry = it.next(); + String fieldName = entry.getKey(); + Object fieldValue = entry.getValue(); + JRFillField field = getField(fieldName); + field.overwriteValue(fieldValue, evaluation); + } + } + + Map variableValues = recordedValues.getRecordedVariableValues(); + if (variableValues != null) + { + for (Iterator> it = variableValues.entrySet().iterator(); it.hasNext();) + { + Map.Entry entry = it.next(); + String variableName = entry.getKey(); + Object variableValue = entry.getValue(); + JRFillVariable variable = getVariable(variableName); + variable.overwriteValue(variableValue, evaluation); + } + } + } + + private void restoreValues(JRRecordedValues recordedValues, byte evaluation) + { + Map fieldValues = recordedValues.getRecordedFieldValues(); + if (fieldValues != null) + { + for (Iterator it = fieldValues.keySet().iterator(); it.hasNext();) + { + String fieldName = it.next(); + JRFillField field = getField(fieldName); + field.restoreValue(evaluation); + } + } + + Map variableValues = recordedValues.getRecordedVariableValues(); + if (variableValues != null) + { + for (Iterator it = variableValues.keySet().iterator(); it.hasNext();) + { + String variableName = it.next(); + JRFillVariable variable = getVariable(variableName); + variable.restoreValue(evaluation); + } + } + } + + /** + * + */ + public void setConditionalStylesContainer(JRFillElementContainer conditionalStylesContainer) + { + this.conditionalStylesContainer = conditionalStylesContainer; + if (fillContainerContext == null) + { + fillContainerContext = conditionalStylesContainer; + } + } + + /** + * + */ + public JRFillElementContainer getConditionalStylesContainer() + { + return conditionalStylesContainer; + } + + @Override + public JRStyle getStyle() + { + // the current style overrides other style objects + if (currentStyle != null) + { + return currentStyle; + } + + JRStyle crtStyle = initStyle; + + boolean isUsingDefaultStyle = false; + + if (crtStyle == null) + { + crtStyle = filler.getDefaultStyle(); + isUsingDefaultStyle = true; + } + + JRStyle evalStyle = crtStyle; + + if (conditionalStylesContainer != null) + { + evalStyle = conditionalStylesContainer.getEvaluatedConditionalStyle(crtStyle); + } + if (isUsingDefaultStyle && evalStyle == crtStyle) + { + evalStyle = null; + } + + return evalStyle; + } + + /** + * + */ + protected JRTemplateElement getTemplate(JRStyle style) + { + return templates.get(style); + } + + /** + * + */ + protected void registerTemplate(JRStyle style, JRTemplateElement template) + { + templates.put(style, template); + } + + + /** + * Indicates whether an element is shrinkable. + *

+ * This flag is only effective when {@link #isRemoveLineWhenBlank() isRemoveLineWhenBlank} is also set. + * + * @param shrinkable whether the element is shrinkable + */ + protected final void setShrinkable(boolean shrinkable) + { + this.shrinkable = shrinkable; + } + + + /** + * Called when the stretch height of an element is final so that + * the element can perform any adjustments. + * @deprecated To be removed. + */ + protected void stretchHeightFinal() + { + // nothing + } + + + protected boolean isEvaluateNow() + { + boolean evaluateNow; + switch (getEvaluationTimeValue()) + { + case NOW: + evaluateNow = true; + break; + + case AUTO: + evaluateNow = isAutoEvaluateNow(); + break; + + default: + evaluateNow = false; + break; + } + return evaluateNow; + } + + + protected boolean isAutoEvaluateNow() + { + return delayedEvaluationsMap == null || delayedEvaluationsMap.isEmpty() + || (delayedEvaluationsMap.size() == 1 + && delayedEvaluationsMap.containsKey(JREvaluationTime.EVALUATION_TIME_NOW)); + } + + + protected boolean isEvaluateAuto() + { + return getEvaluationTimeValue() == EvaluationTimeEnum.AUTO && !isAutoEvaluateNow(); + } + + @Override + public String getStyleNameReference() + { + return null; + } + + @Override + public void setStyle(JRStyle style) + { + initStyle = style; + if (conditionalStylesContainer != null) + { + conditionalStylesContainer.collectConditionalStyle(style); + } + } + + @Override + public void setStyleNameReference(String name) + { + throw new UnsupportedOperationException("Style name references not allowed at fill time"); + } + + @Override + public Object clone() + { + throw new UnsupportedOperationException(); + } + + @Override + public Object clone(JRElementGroup parentGroup) + { + throw new UnsupportedOperationException(); + } + + @Override + public JRElement clone(JRElementGroup parentGroup, int y) + { + throw new UnsupportedOperationException(); + } + + @Override + public boolean hasProperties() + { + return mergedProperties != null && mergedProperties.hasProperties(); + } + + @Override + public JRPropertiesMap getPropertiesMap() + { + return mergedProperties; + } + + @Override + public JRPropertiesHolder getParentProperties() + { + //element properties default to report properties + return filler.getMainDataset(); + } + + + @Override + public JRPropertyExpression[] getPropertyExpressions() + { + return propertyExpressions.toArray(new JRPropertyExpression[propertyExpressions.size()]); + } + + protected void transferProperties(JRTemplateElement template) + { + if (staticTransferProperties != null) + { + template.getPropertiesMap().copyOwnProperties(staticTransferProperties); + } + } + + protected void transferProperties(JRPrintElement element) + { + filler.getPropertiesUtil().transferProperties(dynamicProperties, element, + dynamicTransferProperties); + } + + protected JRPropertiesMap getEvaluatedProperties() + { + return mergedProperties; + } + + protected void evaluateProperties(byte evaluation) throws JRException + { + if (propertyExpressions.isEmpty()) + { + dynamicProperties = null; + mergedProperties = staticProperties; + } + else + { + dynamicProperties = new JRPropertiesMap(); + + for (JRPropertyExpression prop : propertyExpressions) + { + String value = (String) evaluateExpression(prop.getValueExpression(), evaluation); + //if (value != null) //for some properties such as data properties in metadata exporters, the null value is significant + { + dynamicProperties.setProperty(prop.getName(), value); + } + } + + mergedProperties = dynamicProperties.cloneProperties(); + mergedProperties.setBaseProperties(staticProperties); + + lookForPartProperty(dynamicProperties); + } + } + + protected void setOriginProvider(JROriginProvider originProvider) + { + this.originProvider = originProvider; + } + + protected JROrigin getElementOrigin() + { + JROrigin elementOrigin = null; + if (originProvider != null) + { + elementOrigin = originProvider.getOrigin(); + } + return elementOrigin; + } + + protected boolean isDelayedStyleEvaluation() + { + return filler.getPropertiesUtil().getBooleanProperty(this, + JRStyle.PROPERTY_EVALUATION_TIME_ENABLED, false); + } + + public JRBaseFiller getFiller() + { + return filler; + } + + + @Override + public boolean hasDynamicProperties() + { + return !propertyExpressions.isEmpty(); + } + + @Override + public boolean hasDynamicProperty(String name) + { + // not called very often for now so doing linear search in array + for (JRPropertyExpression prop : propertyExpressions) + { + if (prop.getName().equals(name)) + { + return true; + } + } + return false; + } + + @Override + public JRPropertiesMap getDynamicProperties() + { + return dynamicProperties; + } + + protected JRStyle getInitStyle() + { + return initStyle; + } + + protected JRElement getParent() + { + return parent; + } + + protected void addDynamicProperty(String name, JRExpression expression) + { + JRDesignPropertyExpression prop = new JRDesignPropertyExpression(); + prop.setName(name); + prop.setValueExpression(expression); + + propertyExpressions.add(prop); + // recomputing + dynamicTransferProperties = findDynamicTransferProperties(); + } + + protected void setExpressionEvaluator(JRFillExpressionEvaluator expressionEvaluator) + { + this.expressionEvaluator = expressionEvaluator; + } + + + /** + * + */ + public static Integer getBookmarkLevel(Object value) throws JRException + { + Integer level = null; + + if (value != null) + { + if (value instanceof Number) + { + level = ((Number)value).intValue(); + } + else + { + try + { + level = Integer.parseInt(value.toString()); + } + catch (NumberFormatException e) + { + //do nothing + } + } + + if (level == null || level < 0) + { + throw + new JRException( + EXCEPTION_MESSAGE_KEY_INVALID_BOOKMARK_LEVEL, + new Object[] {value} + ); + } + } + + return level; + } } diff --git a/jasperreports/src/net/sf/jasperreports/engine/fill/JRFillElementContainer.java b/jasperreports/src/net/sf/jasperreports/engine/fill/JRFillElementContainer.java index 9a00a196a6..90bdaa5e1c 100644 --- a/jasperreports/src/net/sf/jasperreports/engine/fill/JRFillElementContainer.java +++ b/jasperreports/src/net/sf/jasperreports/engine/fill/JRFillElementContainer.java @@ -57,1261 +57,1256 @@ */ public abstract class JRFillElementContainer extends JRFillElementGroup implements FillContainerContext { - protected JRBaseFiller filler; - - private JRFillElement[] ySortedElements; - private JRFillElement[] stretchElements; - private JRFillElement[] bandBottomElements; - private JRFillElement[] removableElements; - - protected boolean willOverflowWithElements; - protected boolean willOverflowWithWhiteSpace; - protected boolean isOverflow; - protected boolean currentOverflowWithElements; - protected boolean currentOverflowWithWhiteSpace; - private boolean currentOverflowAllowed; - - private int stretchHeight; - private int firstY; - protected boolean atLeastOneElementIsToPrint; - - protected final JRFillExpressionEvaluator expressionEvaluator; - - protected JRFillElement[] deepElements; - - /** - * - */ - protected Set stylesToEvaluate = new HashSet<>(); - protected Map evaluatedStyles = new HashMap<>(); - - protected boolean hasPrintWhenOverflowElement; - - private final boolean legacyElementStretchEnabled; - - - protected JRFillElementContainer(JRBaseFiller filler, JRElementGroup container, JRFillObjectFactory factory) - { - super(container, factory); - - expressionEvaluator = factory.getExpressionEvaluator(); - initDeepElements(); - - this.filler = filler; - - @SuppressWarnings("deprecation") - boolean depFlag = filler.getFillContext().isLegacyElementStretchEnabled(); - legacyElementStretchEnabled = depFlag; - } - - protected JRFillElementContainer(JRFillElementContainer container, JRFillCloneFactory factory) - { - super(container, factory); - - expressionEvaluator = container.expressionEvaluator; - initDeepElements(); - - this.filler = container.filler; - - @SuppressWarnings("deprecation") - boolean depFlag = filler.getFillContext().isLegacyElementStretchEnabled(); - legacyElementStretchEnabled = depFlag; - } - - - protected void initDeepElements() - { - if (elements == null) - { - deepElements = new JRFillElement[0]; - } - else - { - List deepElementsList = new ArrayList<>(elements.length); - collectDeepElements(elements, deepElementsList); - deepElements = new JRFillElement[deepElementsList.size()]; - deepElementsList.toArray(deepElements); - } - } - - private static void collectDeepElements(JRElement[] elements, List deepElementsList) - { - for (int i = 0; i < elements.length; i++) - { - JRElement element = elements[i]; - deepElementsList.add((JRFillElement)element); - - if (element instanceof JRFillFrame) - { - JRFrame frame = (JRFrame) element; - collectDeepElements(frame.getElements(), deepElementsList); - } - } - } - - /** - * @deprecated To be removed. - */ - protected final void _initElements() - { - hasPrintWhenOverflowElement = false; - - if (elements != null && elements.length > 0) - { - List sortedElemsList = new ArrayList<>(); - List stretchElemsList = new ArrayList<>(); - List bandBottomElemsList = new ArrayList<>(); - List removableElemsList = new ArrayList<>(); - - topElementInGroup = null; - bottomElementInGroup = null; - - for (JRFillElement element : elements) - { - sortedElemsList.add(element); - - if (element.getPositionTypeValue() == PositionTypeEnum.FIX_RELATIVE_TO_BOTTOM) - { - bandBottomElemsList.add(element); - } - - if (element.getStretchTypeValue() != StretchTypeEnum.NO_STRETCH) - { - stretchElemsList.add(element); - } - - if (element.isRemoveLineWhenBlank()) - { - removableElemsList.add(element); - } - - if (element.isPrintWhenDetailOverflows()) - { - hasPrintWhenOverflowElement = true; - } - - if ( - topElementInGroup == null || - ( - element.getY() + element.getHeight() < - topElementInGroup.getY() + topElementInGroup.getHeight()) - ) - { - topElementInGroup = element; - } - - if ( - bottomElementInGroup == null || - ( - element.getY() + element.getHeight() > - bottomElementInGroup.getY() + bottomElementInGroup.getHeight()) - ) - { - bottomElementInGroup = element; - } - } - - /* */ - Collections.sort(sortedElemsList, new JRYComparator()); - ySortedElements = new JRFillElement[elements.length]; - sortedElemsList.toArray(ySortedElements); - - /* */ - stretchElements = new JRFillElement[stretchElemsList.size()]; - stretchElemsList.toArray(stretchElements); - - /* */ - bandBottomElements = new JRFillElement[bandBottomElemsList.size()]; - bandBottomElemsList.toArray(bandBottomElements); - - /* */ - removableElements = new JRFillElement[removableElemsList.size()]; - removableElemsList.toArray(removableElements); - } - - /* */ - setDependentElements(); - } - - protected final void initElements() - { - if (isLegacyElementStretchEnabled()) - { - _initElements(); - return; - } - - hasPrintWhenOverflowElement = false; - - if (elements != null && elements.length > 0) - { - List stretchElemsList = new ArrayList<>(); - List bandBottomElemsList = new ArrayList<>(); - List removableElemsList = new ArrayList<>(); - - JRYComparator yComparator = new JRYComparator(); - - /* */ - ySortedElements = Arrays.copyOf(elements, elements.length); - Arrays.sort(ySortedElements, yComparator); - - topElementInGroup = null; - bottomElementInGroup = null; - - for (JRFillElement element : ySortedElements) - { - if (element.getPositionTypeValue() == PositionTypeEnum.FIX_RELATIVE_TO_BOTTOM) - { - bandBottomElemsList.add(element); - } - - if (element.getStretchTypeValue() != StretchTypeEnum.NO_STRETCH) - { - stretchElemsList.add(element); - } - - if (element.isRemoveLineWhenBlank()) - { - removableElemsList.add(element); - } - - if (element.isPrintWhenDetailOverflows()) - { - hasPrintWhenOverflowElement = true; - } - - if ( - topElementInGroup == null || - ( - element.getY() + element.getHeight() < - topElementInGroup.getY() + topElementInGroup.getHeight()) - ) - { - topElementInGroup = element; - } - - if ( - bottomElementInGroup == null || - ( - element.getY() + element.getHeight() > - bottomElementInGroup.getY() + bottomElementInGroup.getHeight()) - ) - { - bottomElementInGroup = element; - } - } - - /* */ - stretchElements = new JRFillElement[stretchElemsList.size()]; - stretchElemsList.toArray(stretchElements); - - /* */ - bandBottomElements = new JRFillElement[bandBottomElemsList.size()]; - bandBottomElemsList.toArray(bandBottomElements); - - /* */ - removableElements = new JRFillElement[removableElemsList.size()]; - removableElemsList.toArray(removableElements); - } - - /* */ - setDependentElements(); - } - - /** - * - */ - private void setDependentElements() - { - if (ySortedElements != null && ySortedElements.length > 0) - { - for(int i = 0; i < ySortedElements.length - 1; i++) - { - JRFillElement iElem = ySortedElements[i]; - boolean isBreakElem = iElem instanceof JRFillBreak; - - for(int j = i + 1; j < ySortedElements.length; j++) - { - JRFillElement jElem = ySortedElements[j]; - - int left = Math.min(iElem.getX(), jElem.getX()); - int right = Math.max(iElem.getX() + iElem.getWidth(), jElem.getX() + jElem.getWidth()); - - if ( - ((isBreakElem && jElem.getPositionTypeValue() == PositionTypeEnum.FIX_RELATIVE_TO_TOP) || jElem.getPositionTypeValue() == PositionTypeEnum.FLOAT) && - iElem.getY() + iElem.getHeight() <= jElem.getY() && - iElem.getWidth() + jElem.getWidth() > right - left // FIXME band bottom elements should not have dependent elements - ) - { - iElem.addDependantElement(jElem); - } - } - - /* - if (iElem.getParent().getElementGroup() != null) //parent might be null - { - iElem.setGroupElements( - iElem.getParent().getElementGroup().getElements() - ); - } - */ - } - } - } - - - /** - * - */ - protected void evaluate(byte evaluation) throws JRException - { - //evaluatePrintWhenExpression(evaluation); - - //if ( - // (isPrintWhenExpressionNull() || - // (!isPrintWhenExpressionNull() && - // isPrintWhenTrue())) - // ) - //{ - JRElement[] allElements = getElements(); - if (allElements != null && allElements.length > 0) - { - for(int i = 0; i < allElements.length; i++) - { - JRFillElement element = (JRFillElement)allElements[i]; - element.setCurrentEvaluation(evaluation); - element.evaluate(evaluation); - } - } - //} - } - - - /** - * - */ - protected void resetElements() - { - if (ySortedElements != null && ySortedElements.length > 0) - { - for(int i = 0; i < ySortedElements.length; i++) - { - JRFillElement element = ySortedElements[i]; - - element.reset(); - - if (!isOverflow) - { - element.setAlreadyPrinted(false); - } - } - } - } - - - /** - * Indicates whether the elements in this container will overflow. - * - * @return whether this container will overflow - */ - public boolean willOverflow() - { - return willOverflowWithElements || willOverflowWithWhiteSpace; - } - - - protected void initFill() - { - isOverflow = willOverflow(); - firstY = 0; - atLeastOneElementIsToPrint = false; - } - - - /** - * @deprecated To be removed. - */ - protected void _prepareElements( - int availableHeight, - boolean isOverflowAllowed - ) throws JRException - { - currentOverflowWithElements = false; - currentOverflowWithWhiteSpace = false; - currentOverflowAllowed = isOverflowAllowed; - - int calculatedStretchHeight = getContainerHeight(); - - firstY = isOverflow ? getActualContainerHeight() : 0; - atLeastOneElementIsToPrint = false; - boolean isFirstYFound = false; - - if (ySortedElements != null && ySortedElements.length > 0) - { - for(int i = 0; i < ySortedElements.length; i++) - { - JRFillElement element = ySortedElements[i]; - - currentOverflowWithElements = - element.prepare( - availableHeight + getElementFirstY(element), - isOverflow - ) - || currentOverflowWithElements; - - element._moveDependantElements(); - - if (element.isToPrint()) - { - if (isOverflow) - { - if (element.isReprinted()) - { - firstY = 0; - } - else if (!isFirstYFound) - { - firstY = element.getY(); - } - isFirstYFound = true; - } - - atLeastOneElementIsToPrint = true; - - int spaceToBottom = getContainerHeight() - element.getY() - element.getHeight(); - if (spaceToBottom < 0) - { - spaceToBottom = 0; - } - - if (calculatedStretchHeight < element.getRelativeY() + element.getStretchHeight() + spaceToBottom) - { - calculatedStretchHeight = element.getRelativeY() + element.getStretchHeight() + spaceToBottom; - } - } - } - } - - if (calculatedStretchHeight > availableHeight + firstY) - { - currentOverflowWithWhiteSpace = true; - } - - // stretchHeight includes firstY, which is subtracted in fillElements - if (currentOverflowWithElements || currentOverflowWithWhiteSpace) - { - stretchHeight = availableHeight + firstY; - } - else - { - stretchHeight = calculatedStretchHeight; - } - - willOverflowWithElements = currentOverflowWithElements && isOverflowAllowed; - willOverflowWithWhiteSpace = currentOverflowWithWhiteSpace && isOverflowAllowed; - } - - - /** - * - */ - protected void prepareElements( - int availableHeight, - boolean isOverflowAllowed - ) throws JRException - { - if (isLegacyElementStretchEnabled()) - { - _prepareElements(availableHeight, isOverflowAllowed); - return; - } - - currentOverflowWithElements = false; - currentOverflowWithWhiteSpace = false; - currentOverflowAllowed = isOverflowAllowed; - - firstY = isOverflow ? getActualContainerHeight() : 0; - atLeastOneElementIsToPrint = false; - boolean isFirstYFound = false; - - if (ySortedElements != null && ySortedElements.length > 0) - { - for (JRFillElement element : ySortedElements) - { - currentOverflowWithElements = - element.prepare( - availableHeight + getElementFirstY(element), - isOverflow - ) - || currentOverflowWithElements; - - // it does not seem to make sense for elements that do not print because of their isToPrint() returning false, - // to push other dependent elements, but it was always like that; furthermore, such disappearing elements are pushed by - // other elements and also participate in white space collapse later on, so it is somewhat fair to also allow them - // to push others - element.moveDependantElements(); - - if (element.isToPrint()) - { - if (isOverflow) - { - if (element.isReprinted()) - { - firstY = 0; - } - else if (!isFirstYFound) - { - firstY = element.getY(); - } - isFirstYFound = true; - } - - atLeastOneElementIsToPrint = true; - } - } - } - - // normally, we should add firstY here, because it says below stretchHeight contains firstY; - // this initialization matters when band overflows with white space and no element is rendered on the next page; - // but we don't add it because, historically, bands did not preserve the white space when overflowing, unlike frames for example, - // which re-render at their design height even when overflowing with white space - stretchHeight = getContainerHeight();// + firstY; - - // certain elements have stretched to their natural height, while others have been moved in the process; - // we are now ready to calculate the stretch height of the current container, so that we can use that for - // moving elements to bottom, before attempting to remove blank elements; - // ATTENTION: this calculation needed to be in a separate ySortedElement loop as the above one, because - // it needs to take into consideration the displacement of dependent elements made after each element prepare - prepareStretchHeight(availableHeight, isOverflowAllowed); - - moveBandBottomElements(); - - // removing blank elements and thus collapsing white space performs both - // element height shrinking and relative Y repositioning, also changing the current - // container stretch height - removeBlankElements(); - - // we are first stretching elements relative to group, because they do not need container height and might - // actually cause the container to stretch further - stretchElementsToElementGroup(); - - // recalculating container stretch height to account for element group stretching, just before triggering - // container related stretch - prepareStretchHeight(availableHeight, isOverflowAllowed); - - moveBandBottomElements(); - - // container based element stretching is the last one to be performed - stretchElementsToContainer(); - } - - /** - * - */ - protected void prepareStretchHeight( - int availableHeight, - boolean isOverflowAllowed - ) throws JRException - { - int calculatedStretchHeight = calculateStretchHeight(); - - if (calculatedStretchHeight > availableHeight + firstY) - { - currentOverflowWithWhiteSpace = true; - } - - // stretchHeight includes firstY, which is subtracted in fillElements - if (currentOverflowWithElements || currentOverflowWithWhiteSpace) - { - stretchHeight = availableHeight + firstY; - } - else - { - stretchHeight = calculatedStretchHeight; - } - - willOverflowWithElements = currentOverflowWithElements && isOverflowAllowed; - willOverflowWithWhiteSpace = currentOverflowWithWhiteSpace && isOverflowAllowed; - } - - /** - * - */ - protected int calculateStretchHeight() throws JRException - { - int calculatedStretchHeight = -1; - - if (ySortedElements != null && ySortedElements.length > 0) - { - int containerHeight = getContainerHeight(); - - for (JRFillElement element : ySortedElements) - { - if (element.isToPrint()) - { - int spaceToBottom = containerHeight - (element.getY() + element.getHeight()) - element.getCollapsedHeightBelow(); - if (spaceToBottom < 0) - { - spaceToBottom = 0; - } - - if (calculatedStretchHeight < element.getRelativeY() + element.getStretchHeight() + spaceToBottom) - { - calculatedStretchHeight = element.getRelativeY() + element.getStretchHeight() + spaceToBottom; - } - } - } - } - - if (calculatedStretchHeight < 0) - { - // there was no element printing; so trying to preserve stretchHeight - calculatedStretchHeight = stretchHeight; - } - - return calculatedStretchHeight; - } - - /** - * - */ - public boolean isLegacyElementStretchEnabled() - { - return legacyElementStretchEnabled; - } - - @Override - public boolean isCurrentOverflow() - { - return currentOverflowWithElements || currentOverflowWithWhiteSpace; - } - - @Override - public boolean isCurrentOverflowAllowed() - { - return currentOverflowAllowed; - } - - private int getElementFirstY(JRFillElement element) - { - int elemFirstY; - if (!isOverflow || hasPrintWhenOverflowElement) - { - elemFirstY = 0; - } - else if (element.getY() >= firstY) - { - elemFirstY = firstY; - } - else - { - elemFirstY = element.getY(); - } - return elemFirstY; - } - - /** - * @deprecated To be removed. - */ - protected void _setStretchHeight(int stretchHeight) - { - if (stretchHeight > this.stretchHeight) - { - this.stretchHeight = stretchHeight; - } - } - - /** - * This method is deprecated and is going to be removed. - * Not marked as deprecated to avoid deprecation warnings. - */ - @SuppressWarnings("deprecation") - protected void stretchElements() - { - if (stretchElements != null && stretchElements.length > 0) - { - for(int i = 0; i < stretchElements.length; i++) - { - JRFillElement element = stretchElements[i]; - - element._stretchElement(stretchHeight - getContainerHeight());//TODO subtract firstY? - - element._moveDependantElements(); - } - } - - if (ySortedElements != null && ySortedElements.length > 0) - { - for(int i = 0; i < ySortedElements.length; i++) - { - JRFillElement element = ySortedElements[i]; - - element.stretchHeightFinal(); - } - } - } - - protected void setStretchHeight(int stretchHeight) - { - if (isLegacyElementStretchEnabled()) - { - _setStretchHeight(stretchHeight); - return; - } - - this.stretchHeight = stretchHeight; - } - - /** - * - */ - protected void stretchElementsToElementGroup() - { - if (stretchElements != null && stretchElements.length > 0) - { - for (int i = 0; i < stretchElements.length; i++) - { - JRFillElement element = stretchElements[i]; - - if (element.isToPrint()) - { - boolean applied = element.stretchElementToElementGroup(); - - if (applied) - { - element.moveDependantElements(); - } - } - } - } - } - - /** - * - */ - protected void stretchElementsToContainer() - { - if (stretchElements != null && stretchElements.length > 0) - { - int containerStretch = stretchHeight - getContainerHeight(); - - for (int i = 0; i < stretchElements.length; i++) - { - JRFillElement element = stretchElements[i]; - - if (element.isToPrint()) - { - boolean applied = element.stretchElementToContainer(containerStretch); - - if (applied) - { - element.moveDependantElements(); - } - } - } - } - } - - - protected int getStretchHeight() - { - return stretchHeight; - } - - - /** - * - */ - protected void moveBandBottomElements() - { - //if (!willOverflow) - //{ - if (bandBottomElements != null && bandBottomElements.length > 0) - { - for (int i = 0; i < bandBottomElements.length; i++) - { - JRFillElement element = bandBottomElements[i]; - - if (element.isToPrint()) - { - // band bottom elements do not print if there will be an overflow - if (currentOverflowWithElements || currentOverflowWithWhiteSpace) - { - currentOverflowWithElements = true; - } - - element.setToPrint(!((currentOverflowWithElements || willOverflowWithWhiteSpace) && currentOverflowAllowed));// don't use willOverflow() method as it is overridden at least in bands - } - - if (element.isToPrint()) - { - element.setRelativeY( - element.getY() + stretchHeight - getActualContainerHeight() - ); - } - } - } - //} - } - - - /** - * @deprecated To be removed. - */ - protected void _removeBlankElements() - { - JRElement[] remElems = removableElements; - if (remElems != null && remElems.length > 0) - { - JRElement[] elems = ySortedElements; - - for(int i = 0; i < remElems.length; i++) - { - JRFillElement iElem = (JRFillElement)remElems[i]; - - int blankHeight; - if (iElem.isToPrint()) - { - blankHeight = iElem.getHeight() - iElem.getStretchHeight(); - } - else - { - blankHeight = iElem.getHeight();//FIXME subreports that stretch and then don't print, will not remove all space - } - - if ( - blankHeight > 0 && - iElem.getRelativeY() + iElem.getStretchHeight() <= stretchHeight && - iElem.getRelativeY() >= firstY - ) - { - int blankY = iElem.getRelativeY() + iElem.getHeight() - blankHeight; - boolean isToRemove = true; - - for(int j = 0; j < elems.length; j++) - { - JRFillElement jElem = (JRFillElement)elems[j]; - - if (iElem != jElem && jElem.isToPrint()) - { - int top = - Math.min(blankY, jElem.getRelativeY()); - int bottom = - Math.max( - blankY + blankHeight, - jElem.getRelativeY() + jElem.getStretchHeight() - ); - - if (blankHeight + jElem.getStretchHeight() > bottom - top) - { - isToRemove = false; - break; - } - } - } - - if (isToRemove) - { - for(int j = 0; j < elems.length; j++) - { - JRFillElement jElem = (JRFillElement)elems[j]; - - if (jElem.getRelativeY() >= blankY + blankHeight) - { - jElem.setRelativeY(jElem.getRelativeY() - blankHeight); - } - } - - stretchHeight = stretchHeight - blankHeight; - } - } - } - } - } - - - /** - * - */ - protected void removeBlankElements() - { - if (isLegacyElementStretchEnabled()) - { - _removeBlankElements(); - return; - } - - if (removableElements != null && removableElements.length > 0) - { - for (JRFillElement remElem : removableElements) - { - int blankHeight; - if (remElem.isToPrint()) - { - blankHeight = remElem.getHeight() - remElem.getStretchHeight(); - } - else - { - blankHeight = remElem.getHeight();//FIXME subreports that stretch and then don't print, will not remove all space - } - - if ( - blankHeight > 0 && - remElem.getRelativeY() + remElem.getStretchHeight() <= stretchHeight && - remElem.getRelativeY() >= firstY - ) - { - int blankY = remElem.getRelativeY() + remElem.getHeight() - blankHeight; - boolean isToRemove = true; - - for (JRFillElement jElem : ySortedElements) - { - if (remElem != jElem && jElem.isToPrint()) - { - int top = - Math.min(blankY, jElem.getRelativeY()); - int bottom = - Math.max( - blankY + blankHeight, - jElem.getRelativeY() + jElem.getStretchHeight() - ); - - if (blankHeight + jElem.getStretchHeight() > bottom - top) - { - isToRemove = false; - break; - } - } - } - - if (isToRemove) - { - for (JRFillElement jElem : ySortedElements) - { - if (jElem.getRelativeY() + jElem.getStretchHeight() <= blankY) - { - jElem.setCollapsedHeightBelow(jElem.getCollapsedHeightBelow() + blankHeight); - } - - if (jElem.getRelativeY() >= blankY + blankHeight) - { - jElem.setCollapsedHeightAbove(jElem.getCollapsedHeightAbove() + blankHeight); - jElem.setRelativeY(jElem.getRelativeY() - blankHeight); - } - } - - stretchHeight = stretchHeight - blankHeight; - } - } - } - } - } - - - /** - * Fills the elements from this container into a print element container. - * - * @param printContainer the print element container - * @throws JRException - */ - public void fillElements(JRPrintElementContainer printContainer) throws JRException - { - //int maxStretch = 0; - //int stretch = 0; - int maxWidth = 0; - JRElement[] allElements = getElements(); - if (allElements != null && allElements.length > 0) - { - for(int i = 0; i < allElements.length; i++) - { - JRFillElement element = (JRFillElement)allElements[i]; - - element.setRelativeY(element.getRelativeY() - firstY); - - if (element.getRelativeY() + element.getStretchHeight() > stretchHeight - firstY) - { - element.setToPrint(false); - } - - element.setAlreadyPrinted(element.isToPrint() || element.isAlreadyPrinted()); - - if (element.isToPrint()) - { - JRPrintElement printElement = element.fill(); - //printElement.setY(printElement.getY() - firstY); - - if (printElement != null) - { - //FIXME not all elements affect height - //stretch = printElement.getY() + firstY + printElement.getHeight() - element.getY() - element.getHeight(); - //if (stretch > maxStretch) - //{ - // maxStretch = stretch; - //} - printContainer.addElement(printElement); - if (printElement.getX() + printElement.getWidth() > maxWidth) - { - maxWidth = printElement.getX() + printElement.getWidth(); - } - } - - if (element instanceof JRFillSubreport) - { - JRFillSubreport subreport = (JRFillSubreport)element; - - List styles = subreport.subreportFiller.getJasperPrint().getStylesList(); - for(int j = 0; j < styles.size(); j++) - { - filler.addPrintStyle(styles.get(j)); - } - - List origins = subreport.subreportFiller.getJasperPrint().getOriginsList(); - for(int j = 0; j < origins.size(); j++) - { - filler.getJasperPrint().addOrigin(origins.get(j)); - } - - Collection printElements = subreport.getPrintElements(); - addSubElements(printContainer, element, printElements); - if (subreport.getX() + subreport.getPrintContentsWidth() > maxWidth) - { - maxWidth = subreport.getX() + subreport.getPrintContentsWidth(); - } - - subreport.subreportPageFilled(); - } - - // crosstabs do not return a fill() element - if (element instanceof JRFillCrosstab) - { - JRFillCrosstab crosstab = (JRFillCrosstab) element; - List printElements = crosstab.getPrintElements(); - addSubElements(printContainer, element, printElements); - if (crosstab.getX() + crosstab.getPrintElementsWidth() > maxWidth) - { - maxWidth = crosstab.getX() + crosstab.getPrintElementsWidth(); - } - } - } - } - } - - //printBand.setHeight(getHeight() + maxStretch - firstY); - printContainer.setHeight(stretchHeight - firstY); - printContainer.setContentsWidth(maxWidth); - } - - - protected void addSubElements(JRPrintElementContainer printContainer, JRFillElement element, - Collection printElements) - { - if (printContainer instanceof OffsetElementsContainer) - { - // adding the subelements as whole lists to bands so that we don't need - // another virtualized list at print band level - ((OffsetElementsContainer) printContainer).addOffsetElements(printElements, - element.getX(), element.getRelativeY()); - } - else - { - if (printElements != null && printElements.size() > 0) - { - for(Iterator it = printElements.iterator(); it.hasNext();) - { - JRPrintElement printElement =it.next(); - printElement.setX(element.getX() + printElement.getX()); - printElement.setY(element.getRelativeY() + printElement.getY()); - printContainer.addElement(printElement); - } - } - } - } - - - /** - * - */ - protected void rewind() throws JRException - { - if (ySortedElements != null && ySortedElements.length > 0) - { - for(int i = 0; i < ySortedElements.length; i++) - { - JRFillElement element = ySortedElements[i]; - - element.rewind(); - - element.setAlreadyPrinted(false); - } - } - - willOverflowWithElements = false; - willOverflowWithWhiteSpace = false; - } - - protected int getFirstY() - { - return firstY; - } - - - /** - * Returns the actual height of the element container. - * Some element containers such as frames have a larger calculated container height, resulting from content being placed beyond container declared height. - * - * @return the height of the element container - */ - protected abstract int getActualContainerHeight(); - - - /** - * Returns the height of the element container. - * - * @return the height of the element container - */ - protected abstract int getContainerHeight(); - - - /** - * Find all styles containing conditional styles which are referenced by elements in this band. - */ - protected void initConditionalStyles() - { - filler.addDefaultStyleListener(new JRBaseFiller.DefaultStyleListener(){ - @Override - public void defaultStyleSet(JRStyle style) - { - collectConditionalStyle(style); - } - }); - - for (int i = 0; i < deepElements.length; i++) - { - JRStyle style = deepElements[i].initStyle; - collectConditionalStyle(style); - } - - if (deepElements.length > 0) - { - for(int i = 0; i < deepElements.length; i++) - { - deepElements[i].setConditionalStylesContainer(this); - } - } - } - - protected void collectConditionalStyle(JRStyle style) - { - if (style != null)// && style.getConditionalStyles() != null) - { - stylesToEvaluate.add(style); - } - } - - - protected void evaluateConditionalStyles(byte evaluation) throws JRException - { - for (Iterator it = stylesToEvaluate.iterator(); it.hasNext();) - { - evaluateConditionalStyle(it.next(), evaluation); - } - } - - - protected JRStyle evaluateConditionalStyle(JRStyle initialStyle, byte evaluation) throws JRException - { - JRStyle consolidatedStyle = initialStyle; - - StringBuilder code = new StringBuilder(); - List condStylesToApply = new ArrayList<>(); - - boolean anyTrue = buildConsolidatedStyle(initialStyle, evaluation, code, condStylesToApply); - - if (anyTrue) - { - String consolidatedStyleName = initialStyle.getName() + "|" + code.toString(); - consolidatedStyle = filler.getJasperPrint().getStylesMap().get(consolidatedStyleName); - if (consolidatedStyle == null) - { - JRBaseStyle style = new JRBaseStyle(initialStyle.getDefaultStyleProvider(), consolidatedStyleName); - for (int j = condStylesToApply.size() - 1; j >= 0; j--) - { - StyleUtil.appendStyle(style, condStylesToApply.get(j)); - } - - // deduplicate to previously created identical instances - style = filler.fillContext.deduplicate(style); - filler.addPrintStyle(style); - - consolidatedStyle = style; - } - } - - evaluatedStyles.put(initialStyle, consolidatedStyle); - - return consolidatedStyle; - } - - - protected boolean buildConsolidatedStyle(JRStyle style, byte evaluation, StringBuilder code, List condStylesToApply) throws JRException - { - boolean anyTrue = false; - - JRConditionalStyle[] conditionalStyles = style.getConditionalStyles(); - if (conditionalStyles != null && conditionalStyles.length > 0) - { - for (int j = 0; j < conditionalStyles.length; j++) - { - JRConditionalStyle conditionalStyle = conditionalStyles[j]; - Boolean expressionValue = - (Boolean) expressionEvaluator.evaluate( - conditionalStyle.getConditionExpression(), - evaluation - ); - - boolean condition; - if (expressionValue == null) - { - condition = false; - } - else - { - condition = expressionValue; - } - - code.append(condition ? '1' : '0'); - anyTrue = anyTrue | condition; - - if (condition) - { - condStylesToApply.add(conditionalStyle); - } - } - } - - condStylesToApply.add(style); - - if (style.getStyle() != null) - { - anyTrue = anyTrue | buildConsolidatedStyle(style.getStyle(), evaluation, code, condStylesToApply); - } - return anyTrue; - } - - - public JRStyle getEvaluatedConditionalStyle(JRStyle parentStyle) - { - return evaluatedStyles.get(parentStyle); - } - - protected final void setElementOriginProvider(JROriginProvider originProvider) - { - if (originProvider != null) - { - for (int i = 0; i < deepElements.length; i++) - { - deepElements[i].setOriginProvider(originProvider); - } - } - } + protected JRBaseFiller filler; + + private JRFillElement[] ySortedElements; + private JRFillElement[] stretchElements; + private JRFillElement[] bandBottomElements; + private JRFillElement[] removableElements; + + protected boolean willOverflowWithElements; + protected boolean willOverflowWithWhiteSpace; + protected boolean isOverflow; + protected boolean currentOverflowWithElements; + protected boolean currentOverflowWithWhiteSpace; + private boolean currentOverflowAllowed; + + private int stretchHeight; + private int firstY; + protected boolean atLeastOneElementIsToPrint; + + protected final JRFillExpressionEvaluator expressionEvaluator; + + protected JRFillElement[] deepElements; + + /** + * + */ + protected Set stylesToEvaluate = new HashSet<>(); + protected Map evaluatedStyles = new HashMap<>(); + + protected boolean hasPrintWhenOverflowElement; + + private final boolean legacyElementStretchEnabled; + + + protected JRFillElementContainer(JRBaseFiller filler, JRElementGroup container, JRFillObjectFactory factory) + { + super(container, factory); + + expressionEvaluator = factory.getExpressionEvaluator(); + initDeepElements(); + + this.filler = filler; + + @SuppressWarnings("deprecation") + boolean depFlag = filler.getFillContext().isLegacyElementStretchEnabled(); + legacyElementStretchEnabled = depFlag; + } + + protected JRFillElementContainer(JRFillElementContainer container, JRFillCloneFactory factory) + { + super(container, factory); + + expressionEvaluator = container.expressionEvaluator; + initDeepElements(); + + this.filler = container.filler; + + @SuppressWarnings("deprecation") + boolean depFlag = filler.getFillContext().isLegacyElementStretchEnabled(); + legacyElementStretchEnabled = depFlag; + } + + + protected void initDeepElements() + { + if (elements == null) + { + deepElements = new JRFillElement[0]; + } + else + { + List deepElementsList = new ArrayList<>(elements.length); + collectDeepElements(elements, deepElementsList); + deepElements = new JRFillElement[deepElementsList.size()]; + deepElementsList.toArray(deepElements); + } + } + + private static void collectDeepElements(JRElement[] elements, List deepElementsList) + { + for (int i = 0; i < elements.length; i++) + { + JRElement element = elements[i]; + deepElementsList.add((JRFillElement)element); + + if (element instanceof JRFillFrame) + { + JRFrame frame = (JRFrame) element; + collectDeepElements(frame.getElements(), deepElementsList); + } + } + } + + /** + * @deprecated To be removed. + */ + protected final void _initElements() + { + hasPrintWhenOverflowElement = false; + + if (elements != null && elements.length > 0) + { + List sortedElemsList = new ArrayList<>(); + List stretchElemsList = new ArrayList<>(); + List bandBottomElemsList = new ArrayList<>(); + List removableElemsList = new ArrayList<>(); + + topElementInGroup = null; + bottomElementInGroup = null; + + for (JRFillElement element : elements) + { + sortedElemsList.add(element); + + if (element.getPositionTypeValue() == PositionTypeEnum.FIX_RELATIVE_TO_BOTTOM) + { + bandBottomElemsList.add(element); + } + + if (element.getStretchTypeValue() != StretchTypeEnum.NO_STRETCH) + { + stretchElemsList.add(element); + } + + if (element.isRemoveLineWhenBlank()) + { + removableElemsList.add(element); + } + + if (element.isPrintWhenDetailOverflows()) + { + hasPrintWhenOverflowElement = true; + } + + if ( + topElementInGroup == null || + ( + element.getY() + element.getHeight() < + topElementInGroup.getY() + topElementInGroup.getHeight()) + ) + { + topElementInGroup = element; + } + + if ( + bottomElementInGroup == null || + ( + element.getY() + element.getHeight() > + bottomElementInGroup.getY() + bottomElementInGroup.getHeight()) + ) + { + bottomElementInGroup = element; + } + } + + /* */ + Collections.sort(sortedElemsList, new JRYComparator()); + ySortedElements = new JRFillElement[elements.length]; + sortedElemsList.toArray(ySortedElements); + + /* */ + stretchElements = new JRFillElement[stretchElemsList.size()]; + stretchElemsList.toArray(stretchElements); + + /* */ + bandBottomElements = new JRFillElement[bandBottomElemsList.size()]; + bandBottomElemsList.toArray(bandBottomElements); + + /* */ + removableElements = new JRFillElement[removableElemsList.size()]; + removableElemsList.toArray(removableElements); + } + + /* */ + setDependentElements(); + } + + protected final void initElements() + { + if (isLegacyElementStretchEnabled()) + { + _initElements(); + return; + } + + hasPrintWhenOverflowElement = false; + + if (elements != null && elements.length > 0) + { + List stretchElemsList = new ArrayList<>(); + List bandBottomElemsList = new ArrayList<>(); + List removableElemsList = new ArrayList<>(); + + JRYComparator yComparator = new JRYComparator(); + + /* */ + ySortedElements = Arrays.copyOf(elements, elements.length); + Arrays.sort(ySortedElements, yComparator); + + topElementInGroup = null; + bottomElementInGroup = null; + + for (JRFillElement element : ySortedElements) + { + if (element.getPositionTypeValue() == PositionTypeEnum.FIX_RELATIVE_TO_BOTTOM) + { + bandBottomElemsList.add(element); + } + + if (element.getStretchTypeValue() != StretchTypeEnum.NO_STRETCH) + { + stretchElemsList.add(element); + } + + if (element.isRemoveLineWhenBlank()) + { + removableElemsList.add(element); + } + + if (element.isPrintWhenDetailOverflows()) + { + hasPrintWhenOverflowElement = true; + } + + if ( + topElementInGroup == null || + ( + element.getY() + element.getHeight() < + topElementInGroup.getY() + topElementInGroup.getHeight()) + ) + { + topElementInGroup = element; + } + + if ( + bottomElementInGroup == null || + ( + element.getY() + element.getHeight() > + bottomElementInGroup.getY() + bottomElementInGroup.getHeight()) + ) + { + bottomElementInGroup = element; + } + } + + /* */ + stretchElements = new JRFillElement[stretchElemsList.size()]; + stretchElemsList.toArray(stretchElements); + + /* */ + bandBottomElements = new JRFillElement[bandBottomElemsList.size()]; + bandBottomElemsList.toArray(bandBottomElements); + + /* */ + removableElements = new JRFillElement[removableElemsList.size()]; + removableElemsList.toArray(removableElements); + } + + /* */ + setDependentElements(); + } + + /** + * + */ + private void setDependentElements() { + if (ySortedElements != null && ySortedElements.length > 0) { + outerLoop: + for (int i = 0; i < ySortedElements.length - 1; i++) { + JRFillElement iElem = ySortedElements[i]; + boolean isBreakElem = iElem instanceof JRFillBreak; + int iElemX = iElem.getX(); + int iElemWidth = iElem.getWidth(); + int iElemRight = iElemX + iElemWidth; + int iElemBottom = iElem.getY() + iElem.getHeight(); + for (int j = i + 1; j < ySortedElements.length; j++) { + JRFillElement jElem = ySortedElements[j]; + int jElemX = jElem.getX(); + int jElemWidth = jElem.getWidth(); + int jElemRight = jElemX + jElemWidth; + PositionTypeEnum positionType = jElem.getPositionTypeValue(); + if (((isBreakElem && positionType == PositionTypeEnum.FIX_RELATIVE_TO_TOP) + || positionType == PositionTypeEnum.FLOAT) + && iElemBottom <= jElem.getY() + && iElemWidth + jElemWidth > Math.max(iElemRight, jElemRight) - Math.min(iElemX, jElemX)) { + iElem.addDependantElement(jElem); + if (jElemX <= iElemX && jElemRight >= iElemRight) { + continue outerLoop; + } + } + } + } + } + } + + + /** + * + */ + protected void evaluate(byte evaluation) throws JRException + { + //evaluatePrintWhenExpression(evaluation); + + //if ( + // (isPrintWhenExpressionNull() || + // (!isPrintWhenExpressionNull() && + // isPrintWhenTrue())) + // ) + //{ + JRElement[] allElements = getElements(); + if (allElements != null && allElements.length > 0) + { + for(int i = 0; i < allElements.length; i++) + { + JRFillElement element = (JRFillElement)allElements[i]; + element.setCurrentEvaluation(evaluation); + element.evaluate(evaluation); + } + } + //} + } + + + /** + * + */ + protected void resetElements() + { + if (ySortedElements != null && ySortedElements.length > 0) + { + for(int i = 0; i < ySortedElements.length; i++) + { + JRFillElement element = ySortedElements[i]; + + element.reset(); + + if (!isOverflow) + { + element.setAlreadyPrinted(false); + } + } + } + } + + + /** + * Indicates whether the elements in this container will overflow. + * + * @return whether this container will overflow + */ + public boolean willOverflow() + { + return willOverflowWithElements || willOverflowWithWhiteSpace; + } + + + protected void initFill() + { + isOverflow = willOverflow(); + firstY = 0; + atLeastOneElementIsToPrint = false; + } + + + /** + * @deprecated To be removed. + */ + protected void _prepareElements( + int availableHeight, + boolean isOverflowAllowed + ) throws JRException + { + currentOverflowWithElements = false; + currentOverflowWithWhiteSpace = false; + currentOverflowAllowed = isOverflowAllowed; + + int calculatedStretchHeight = getContainerHeight(); + + firstY = isOverflow ? getActualContainerHeight() : 0; + atLeastOneElementIsToPrint = false; + boolean isFirstYFound = false; + + if (ySortedElements != null && ySortedElements.length > 0) + { + for(int i = 0; i < ySortedElements.length; i++) + { + JRFillElement element = ySortedElements[i]; + + currentOverflowWithElements = + element.prepare( + availableHeight + getElementFirstY(element), + isOverflow + ) + || currentOverflowWithElements; + + element._moveDependantElements(); + + if (element.isToPrint()) + { + if (isOverflow) + { + if (element.isReprinted()) + { + firstY = 0; + } + else if (!isFirstYFound) + { + firstY = element.getY(); + } + isFirstYFound = true; + } + + atLeastOneElementIsToPrint = true; + + int spaceToBottom = getContainerHeight() - element.getY() - element.getHeight(); + if (spaceToBottom < 0) + { + spaceToBottom = 0; + } + + if (calculatedStretchHeight < element.getRelativeY() + element.getStretchHeight() + spaceToBottom) + { + calculatedStretchHeight = element.getRelativeY() + element.getStretchHeight() + spaceToBottom; + } + } + } + } + + if (calculatedStretchHeight > availableHeight + firstY) + { + currentOverflowWithWhiteSpace = true; + } + + // stretchHeight includes firstY, which is subtracted in fillElements + if (currentOverflowWithElements || currentOverflowWithWhiteSpace) + { + stretchHeight = availableHeight + firstY; + } + else + { + stretchHeight = calculatedStretchHeight; + } + + willOverflowWithElements = currentOverflowWithElements && isOverflowAllowed; + willOverflowWithWhiteSpace = currentOverflowWithWhiteSpace && isOverflowAllowed; + } + + + /** + * + */ + protected void prepareElements( + int availableHeight, + boolean isOverflowAllowed + ) throws JRException + { + if (isLegacyElementStretchEnabled()) + { + _prepareElements(availableHeight, isOverflowAllowed); + return; + } + + currentOverflowWithElements = false; + currentOverflowWithWhiteSpace = false; + currentOverflowAllowed = isOverflowAllowed; + + firstY = isOverflow ? getActualContainerHeight() : 0; + atLeastOneElementIsToPrint = false; + boolean isFirstYFound = false; + + if (ySortedElements != null && ySortedElements.length > 0) + { + for (JRFillElement element : ySortedElements) + { + if (currentOverflowWithElements && isOverflowAllowed && element.getY() > firstY + availableHeight) { + break; + } + currentOverflowWithElements = + element.prepare( + availableHeight + getElementFirstY(element), + isOverflow + ) + || currentOverflowWithElements; + + // it does not seem to make sense for elements that do not print because of their isToPrint() returning false, + // to push other dependent elements, but it was always like that; furthermore, such disappearing elements are pushed by + // other elements and also participate in white space collapse later on, so it is somewhat fair to also allow them + // to push others + element.moveDependantElements(); + + if (element.isToPrint()) + { + if (isOverflow) + { + if (element.isReprinted()) + { + firstY = 0; + } + else if (!isFirstYFound) + { + firstY = element.getY(); + } + isFirstYFound = true; + } + + atLeastOneElementIsToPrint = true; + } + } + } + + // normally, we should add firstY here, because it says below stretchHeight contains firstY; + // this initialization matters when band overflows with white space and no element is rendered on the next page; + // but we don't add it because, historically, bands did not preserve the white space when overflowing, unlike frames for example, + // which re-render at their design height even when overflowing with white space + stretchHeight = getContainerHeight();// + firstY; + + // certain elements have stretched to their natural height, while others have been moved in the process; + // we are now ready to calculate the stretch height of the current container, so that we can use that for + // moving elements to bottom, before attempting to remove blank elements; + // ATTENTION: this calculation needed to be in a separate ySortedElement loop as the above one, because + // it needs to take into consideration the displacement of dependent elements made after each element prepare + prepareStretchHeight(availableHeight, isOverflowAllowed); + + moveBandBottomElements(); + + // removing blank elements and thus collapsing white space performs both + // element height shrinking and relative Y repositioning, also changing the current + // container stretch height + removeBlankElements(); + + // we are first stretching elements relative to group, because they do not need container height and might + // actually cause the container to stretch further + stretchElementsToElementGroup(); + + // recalculating container stretch height to account for element group stretching, just before triggering + // container related stretch + prepareStretchHeight(availableHeight, isOverflowAllowed); + + moveBandBottomElements(); + + // container based element stretching is the last one to be performed + stretchElementsToContainer(); + } + + /** + * + */ + protected void prepareStretchHeight( + int availableHeight, + boolean isOverflowAllowed + ) throws JRException + { + int calculatedStretchHeight = calculateStretchHeight(); + + if (calculatedStretchHeight > availableHeight + firstY) + { + currentOverflowWithWhiteSpace = true; + } + + // stretchHeight includes firstY, which is subtracted in fillElements + if (currentOverflowWithElements || currentOverflowWithWhiteSpace) + { + stretchHeight = availableHeight + firstY; + } + else + { + stretchHeight = calculatedStretchHeight; + } + + willOverflowWithElements = currentOverflowWithElements && isOverflowAllowed; + willOverflowWithWhiteSpace = currentOverflowWithWhiteSpace && isOverflowAllowed; + } + + /** + * + */ + protected int calculateStretchHeight() throws JRException + { + int calculatedStretchHeight = -1; + + if (ySortedElements != null && ySortedElements.length > 0) + { + int containerHeight = getContainerHeight(); + + for (JRFillElement element : ySortedElements) + { + if (element.isToPrint()) + { + int spaceToBottom = containerHeight - (element.getY() + element.getHeight()) - element.getCollapsedHeightBelow(); + if (spaceToBottom < 0) + { + spaceToBottom = 0; + } + + if (calculatedStretchHeight < element.getRelativeY() + element.getStretchHeight() + spaceToBottom) + { + calculatedStretchHeight = element.getRelativeY() + element.getStretchHeight() + spaceToBottom; + } + } + } + } + + if (calculatedStretchHeight < 0) + { + // there was no element printing; so trying to preserve stretchHeight + calculatedStretchHeight = stretchHeight; + } + + return calculatedStretchHeight; + } + + /** + * + */ + public boolean isLegacyElementStretchEnabled() + { + return legacyElementStretchEnabled; + } + + @Override + public boolean isCurrentOverflow() + { + return currentOverflowWithElements || currentOverflowWithWhiteSpace; + } + + @Override + public boolean isCurrentOverflowAllowed() + { + return currentOverflowAllowed; + } + + private int getElementFirstY(JRFillElement element) + { + int elemFirstY; + if (!isOverflow || hasPrintWhenOverflowElement) + { + elemFirstY = 0; + } + else if (element.getY() >= firstY) + { + elemFirstY = firstY; + } + else + { + elemFirstY = element.getY(); + } + return elemFirstY; + } + + /** + * @deprecated To be removed. + */ + protected void _setStretchHeight(int stretchHeight) + { + if (stretchHeight > this.stretchHeight) + { + this.stretchHeight = stretchHeight; + } + } + + /** + * This method is deprecated and is going to be removed. + * Not marked as deprecated to avoid deprecation warnings. + */ + @SuppressWarnings("deprecation") + protected void stretchElements() + { + if (stretchElements != null && stretchElements.length > 0) + { + for(int i = 0; i < stretchElements.length; i++) + { + JRFillElement element = stretchElements[i]; + + element._stretchElement(stretchHeight - getContainerHeight());//TODO subtract firstY? + + element._moveDependantElements(); + } + } + + if (ySortedElements != null && ySortedElements.length > 0) + { + for(int i = 0; i < ySortedElements.length; i++) + { + JRFillElement element = ySortedElements[i]; + + element.stretchHeightFinal(); + } + } + } + + protected void setStretchHeight(int stretchHeight) + { + if (isLegacyElementStretchEnabled()) + { + _setStretchHeight(stretchHeight); + return; + } + + this.stretchHeight = stretchHeight; + } + + /** + * + */ + protected void stretchElementsToElementGroup() + { + if (stretchElements != null && stretchElements.length > 0) + { + for (int i = 0; i < stretchElements.length; i++) + { + JRFillElement element = stretchElements[i]; + + if (element.isToPrint()) + { + boolean applied = element.stretchElementToElementGroup(); + + if (applied) + { + element.moveDependantElements(); + } + } + } + } + } + + /** + * + */ + protected void stretchElementsToContainer() + { + if (stretchElements != null && stretchElements.length > 0) + { + int containerStretch = stretchHeight - getContainerHeight(); + + for (int i = 0; i < stretchElements.length; i++) + { + JRFillElement element = stretchElements[i]; + + if (element.isToPrint()) + { + boolean applied = element.stretchElementToContainer(containerStretch); + + if (applied) + { + element.moveDependantElements(); + } + } + } + } + } + + + protected int getStretchHeight() + { + return stretchHeight; + } + + + /** + * + */ + protected void moveBandBottomElements() + { + //if (!willOverflow) + //{ + if (bandBottomElements != null && bandBottomElements.length > 0) + { + for (int i = 0; i < bandBottomElements.length; i++) + { + JRFillElement element = bandBottomElements[i]; + + if (element.isToPrint()) + { + // band bottom elements do not print if there will be an overflow + if (currentOverflowWithElements || currentOverflowWithWhiteSpace) + { + currentOverflowWithElements = true; + } + + element.setToPrint(!((currentOverflowWithElements || willOverflowWithWhiteSpace) && currentOverflowAllowed));// don't use willOverflow() method as it is overridden at least in bands + } + + if (element.isToPrint()) + { + element.setRelativeY( + element.getY() + stretchHeight - getActualContainerHeight() + ); + } + } + } + //} + } + + + /** + * @deprecated To be removed. + */ + protected void _removeBlankElements() + { + JRElement[] remElems = removableElements; + if (remElems != null && remElems.length > 0) + { + JRElement[] elems = ySortedElements; + + for(int i = 0; i < remElems.length; i++) + { + JRFillElement iElem = (JRFillElement)remElems[i]; + + int blankHeight; + if (iElem.isToPrint()) + { + blankHeight = iElem.getHeight() - iElem.getStretchHeight(); + } + else + { + blankHeight = iElem.getHeight();//FIXME subreports that stretch and then don't print, will not remove all space + } + + if ( + blankHeight > 0 && + iElem.getRelativeY() + iElem.getStretchHeight() <= stretchHeight && + iElem.getRelativeY() >= firstY + ) + { + int blankY = iElem.getRelativeY() + iElem.getHeight() - blankHeight; + boolean isToRemove = true; + + for(int j = 0; j < elems.length; j++) + { + JRFillElement jElem = (JRFillElement)elems[j]; + + if (iElem != jElem && jElem.isToPrint()) + { + int top = + Math.min(blankY, jElem.getRelativeY()); + int bottom = + Math.max( + blankY + blankHeight, + jElem.getRelativeY() + jElem.getStretchHeight() + ); + + if (blankHeight + jElem.getStretchHeight() > bottom - top) + { + isToRemove = false; + break; + } + } + } + + if (isToRemove) + { + for(int j = 0; j < elems.length; j++) + { + JRFillElement jElem = (JRFillElement)elems[j]; + + if (jElem.getRelativeY() >= blankY + blankHeight) + { + jElem.setRelativeY(jElem.getRelativeY() - blankHeight); + } + } + + stretchHeight = stretchHeight - blankHeight; + } + } + } + } + } + + + /** + * + */ + protected void removeBlankElements() + { + if (isLegacyElementStretchEnabled()) + { + _removeBlankElements(); + return; + } + + if (removableElements != null && removableElements.length > 0) + { + for (JRFillElement remElem : removableElements) + { + int blankHeight; + if (remElem.isToPrint()) + { + blankHeight = remElem.getHeight() - remElem.getStretchHeight(); + } + else + { + blankHeight = remElem.getHeight();//FIXME subreports that stretch and then don't print, will not remove all space + } + + if ( + blankHeight > 0 && + remElem.getRelativeY() + remElem.getStretchHeight() <= stretchHeight && + remElem.getRelativeY() >= firstY + ) + { + int blankY = remElem.getRelativeY() + remElem.getHeight() - blankHeight; + boolean isToRemove = true; + + for (JRFillElement jElem : ySortedElements) + { + if (remElem != jElem && jElem.isToPrint()) + { + int top = + Math.min(blankY, jElem.getRelativeY()); + int bottom = + Math.max( + blankY + blankHeight, + jElem.getRelativeY() + jElem.getStretchHeight() + ); + + if (blankHeight + jElem.getStretchHeight() > bottom - top) + { + isToRemove = false; + break; + } + } + } + + if (isToRemove) + { + for (JRFillElement jElem : ySortedElements) + { + if (jElem.getRelativeY() + jElem.getStretchHeight() <= blankY) + { + jElem.setCollapsedHeightBelow(jElem.getCollapsedHeightBelow() + blankHeight); + } + + if (jElem.getRelativeY() >= blankY + blankHeight) + { + jElem.setCollapsedHeightAbove(jElem.getCollapsedHeightAbove() + blankHeight); + jElem.setRelativeY(jElem.getRelativeY() - blankHeight); + } + } + + stretchHeight = stretchHeight - blankHeight; + } + } + } + } + } + + + /** + * Fills the elements from this container into a print element container. + * + * @param printContainer the print element container + * @throws JRException + */ + public void fillElements(JRPrintElementContainer printContainer) throws JRException + { + //int maxStretch = 0; + //int stretch = 0; + int maxWidth = 0; + JRElement[] allElements = getElements(); + if (allElements != null && allElements.length > 0) + { + for(int i = 0; i < allElements.length; i++) + { + JRFillElement element = (JRFillElement)allElements[i]; + + element.setRelativeY(element.getRelativeY() - firstY); + + if (element.getRelativeY() + element.getStretchHeight() > stretchHeight - firstY) + { + element.setToPrint(false); + } + + element.setAlreadyPrinted(element.isToPrint() || element.isAlreadyPrinted()); + + if (element.isToPrint()) + { + JRPrintElement printElement = element.fill(); + //printElement.setY(printElement.getY() - firstY); + + if (printElement != null) + { + //FIXME not all elements affect height + //stretch = printElement.getY() + firstY + printElement.getHeight() - element.getY() - element.getHeight(); + //if (stretch > maxStretch) + //{ + // maxStretch = stretch; + //} + printContainer.addElement(printElement); + if (printElement.getX() + printElement.getWidth() > maxWidth) + { + maxWidth = printElement.getX() + printElement.getWidth(); + } + } + + if (element instanceof JRFillSubreport) + { + JRFillSubreport subreport = (JRFillSubreport)element; + + List styles = subreport.subreportFiller.getJasperPrint().getStylesList(); + for(int j = 0; j < styles.size(); j++) + { + filler.addPrintStyle(styles.get(j)); + } + + List origins = subreport.subreportFiller.getJasperPrint().getOriginsList(); + for(int j = 0; j < origins.size(); j++) + { + filler.getJasperPrint().addOrigin(origins.get(j)); + } + + Collection printElements = subreport.getPrintElements(); + addSubElements(printContainer, element, printElements); + if (subreport.getX() + subreport.getPrintContentsWidth() > maxWidth) + { + maxWidth = subreport.getX() + subreport.getPrintContentsWidth(); + } + + subreport.subreportPageFilled(); + } + + // crosstabs do not return a fill() element + if (element instanceof JRFillCrosstab) + { + JRFillCrosstab crosstab = (JRFillCrosstab) element; + List printElements = crosstab.getPrintElements(); + addSubElements(printContainer, element, printElements); + if (crosstab.getX() + crosstab.getPrintElementsWidth() > maxWidth) + { + maxWidth = crosstab.getX() + crosstab.getPrintElementsWidth(); + } + } + } + } + } + + //printBand.setHeight(getHeight() + maxStretch - firstY); + printContainer.setHeight(stretchHeight - firstY); + printContainer.setContentsWidth(maxWidth); + } + + + protected void addSubElements(JRPrintElementContainer printContainer, JRFillElement element, + Collection printElements) + { + if (printContainer instanceof OffsetElementsContainer) + { + // adding the subelements as whole lists to bands so that we don't need + // another virtualized list at print band level + ((OffsetElementsContainer) printContainer).addOffsetElements(printElements, + element.getX(), element.getRelativeY()); + } + else + { + if (printElements != null && printElements.size() > 0) + { + for(Iterator it = printElements.iterator(); it.hasNext();) + { + JRPrintElement printElement =it.next(); + printElement.setX(element.getX() + printElement.getX()); + printElement.setY(element.getRelativeY() + printElement.getY()); + printContainer.addElement(printElement); + } + } + } + } + + + /** + * + */ + protected void rewind() throws JRException + { + if (ySortedElements != null && ySortedElements.length > 0) + { + for(int i = 0; i < ySortedElements.length; i++) + { + JRFillElement element = ySortedElements[i]; + + element.rewind(); + + element.setAlreadyPrinted(false); + } + } + + willOverflowWithElements = false; + willOverflowWithWhiteSpace = false; + } + + protected int getFirstY() + { + return firstY; + } + + + /** + * Returns the actual height of the element container. + * Some element containers such as frames have a larger calculated container height, resulting from content being placed beyond container declared height. + * + * @return the height of the element container + */ + protected abstract int getActualContainerHeight(); + + + /** + * Returns the height of the element container. + * + * @return the height of the element container + */ + protected abstract int getContainerHeight(); + + + /** + * Find all styles containing conditional styles which are referenced by elements in this band. + */ + protected void initConditionalStyles() + { + filler.addDefaultStyleListener(new JRBaseFiller.DefaultStyleListener(){ + @Override + public void defaultStyleSet(JRStyle style) + { + collectConditionalStyle(style); + } + }); + + for (int i = 0; i < deepElements.length; i++) + { + JRStyle style = deepElements[i].initStyle; + collectConditionalStyle(style); + } + + if (deepElements.length > 0) + { + for(int i = 0; i < deepElements.length; i++) + { + deepElements[i].setConditionalStylesContainer(this); + } + } + } + + protected void collectConditionalStyle(JRStyle style) + { + if (style != null)// && style.getConditionalStyles() != null) + { + stylesToEvaluate.add(style); + } + } + + + protected void evaluateConditionalStyles(byte evaluation) throws JRException + { + for (Iterator it = stylesToEvaluate.iterator(); it.hasNext();) + { + evaluateConditionalStyle(it.next(), evaluation); + } + } + + + protected JRStyle evaluateConditionalStyle(JRStyle initialStyle, byte evaluation) throws JRException + { + JRStyle consolidatedStyle = initialStyle; + + StringBuilder code = new StringBuilder(); + List condStylesToApply = new ArrayList<>(); + + boolean anyTrue = buildConsolidatedStyle(initialStyle, evaluation, code, condStylesToApply); + + if (anyTrue) + { + String consolidatedStyleName = initialStyle.getName() + "|" + code.toString(); + consolidatedStyle = filler.getJasperPrint().getStylesMap().get(consolidatedStyleName); + if (consolidatedStyle == null) + { + JRBaseStyle style = new JRBaseStyle(initialStyle.getDefaultStyleProvider(), consolidatedStyleName); + for (int j = condStylesToApply.size() - 1; j >= 0; j--) + { + StyleUtil.appendStyle(style, condStylesToApply.get(j)); + } + + // deduplicate to previously created identical instances + style = filler.fillContext.deduplicate(style); + filler.addPrintStyle(style); + + consolidatedStyle = style; + } + } + + evaluatedStyles.put(initialStyle, consolidatedStyle); + + return consolidatedStyle; + } + + + protected boolean buildConsolidatedStyle(JRStyle style, byte evaluation, StringBuilder code, List condStylesToApply) throws JRException + { + boolean anyTrue = false; + + JRConditionalStyle[] conditionalStyles = style.getConditionalStyles(); + if (conditionalStyles != null && conditionalStyles.length > 0) + { + for (int j = 0; j < conditionalStyles.length; j++) + { + JRConditionalStyle conditionalStyle = conditionalStyles[j]; + Boolean expressionValue = + (Boolean) expressionEvaluator.evaluate( + conditionalStyle.getConditionExpression(), + evaluation + ); + + boolean condition; + if (expressionValue == null) + { + condition = false; + } + else + { + condition = expressionValue; + } + + code.append(condition ? '1' : '0'); + anyTrue = anyTrue | condition; + + if (condition) + { + condStylesToApply.add(conditionalStyle); + } + } + } + + condStylesToApply.add(style); + + if (style.getStyle() != null) + { + anyTrue = anyTrue | buildConsolidatedStyle(style.getStyle(), evaluation, code, condStylesToApply); + } + return anyTrue; + } + + + public JRStyle getEvaluatedConditionalStyle(JRStyle parentStyle) + { + return evaluatedStyles.get(parentStyle); + } + + protected final void setElementOriginProvider(JROriginProvider originProvider) + { + if (originProvider != null) + { + for (int i = 0; i < deepElements.length; i++) + { + deepElements[i].setOriginProvider(originProvider); + } + } + } } From 451e52fd9515d388b6e48ffdf0fab2e4f4e951eb Mon Sep 17 00:00:00 2001 From: Yoseph Phillips Date: Mon, 25 Dec 2023 10:00:06 +1100 Subject: [PATCH 2/2] Reduce memory usage and improve performance - replace 2 spaces with tabs --- .../engine/fill/JRFillElement.java | 3862 ++++++++--------- .../engine/fill/JRFillElementContainer.java | 2504 +++++------ 2 files changed, 3183 insertions(+), 3183 deletions(-) diff --git a/jasperreports/src/net/sf/jasperreports/engine/fill/JRFillElement.java b/jasperreports/src/net/sf/jasperreports/engine/fill/JRFillElement.java index df89ee0dc5..3795d54205 100644 --- a/jasperreports/src/net/sf/jasperreports/engine/fill/JRFillElement.java +++ b/jasperreports/src/net/sf/jasperreports/engine/fill/JRFillElement.java @@ -74,1935 +74,1935 @@ */ public abstract class JRFillElement implements JRElement, JRFillCloneable, JRStyleSetter, DynamicPropertiesHolder { - /** - * - */ - public static final String EXCEPTION_MESSAGE_KEY_INVALID_BOOKMARK_LEVEL = "fill.anchor.bookmark.level.invalid"; - - - /** - * - */ - protected JRElement parent; - protected List propertyExpressions; - protected List dynamicTransferProperties; - protected JRStyle providerStyle; - protected Map templates = new HashMap<>(); - protected List styleProviders; - - /** - * - */ - protected JRBaseFiller filler; - protected JRFillExpressionEvaluator expressionEvaluator; - - protected JRDefaultStyleProvider defaultStyleProvider; - - /** - * - */ - protected JRGroup printWhenGroupChanges; - protected JRFillElementGroup elementGroup; - - /** - * - */ - protected JRFillBand band; - - protected JROriginProvider originProvider; - - protected PrintElementOriginator printElementOriginator; - - /** - * - */ - private boolean isPrintWhenExpressionNull = true; - private boolean isPrintWhenTrue = true; - private boolean isToPrint = true; - private boolean isReprinted; - private boolean isAlreadyPrinted; - private Collection dependantElements = new ArrayList<>(); - private int relativeY; - private int collapsedHeightAbove; - private int collapsedHeightBelow; - /** - * Keeps total stretch height, including forced stretch after honoring the stretchType property of the element. - */ - private int stretchHeight; - /** - * Keeps the natural stretch height calculated during element prepare. - */ - private int prepareHeight; - - private int x; - private int y; - private int width; - private int height; - - private boolean isValueRepeating; - - protected byte currentEvaluation; - - // used by elements that support evaluationTime=Auto - protected Map delayedEvaluationsMap; - - protected JRFillElementContainer conditionalStylesContainer; - protected FillContainerContext fillContainerContext; - - protected JRStyle initStyle; - protected JRStyle exprStyle; - protected JRStyle currentStyle; - - /** - * Flag indicating whether the element is shrinkable. - * @see #setShrinkable(boolean) - */ - private boolean shrinkable; - - protected JRPropertiesMap staticProperties; - protected JRPropertiesMap staticTransferProperties; - protected JRPropertiesMap dynamicProperties; - protected JRPropertiesMap mergedProperties; - - protected boolean hasDynamicPopulateTemplateStyle; - protected Boolean defaultPopulateTemplateStyle; - - /** - * - * - private JRElement topElementInGroup; - private JRElement bottomElementInGroup; - - - /** - * - */ - protected JRFillElement( - JRBaseFiller filler, - JRElement element, - JRFillObjectFactory factory - ) - { - factory.put(element, this); - - this.parent = element; - this.filler = filler; - this.expressionEvaluator = factory.getExpressionEvaluator(); - this.defaultStyleProvider = factory.getDefaultStyleProvider(); - - printElementOriginator = filler.assignElementId(this); - - /* */ - printWhenGroupChanges = factory.getGroup(element.getPrintWhenGroupChanges()); - elementGroup = (JRFillElementGroup)factory.getVisitResult(element.getElementGroup()); - - x = element.getX(); - y = element.getY(); - width = element.getWidth(); - height = element.getHeight(); - - staticProperties = element.hasProperties() ? element.getPropertiesMap().cloneProperties() : null; - staticTransferProperties = findStaticTransferProperties(); - mergedProperties = staticProperties; - - JRPropertyExpression[] elementPropertyExpressions = element.getPropertyExpressions(); - propertyExpressions = elementPropertyExpressions == null ? new ArrayList<>(0) - : new ArrayList<>(Arrays.asList(elementPropertyExpressions)); - - dynamicTransferProperties = findDynamicTransferProperties(); - - factory.registerDelayedStyleSetter(this, parent); - - initStyleProviders(); - lookForPartProperty(staticProperties); - - hasDynamicPopulateTemplateStyle = hasDynamicProperty(PROPERTY_ELEMENT_TEMPLATE_POPULATE_STYLE); - } - - - protected JRFillElement(JRFillElement element, JRFillCloneFactory factory) - { - factory.put(element, this); - - this.parent = element.parent; - this.filler = element.filler; - this.expressionEvaluator = element.expressionEvaluator; - this.defaultStyleProvider = element.defaultStyleProvider; - this.originProvider = element.originProvider; - - printElementOriginator = element.printElementOriginator; - - /* */ - printWhenGroupChanges = element.printWhenGroupChanges; - elementGroup = (JRFillElementGroup) factory.getClone((JRFillElementGroup) element.getElementGroup()); - - x = element.getX(); - y = element.getY(); - width = element.getWidth(); - height = element.getHeight(); - - templates = element.templates; - - initStyle = element.initStyle; - - shrinkable = element.shrinkable; - - staticProperties = element.staticProperties == null ? null : element.staticProperties.cloneProperties(); - staticTransferProperties = element.staticTransferProperties; - mergedProperties = staticProperties; - this.propertyExpressions = new ArrayList<>(element.propertyExpressions); - this.dynamicTransferProperties = element.dynamicTransferProperties; - - // we need a style provider context for this element instance - initStyleProviders(); - - this.hasDynamicPopulateTemplateStyle = element.hasDynamicPopulateTemplateStyle; - this.defaultPopulateTemplateStyle = element.defaultPopulateTemplateStyle; - } - - private JRPropertiesMap findStaticTransferProperties() - { - if (staticProperties == null) - { - return null; - } - - String[] propertyNames = staticProperties.getPropertyNames(); - List prefixes = filler.getPrintTransferPropertyPrefixes(); - JRPropertiesMap transferProperties = new JRPropertiesMap(); - for (int i = 0; i < propertyNames.length; i++) - { - for (String prefix : prefixes) - { - String prop = propertyNames[i]; - if (prop.startsWith(prefix)) - { - transferProperties.setProperty(prop, staticProperties.getProperty(prop)); - break; - } - } - } - return transferProperties.isEmpty() ? null : transferProperties; - } - - private List findDynamicTransferProperties() - { - if (propertyExpressions.isEmpty()) - { - return null; - } - - List prefixes = filler.getPrintTransferPropertyPrefixes(); - List transferProperties = new ArrayList<>(propertyExpressions.size()); - for (JRPropertyExpression propertyExpression : propertyExpressions) - { - String propertyName = propertyExpression.getName(); - for (String prefix : prefixes) - { - if (propertyName.startsWith(prefix)) - { - transferProperties.add(propertyName); - break; - } - } - } - return transferProperties; - } - - private void lookForPartProperty(JRPropertiesMap properties) - { - if (properties != null && properties.getProperty(PrintPart.ELEMENT_PROPERTY_PART_NAME) != null) - { - filler.getFillContext().setDetectParts(true); - } - } - - @Override - public JRDefaultStyleProvider getDefaultStyleProvider() - { - return defaultStyleProvider; - } - - /** - * - */ - protected StyleResolver getStyleResolver() - { - return getDefaultStyleProvider().getStyleResolver(); - } - - @Override - public UUID getUUID() - { - return parent.getUUID(); - } - - @Override - public String getKey() - { - return parent.getKey(); - } - - @Override - public PositionTypeEnum getPositionTypeValue() - { - return parent.getPositionTypeValue();//FIXME optimize this by consolidating style properties - } - - @Override - public void setPositionType(PositionTypeEnum positionType) - { - throw new UnsupportedOperationException(); - } - - @Override - public StretchTypeEnum getStretchTypeValue() - { - return parent.getStretchTypeValue(); - } - - @Override - public void setStretchType(StretchTypeEnum stretchType) - { - throw new UnsupportedOperationException(); - } - - @Override - public boolean isPrintRepeatedValues() - { - return parent.isPrintRepeatedValues(); - } - - @Override - public void setPrintRepeatedValues(boolean isPrintRepeatedValues) - { - } - - @Override - public ModeEnum getModeValue() - { - return getStyleResolver().getMode(this, ModeEnum.OPAQUE); - } - - @Override - public ModeEnum getOwnModeValue() - { - return providerStyle == null || providerStyle.getOwnModeValue() == null ? parent.getOwnModeValue() : providerStyle.getOwnModeValue(); - } - - @Override - public void setMode(ModeEnum modeValue) - { - } - - @Override - public int getX() - { - return x; - } - - @Override - public void setX(int x) - { - this.x = x; - } - - /** - * - */ - public void setY(int y) - { - this.y = y; - } - - @Override - public int getY() - { - return y; - } - - @Override - public int getWidth() - { - return width; - } - - @Override - public void setWidth(int width) - { - this.width = width; - } - - /** - * - */ - public void setHeight(int height) - { - this.height = height; - } - - @Override - public int getHeight() - { - return height; - } - - @Override - public boolean isRemoveLineWhenBlank() - { - return parent.isRemoveLineWhenBlank(); - } - - @Override - public void setRemoveLineWhenBlank(boolean isRemoveLine) - { - } - - @Override - public boolean isPrintInFirstWholeBand() - { - return parent.isPrintInFirstWholeBand(); - } - - @Override - public void setPrintInFirstWholeBand(boolean isPrint) - { - } - - @Override - public boolean isPrintWhenDetailOverflows() - { - return parent.isPrintWhenDetailOverflows(); - } - - @Override - public void setPrintWhenDetailOverflows(boolean isPrint) - { - } - - @Override - public Color getForecolor() - { - return getStyleResolver().getForecolor(this); - } - - @Override - public Color getOwnForecolor() - { - return providerStyle == null || providerStyle.getOwnForecolor() == null ? parent.getOwnForecolor() : providerStyle.getOwnForecolor(); - } - - @Override - public void setForecolor(Color forecolor) - { - } - - @Override - public Color getBackcolor() - { - return getStyleResolver().getBackcolor(this); - } - - @Override - public Color getOwnBackcolor() - { - return providerStyle == null || providerStyle.getOwnBackcolor() == null ? parent.getOwnBackcolor() : providerStyle.getOwnBackcolor(); - } - - @Override - public void setBackcolor(Color backcolor) - { - } - - @Override - public JRExpression getStyleExpression() - { - return parent.getStyleExpression(); - } - - @Override - public JRExpression getPrintWhenExpression() - { - return parent.getPrintWhenExpression(); - } - - @Override - public JRGroup getPrintWhenGroupChanges() - { - return printWhenGroupChanges; - } - - @Override - public JRElementGroup getElementGroup() - { - return elementGroup; - } - - /** - * - */ - protected boolean isPrintWhenExpressionNull() - { - return isPrintWhenExpressionNull; - } - - /** - * - */ - protected void setPrintWhenExpressionNull(boolean isPrintWhenExpressionNull) - { - this.isPrintWhenExpressionNull = isPrintWhenExpressionNull; - } - - /** - * - */ - protected boolean isPrintWhenTrue() - { - return isPrintWhenTrue; - } - - /** - * - */ - protected void setPrintWhenTrue(boolean isPrintWhenTrue) - { - this.isPrintWhenTrue = isPrintWhenTrue; - } - - /** - * - */ - public boolean isToPrint() - { - return isToPrint; - } - - /** - * - */ - protected void setToPrint(boolean isToPrint) - { - this.isToPrint = isToPrint; - } - - /** - * - */ - protected boolean isReprinted() - { - return isReprinted; - } - - /** - * - */ - protected void setReprinted(boolean isReprinted) - { - this.isReprinted = isReprinted; - } - - /** - * - */ - public boolean isAlreadyPrinted() - { - return isAlreadyPrinted; - } - - /** - * - */ - public void setAlreadyPrinted(boolean isAlreadyPrinted) - { - this.isAlreadyPrinted = isAlreadyPrinted; - } - - /** - * - */ - protected JRElement[] getGroupElements() - { - JRElement[] groupElements = null; - - if (elementGroup != null) - { - groupElements = elementGroup.getElements(); - } - - return groupElements; - } - - /** - * - */ - protected Collection getDependantElements() - { - return dependantElements; - } - - /** - * - */ - protected void addDependantElement(JRFillElement element) - { - dependantElements.add(element); - } - - /** - * - */ - protected int getRelativeY() - { - return relativeY; - } - - /** - * - */ - protected void setRelativeY(int relativeY) - { - this.relativeY = relativeY; - } - - /** - * - */ - protected int getCollapsedHeightAbove() - { - return collapsedHeightAbove; - } - - /** - * - */ - protected void setCollapsedHeightAbove(int collapsedHeightAbove) - { - this.collapsedHeightAbove = collapsedHeightAbove; - } - - /** - * - */ - protected int getCollapsedHeightBelow() - { - return collapsedHeightBelow; - } - - /** - * - */ - protected void setCollapsedHeightBelow(int collapsedHeightBelow) - { - this.collapsedHeightBelow = collapsedHeightBelow; - } - - /** - * - */ - public int getStretchHeight() - { - return stretchHeight; - } - - /** - * - */ - protected void setStretchHeight(int stretchHeight) - { - if (stretchHeight > getHeight() || (shrinkable && isRemoveLineWhenBlank())) - { - this.stretchHeight = stretchHeight; - } - else - { - this.stretchHeight = getHeight(); - } - } - - /** - * - */ - public int getPrepareHeight() - { - return prepareHeight; - } - - /** - * Element height is calculated in two phases. - * First, the element stretches on its own during prepare, to accommodate all its content. - * This is the natural stretch and we keep track of the calculated height in prepareHeight. - * Secondly, the element stretches further in accordance with its stretchType property. - * This forced stretch occurs at a later time and the amount of stretch is kept in stretchHeight. - */ - protected void setPrepareHeight(int prepareHeight) - { - this.prepareHeight = prepareHeight; - - setStretchHeight(prepareHeight); - } - - /** - * - */ - protected JRFillBand getBand() - { - return band; - } - - /** - * - */ - protected void setBand(JRFillBand band) - { - this.band = band; - - if (this.originProvider == null) - { - setOriginProvider(band); - } - } - - - /** - * - */ - protected void initStyleProviders() - { - List styleProviderFactories = filler.getJasperReportsContext().getExtensions(StyleProviderFactory.class); - if (styleProviderFactories != null && styleProviderFactories.size() > 0) - { - FillStyleProviderContext styleProviderContext = new FillStyleProviderContext(this); - for (StyleProviderFactory styleProviderFactory : styleProviderFactories) - { - StyleProvider styleProvider = styleProviderFactory.getStyleProvider(styleProviderContext, filler.getJasperReportsContext()); - if (styleProvider != null) - { - if (styleProviders == null) - { - styleProviders = new ArrayList<>(); - } - styleProviders.add(styleProvider); - } - } - } - } - - - /** - * - */ - protected void reset() - { - relativeY = y; - collapsedHeightAbove = 0; - collapsedHeightBelow = 0; - stretchHeight = height; - prepareHeight = height; - - if (elementGroup != null) - { - elementGroup.reset(); - } - } - - protected void setCurrentEvaluation(byte evaluation) - { - currentEvaluation = evaluation; - } - - /** - * - */ - protected abstract void evaluate( - byte evaluation - ) throws JRException; - - - /** - * - */ - protected void evaluateStyle( - byte evaluation - ) throws JRException - { - exprStyle = null; - - JRExpression styleExpression = getStyleExpression(); - if (styleExpression != null) - { - String styleName = (String)evaluateExpression(styleExpression, evaluation); - if (styleName != null) - { - exprStyle = getFiller().factory.stylesMap.getStyle(styleName); - if (exprStyle == null) - { - throw - new JRRuntimeException( - JRFillObjectFactory.EXCEPTION_MESSAGE_KEY_STYLE_NOT_FOUND, - new Object[]{styleName} - ); - } - else - { - conditionalStylesContainer.collectConditionalStyle(exprStyle); - } - } - } - - if (exprStyle != null && isEvaluateNow()) - { - conditionalStylesContainer.evaluateConditionalStyle(exprStyle, evaluation); - initStyle = exprStyle; - } - - providerStyle = null; - - if (styleProviders != null && styleProviders.size() > 0) - { - for (StyleProvider styleProvider : styleProviders) - { - JRStyle style = styleProvider.getStyle(evaluation); - if (style != null) - { - if (providerStyle == null) - { - providerStyle = new JRBaseStyle(); - } - StyleUtil.appendStyle(providerStyle, style); - } - } - } - } - - protected TimeZone getTimeZone() - { - return filler.getTimeZone(); - } - - /** - * - */ - protected void evaluatePrintWhenExpression( - byte evaluation - ) throws JRException - { - boolean isExprNull = true; - boolean isExprTrue = false; - - JRExpression expression = getPrintWhenExpression(); - if (expression != null) - { - isExprNull = false; - Boolean printWhenExpressionValue = (Boolean) evaluateExpression(expression, evaluation); - if (printWhenExpressionValue == null) - { - isExprTrue = false; - } - else - { - isExprTrue = printWhenExpressionValue; - } - } - - setPrintWhenExpressionNull(isExprNull); - setPrintWhenTrue(isExprTrue); - } - - - /** - * - */ - protected abstract void rewind() throws JRException; - - - /** - * - */ - protected abstract JRPrintElement fill() throws JRException; - - protected JRTemplateElement getElementTemplate() - { - JRTemplateElement template = null; - JRStyle style = null; - - if (providerStyle == null) - { - // no style provider has been used so we can use cache template per style below - style = getStyle(); - template = getTemplate(style); - } - - if (template == null) - { - template = createElementTemplate(); - transferProperties(template); - - if (toPopulateTemplateStyle()) - { - template.populateStyle(); - } - - // deduplicate to previously created identical objects - template = filler.fillContext.deduplicate(template); - - if (providerStyle == null) - { - registerTemplate(style, template); - } - } - return template; - } - - protected abstract JRTemplateElement createElementTemplate(); - - protected boolean toPopulateTemplateStyle() - { - if (defaultPopulateTemplateStyle == null) - { - defaultPopulateTemplateStyle = filler.getPropertiesUtil().getBooleanProperty( - PROPERTY_ELEMENT_TEMPLATE_POPULATE_STYLE, false, - parent, filler.getMainDataset()); - } - - boolean populate = defaultPopulateTemplateStyle; - if (hasDynamicPopulateTemplateStyle) - { - String populateProp = getDynamicProperties().getProperty( - PROPERTY_ELEMENT_TEMPLATE_POPULATE_STYLE); - if (populateProp != null) - { - populate = JRPropertiesUtil.asBoolean(populateProp); - } - } - return populate; - } - - /** - * - */ - protected boolean prepare( - int availableHeight, - boolean isOverflow - ) throws JRException - { - if ( - isPrintWhenExpressionNull() || - ( !isPrintWhenExpressionNull() && - isPrintWhenTrue() ) - ) - { - setToPrint(true); - } - else - { - setToPrint(false); - } - - setReprinted(false); - - return false; - } - - - /** - * @deprecated To be removed. - */ - protected void _stretchElement(int bandStretch) - { - switch (getStretchTypeValue()) - { - case RELATIVE_TO_BAND_HEIGHT : - case CONTAINER_HEIGHT : - case CONTAINER_BOTTOM : - { - _stretchElementToHeight(getHeight() + bandStretch); - break; - } - case RELATIVE_TO_TALLEST_OBJECT : - case ELEMENT_GROUP_HEIGHT : - case ELEMENT_GROUP_BOTTOM : - { - if (elementGroup != null) - { - //setStretchHeight(getHeight() + getStretchHeightDiff()); - _stretchElementToHeight(getHeight() + elementGroup.getStretchHeightDiff()); - } - - break; - } - case NO_STRETCH : - default : - { - break; - } - } - } - - /** - * @deprecated To be removed. - */ - protected void _stretchElementToHeight(int stretchHeight) - { - if (stretchHeight > getStretchHeight()) - { - setStretchHeight(stretchHeight); - } - } - - - /** - * - */ - @SuppressWarnings("deprecation") - protected boolean stretchElement(int containerStretch) - { - boolean applied = false; - switch (getStretchTypeValue()) - { - case RELATIVE_TO_BAND_HEIGHT : - case CONTAINER_HEIGHT : - case CONTAINER_BOTTOM : - { - applied = stretchElementToContainer(containerStretch); - break; - } - case RELATIVE_TO_TALLEST_OBJECT : - case ELEMENT_GROUP_HEIGHT : - case ELEMENT_GROUP_BOTTOM : - { - applied = stretchElementToElementGroup(); - break; - } - case NO_STRETCH : - default : - { - break; - } - } - return applied; - } - - - /** - * - */ - @SuppressWarnings("deprecation") - protected boolean stretchElementToContainer(int containerStretch)//TODO subtract firstY? - { - boolean applied = false; - switch (getStretchTypeValue()) - { - case RELATIVE_TO_BAND_HEIGHT : - case CONTAINER_HEIGHT : - { - applied = stretchElementToHeight(getHeight() + containerStretch); - break; - } - case CONTAINER_BOTTOM : - { - applied = stretchElementToHeight(getY() - getRelativeY() + getHeight() + containerStretch); - break; - } - default : - } - return applied; - } - - - /** - * - */ - @SuppressWarnings("deprecation") - protected boolean stretchElementToElementGroup() - { - boolean applied = false; - if (elementGroup != null) - { - switch (getStretchTypeValue()) - { - case RELATIVE_TO_TALLEST_OBJECT : - case ELEMENT_GROUP_HEIGHT : - { - applied = stretchElementToHeight(getHeight() + elementGroup.getStretchHeightDiff()); - break; - } - case ELEMENT_GROUP_BOTTOM : - { - applied = stretchElementToHeight(getY() - getRelativeY() + getHeight() + elementGroup.getStretchHeightDiff()); - break; - } - default : - } - } - return applied; - } - - - /** - * This method returns a boolean signaling if any stretch change occurred. - * It does not say which amount of stretch was applied, but that is OK, because the only place where this is checked - * is during frame cascading stretch, where the stretchHeight field of the frame (set here) is used directly. - */ - protected boolean stretchElementToHeight(int stretchHeight) - { - // cannot force the element to shrink below its natural stretch calculated during prepare; - // such situation could occur when the container breaks and overflows - boolean applied = false; - if (stretchHeight > getPrepareHeight()) - { - // any new stretchHeight that is greater than element's natural growth is fine - setStretchHeight(stretchHeight); - applied = true; - } - return applied; - } - - - /** - * @deprecated To be removed. - */ - protected void _moveDependantElements() - { - Collection elements = getDependantElements(); - if (elements != null && elements.size() > 0) - { - for (JRFillElement element : elements) - { - int newRelativeY = - getRelativeY() + getStretchHeight() //pusher element current bottom edge - + (element.getY() - (getY() + getHeight())); //design time distance between elements; difference between float element top edge and pusher element bottom edge - - if (newRelativeY > element.getRelativeY()) - { - element.setRelativeY(newRelativeY); - } - } - } - } - - - /** - * - */ - protected void moveDependantElements() { - Collection elements = getDependantElements(); - if (elements != null && !elements.isEmpty()) { - int offset = getRelativeY() + getStretchHeight() - getY() - getHeight() + getCollapsedHeightAbove(); - for (JRFillElement element : elements) { - int newRelativeY = offset + element.getY() - element.getCollapsedHeightAbove(); - if (newRelativeY > element.getRelativeY()) { - element.setRelativeY(newRelativeY); - } - } - } - } - - - /** - * Resolves an element. - * - * @param element the element - * @param evaluation the evaluation type - */ - protected abstract void resolveElement(JRPrintElement element, byte evaluation) throws JRException; - - protected void performDelayedEvaluation(JRPrintElement element, byte evaluation) - throws JRException - { - evaluateProperties(evaluation); - evaluateStyle(evaluation); - - boolean updateTemplate = false; - - JRStyle printStyle = element.getStyle(); - if (isDelayedStyleEvaluation() || exprStyle != null) - { - if (exprStyle != null) - { - initStyle = exprStyle; - } - - JRStyle elementStyle = initStyle; - if (elementStyle == null) - { - elementStyle = filler.getDefaultStyle(); - } - - if (elementStyle != null) - { - JRStyle evaluatedStyle = conditionalStylesContainer.evaluateConditionalStyle( - elementStyle, evaluation); - // if the evaluated style differs from the existing style - if (evaluatedStyle != printStyle) - { - // set the evaluated style as element style - printStyle = evaluatedStyle; - - updateTemplate = true; - } - } - } - - // set the current element style - this.currentStyle = printStyle; - - resolveElement(element, evaluation); - - if (updateTemplate || providerStyle != null - || delayedEvaluationUpdatesTemplate()) - { - // get/create an element template that corresponds to the - // current style - JRTemplateElement newTemplate = getElementTemplate(); - ((JRTemplatePrintElement) element).updateElementTemplate( - newTemplate); - } - - // reset the current style - this.currentStyle = null; - //this.providerStyle = null; - } - - protected boolean delayedEvaluationUpdatesTemplate() - { - return false; - } - - - /** - * Evaluates an expression. - * - * @param expression the expression - * @param evaluation the evaluation type - * @return the evaluation result - * @throws JRException - */ - public final Object evaluateExpression(JRExpression expression, byte evaluation) throws JRException - { - return expressionEvaluator.evaluate(expression, evaluation); - } - - - /** - * Decides whether the value for this element is repeating. - *

- * Dynamic elements should call {@link #setValueRepeating(boolean) setValueRepeating(boolean)} on - * {@link #evaluate(byte) evaluate(byte)}. Static elements don't have to do anything, this method - * will return true by default. - * - * @return whether the value for this element is repeating - * @see #setValueRepeating(boolean) - */ - protected boolean isValueRepeating() - { - return isValueRepeating; - } - - - /** - * Sets the repeating flag for this element. - *

- * This method should be called by dynamic elements on {@link #evaluate(byte) evaluate(byte)}. - * - * @param isValueRepeating whether the value of the element is repeating - * @see #isValueRepeating() - */ - protected void setValueRepeating(boolean isValueRepeating) - { - this.isValueRepeating = isValueRepeating; - } - - - protected JRFillVariable getVariable(String variableName) - { - return filler.getVariable(variableName); - } - - - protected JRFillField getField(String fieldName) - { - return filler.getField(fieldName); - } - - // default for elements not supporting evaluationTime - protected EvaluationTimeEnum getEvaluationTimeValue() - { - return EvaluationTimeEnum.NOW; - } - - /** - * Resolves an element. - * - * @param element the element - * @param evaluation the evaluation type - * @param evaluationTime the current evaluation time - */ - protected void resolveElement(JRPrintElement element, byte evaluation, JREvaluationTime evaluationTime) throws JRException - { - EvaluationTimeEnum evaluationTimeType = getEvaluationTimeValue(); - switch (evaluationTimeType) - { - case NOW: - break; - case AUTO: - delayedEvaluate((JRRecordedValuesPrintElement) element, evaluationTime, evaluation); - break; - default: - performDelayedEvaluation(element, evaluation); - break; - } - } - - private static class DelayedEvaluations implements Serializable - { - private static final long serialVersionUID = JRConstants.SERIAL_VERSION_UID; - - final Set fields; - final Set variables; - - DelayedEvaluations() - { - fields = new HashSet<>(); - variables = new HashSet<>(); - } - } - - protected void initDelayedEvaluations() - { - if (getEvaluationTimeValue() == EvaluationTimeEnum.AUTO && delayedEvaluationsMap == null) - { - delayedEvaluationsMap = new HashMap<>(); - collectDelayedEvaluations(); - } - } - - protected void collectDelayedEvaluations() - { - if (isDelayedStyleEvaluation()) - { - collectStyleDelayedEvaluations(); - collectStyleProviderDelayedEvaluations(); - } - } - - protected void collectStyleDelayedEvaluations() - { - JRStyle elementStyle = initStyle; - if (elementStyle == null) - { - elementStyle = filler.getDefaultStyle(); - } - - if (elementStyle != null) - { - JRStyle style = elementStyle; - while (style != null) - { - collectDelayedEvaluations(style); - - // proceed to the parent style - style = style.getStyle(); - } - } - } - - protected void collectDelayedEvaluations(JRStyle style) - { - JRConditionalStyle[] conditionalStyles = style.getConditionalStyles(); - // collect delayed evaluations from conditional style expressions - if (conditionalStyles != null && conditionalStyles.length > 0) - { - for (int i = 0; i < conditionalStyles.length; i++) - { - collectDelayedEvaluations( - conditionalStyles[i].getConditionExpression()); - } - } - } - - - protected void collectDelayedEvaluations(JRExpression expression) - { - if (expression != null) - { - JRExpressionChunk[] chunks = expression.getChunks(); - if (chunks != null) - { - for (int i = 0; i < chunks.length; i++) - { - JRExpressionChunk chunk = chunks[i]; - switch (chunk.getType()) - { - case JRExpressionChunk.TYPE_FIELD: - { - DelayedEvaluations delayedEvaluations = getDelayedEvaluations(JREvaluationTime.EVALUATION_TIME_NOW); - delayedEvaluations.fields.add(chunk.getText()); - break; - } - case JRExpressionChunk.TYPE_VARIABLE: - { - JREvaluationTime time = autogetVariableEvaluationTime(chunk.getText()); - DelayedEvaluations delayedEvaluations = getDelayedEvaluations(time); - delayedEvaluations.variables.add(chunk.getText()); - break; - } - default: - } - } - } - } - } - - - protected void collectStyleProviderDelayedEvaluations() - { - if (styleProviders != null && styleProviders.size() > 0) - { - for (StyleProvider styleProvider : styleProviders) - { - String[] fields = styleProvider.getFields(); - if (fields != null && fields.length > 0) - { - DelayedEvaluations delayedEvaluations = getDelayedEvaluations(JREvaluationTime.EVALUATION_TIME_NOW); - for (String field : fields) - { - delayedEvaluations.fields.add(field); - } - } - String[] variables = styleProvider.getVariables(); - if (variables != null && variables.length > 0) - { - for (String variable : variables) - { - JREvaluationTime time = autogetVariableEvaluationTime(variable); - DelayedEvaluations delayedEvaluations = getDelayedEvaluations(time); - delayedEvaluations.variables.add(variable); - } - } - } - } - } - - - private DelayedEvaluations getDelayedEvaluations(JREvaluationTime time) - { - DelayedEvaluations delayedEvaluations = delayedEvaluationsMap.get(time); - if (delayedEvaluations == null) - { - delayedEvaluations = new DelayedEvaluations(); - delayedEvaluationsMap.put(time, delayedEvaluations); - } - return delayedEvaluations; - } - - - private JREvaluationTime autogetVariableEvaluationTime(String variableName) - { - JRFillVariable variable = getVariable(variableName); - JREvaluationTime evaluationTime; - switch (variable.getResetTypeValue()) - { - case REPORT: - evaluationTime = JREvaluationTime.EVALUATION_TIME_REPORT; - break; - case MASTER: - evaluationTime = JREvaluationTime.EVALUATION_TIME_MASTER; - break; - case PAGE: - evaluationTime = JREvaluationTime.EVALUATION_TIME_PAGE; - break; - case COLUMN: - evaluationTime = JREvaluationTime.EVALUATION_TIME_COLUMN; - break; - case GROUP: - evaluationTime = JREvaluationTime.getGroupEvaluationTime(variable.getResetGroup().getName()); - break; - default: - evaluationTime = JREvaluationTime.EVALUATION_TIME_NOW; - break; - } - - if (!evaluationTime.equals(JREvaluationTime.EVALUATION_TIME_NOW) && - band.isNowEvaluationTime(evaluationTime)) - { - evaluationTime = JREvaluationTime.EVALUATION_TIME_NOW; - } - - if (variable.getCalculationValue() == CalculationEnum.SYSTEM && - evaluationTime.equals(JREvaluationTime.EVALUATION_TIME_NOW) && - band.isVariableUsedInReturns(variableName)) - { - evaluationTime = JREvaluationTime.getBandEvaluationTime(band); - } - - return evaluationTime; - } - - - protected void initDelayedEvaluationPrint(JRRecordedValuesPrintElement printElement) throws JRException - { - for (Iterator it = delayedEvaluationsMap.keySet().iterator(); it.hasNext();) - { - JREvaluationTime evaluationTime = it.next(); - if (!evaluationTime.equals(JREvaluationTime.EVALUATION_TIME_NOW)) - { - filler.addBoundElement(this, printElement, evaluationTime); - } - } - - printElement.initRecordedValues(delayedEvaluationsMap.keySet()); - - if (delayedEvaluationsMap.containsKey(JREvaluationTime.EVALUATION_TIME_NOW)) - { - delayedEvaluate(printElement, JREvaluationTime.EVALUATION_TIME_NOW, currentEvaluation); - } - } - - - protected void delayedEvaluate(JRRecordedValuesPrintElement printElement, JREvaluationTime evaluationTime, byte evaluation) throws JRException - { - JRRecordedValues recordedValues = printElement.getRecordedValues(); - if (!recordedValues.lastEvaluationTime()) - { - DelayedEvaluations delayedEvaluations = delayedEvaluationsMap.get(evaluationTime); - - for (Iterator it = delayedEvaluations.fields.iterator(); it.hasNext();) - { - String fieldName = it.next(); - JRFillField field = getField(fieldName); - recordedValues.recordFieldValue(fieldName, field.getValue(evaluation)); - } - - for (Iterator it = delayedEvaluations.variables.iterator(); it.hasNext();) - { - String variableName = it.next(); - JRFillVariable variable = getVariable(variableName); - recordedValues.recordVariableValue(variableName, variable.getValue(evaluation)); - } - } - - recordedValues.doneEvaluation(evaluationTime); - - if (recordedValues.finishedEvaluations()) - { - overwriteWithRecordedValues(recordedValues, evaluation); - performDelayedEvaluation(printElement, evaluation); - restoreValues(recordedValues, evaluation); - printElement.deleteRecordedValues(); - } - } - - - private void overwriteWithRecordedValues(JRRecordedValues recordedValues, byte evaluation) - { - Map fieldValues = recordedValues.getRecordedFieldValues(); - if (fieldValues != null) - { - for (Iterator> it = fieldValues.entrySet().iterator(); it.hasNext();) - { - Map.Entry entry = it.next(); - String fieldName = entry.getKey(); - Object fieldValue = entry.getValue(); - JRFillField field = getField(fieldName); - field.overwriteValue(fieldValue, evaluation); - } - } - - Map variableValues = recordedValues.getRecordedVariableValues(); - if (variableValues != null) - { - for (Iterator> it = variableValues.entrySet().iterator(); it.hasNext();) - { - Map.Entry entry = it.next(); - String variableName = entry.getKey(); - Object variableValue = entry.getValue(); - JRFillVariable variable = getVariable(variableName); - variable.overwriteValue(variableValue, evaluation); - } - } - } - - private void restoreValues(JRRecordedValues recordedValues, byte evaluation) - { - Map fieldValues = recordedValues.getRecordedFieldValues(); - if (fieldValues != null) - { - for (Iterator it = fieldValues.keySet().iterator(); it.hasNext();) - { - String fieldName = it.next(); - JRFillField field = getField(fieldName); - field.restoreValue(evaluation); - } - } - - Map variableValues = recordedValues.getRecordedVariableValues(); - if (variableValues != null) - { - for (Iterator it = variableValues.keySet().iterator(); it.hasNext();) - { - String variableName = it.next(); - JRFillVariable variable = getVariable(variableName); - variable.restoreValue(evaluation); - } - } - } - - /** - * - */ - public void setConditionalStylesContainer(JRFillElementContainer conditionalStylesContainer) - { - this.conditionalStylesContainer = conditionalStylesContainer; - if (fillContainerContext == null) - { - fillContainerContext = conditionalStylesContainer; - } - } - - /** - * - */ - public JRFillElementContainer getConditionalStylesContainer() - { - return conditionalStylesContainer; - } - - @Override - public JRStyle getStyle() - { - // the current style overrides other style objects - if (currentStyle != null) - { - return currentStyle; - } - - JRStyle crtStyle = initStyle; - - boolean isUsingDefaultStyle = false; - - if (crtStyle == null) - { - crtStyle = filler.getDefaultStyle(); - isUsingDefaultStyle = true; - } - - JRStyle evalStyle = crtStyle; - - if (conditionalStylesContainer != null) - { - evalStyle = conditionalStylesContainer.getEvaluatedConditionalStyle(crtStyle); - } - if (isUsingDefaultStyle && evalStyle == crtStyle) - { - evalStyle = null; - } - - return evalStyle; - } - - /** - * - */ - protected JRTemplateElement getTemplate(JRStyle style) - { - return templates.get(style); - } - - /** - * - */ - protected void registerTemplate(JRStyle style, JRTemplateElement template) - { - templates.put(style, template); - } - - - /** - * Indicates whether an element is shrinkable. - *

- * This flag is only effective when {@link #isRemoveLineWhenBlank() isRemoveLineWhenBlank} is also set. - * - * @param shrinkable whether the element is shrinkable - */ - protected final void setShrinkable(boolean shrinkable) - { - this.shrinkable = shrinkable; - } - - - /** - * Called when the stretch height of an element is final so that - * the element can perform any adjustments. - * @deprecated To be removed. - */ - protected void stretchHeightFinal() - { - // nothing - } - - - protected boolean isEvaluateNow() - { - boolean evaluateNow; - switch (getEvaluationTimeValue()) - { - case NOW: - evaluateNow = true; - break; - - case AUTO: - evaluateNow = isAutoEvaluateNow(); - break; - - default: - evaluateNow = false; - break; - } - return evaluateNow; - } - - - protected boolean isAutoEvaluateNow() - { - return delayedEvaluationsMap == null || delayedEvaluationsMap.isEmpty() - || (delayedEvaluationsMap.size() == 1 - && delayedEvaluationsMap.containsKey(JREvaluationTime.EVALUATION_TIME_NOW)); - } - - - protected boolean isEvaluateAuto() - { - return getEvaluationTimeValue() == EvaluationTimeEnum.AUTO && !isAutoEvaluateNow(); - } - - @Override - public String getStyleNameReference() - { - return null; - } - - @Override - public void setStyle(JRStyle style) - { - initStyle = style; - if (conditionalStylesContainer != null) - { - conditionalStylesContainer.collectConditionalStyle(style); - } - } - - @Override - public void setStyleNameReference(String name) - { - throw new UnsupportedOperationException("Style name references not allowed at fill time"); - } - - @Override - public Object clone() - { - throw new UnsupportedOperationException(); - } - - @Override - public Object clone(JRElementGroup parentGroup) - { - throw new UnsupportedOperationException(); - } - - @Override - public JRElement clone(JRElementGroup parentGroup, int y) - { - throw new UnsupportedOperationException(); - } - - @Override - public boolean hasProperties() - { - return mergedProperties != null && mergedProperties.hasProperties(); - } - - @Override - public JRPropertiesMap getPropertiesMap() - { - return mergedProperties; - } - - @Override - public JRPropertiesHolder getParentProperties() - { - //element properties default to report properties - return filler.getMainDataset(); - } - - - @Override - public JRPropertyExpression[] getPropertyExpressions() - { - return propertyExpressions.toArray(new JRPropertyExpression[propertyExpressions.size()]); - } - - protected void transferProperties(JRTemplateElement template) - { - if (staticTransferProperties != null) - { - template.getPropertiesMap().copyOwnProperties(staticTransferProperties); - } - } - - protected void transferProperties(JRPrintElement element) - { - filler.getPropertiesUtil().transferProperties(dynamicProperties, element, - dynamicTransferProperties); - } - - protected JRPropertiesMap getEvaluatedProperties() - { - return mergedProperties; - } - - protected void evaluateProperties(byte evaluation) throws JRException - { - if (propertyExpressions.isEmpty()) - { - dynamicProperties = null; - mergedProperties = staticProperties; - } - else - { - dynamicProperties = new JRPropertiesMap(); - - for (JRPropertyExpression prop : propertyExpressions) - { - String value = (String) evaluateExpression(prop.getValueExpression(), evaluation); - //if (value != null) //for some properties such as data properties in metadata exporters, the null value is significant - { - dynamicProperties.setProperty(prop.getName(), value); - } - } - - mergedProperties = dynamicProperties.cloneProperties(); - mergedProperties.setBaseProperties(staticProperties); - - lookForPartProperty(dynamicProperties); - } - } - - protected void setOriginProvider(JROriginProvider originProvider) - { - this.originProvider = originProvider; - } - - protected JROrigin getElementOrigin() - { - JROrigin elementOrigin = null; - if (originProvider != null) - { - elementOrigin = originProvider.getOrigin(); - } - return elementOrigin; - } - - protected boolean isDelayedStyleEvaluation() - { - return filler.getPropertiesUtil().getBooleanProperty(this, - JRStyle.PROPERTY_EVALUATION_TIME_ENABLED, false); - } - - public JRBaseFiller getFiller() - { - return filler; - } - - - @Override - public boolean hasDynamicProperties() - { - return !propertyExpressions.isEmpty(); - } - - @Override - public boolean hasDynamicProperty(String name) - { - // not called very often for now so doing linear search in array - for (JRPropertyExpression prop : propertyExpressions) - { - if (prop.getName().equals(name)) - { - return true; - } - } - return false; - } - - @Override - public JRPropertiesMap getDynamicProperties() - { - return dynamicProperties; - } - - protected JRStyle getInitStyle() - { - return initStyle; - } - - protected JRElement getParent() - { - return parent; - } - - protected void addDynamicProperty(String name, JRExpression expression) - { - JRDesignPropertyExpression prop = new JRDesignPropertyExpression(); - prop.setName(name); - prop.setValueExpression(expression); - - propertyExpressions.add(prop); - // recomputing - dynamicTransferProperties = findDynamicTransferProperties(); - } - - protected void setExpressionEvaluator(JRFillExpressionEvaluator expressionEvaluator) - { - this.expressionEvaluator = expressionEvaluator; - } - - - /** - * - */ - public static Integer getBookmarkLevel(Object value) throws JRException - { - Integer level = null; - - if (value != null) - { - if (value instanceof Number) - { - level = ((Number)value).intValue(); - } - else - { - try - { - level = Integer.parseInt(value.toString()); - } - catch (NumberFormatException e) - { - //do nothing - } - } - - if (level == null || level < 0) - { - throw - new JRException( - EXCEPTION_MESSAGE_KEY_INVALID_BOOKMARK_LEVEL, - new Object[] {value} - ); - } - } - - return level; - } + /** + * + */ + public static final String EXCEPTION_MESSAGE_KEY_INVALID_BOOKMARK_LEVEL = "fill.anchor.bookmark.level.invalid"; + + + /** + * + */ + protected JRElement parent; + protected List propertyExpressions; + protected List dynamicTransferProperties; + protected JRStyle providerStyle; + protected Map templates = new HashMap<>(); + protected List styleProviders; + + /** + * + */ + protected JRBaseFiller filler; + protected JRFillExpressionEvaluator expressionEvaluator; + + protected JRDefaultStyleProvider defaultStyleProvider; + + /** + * + */ + protected JRGroup printWhenGroupChanges; + protected JRFillElementGroup elementGroup; + + /** + * + */ + protected JRFillBand band; + + protected JROriginProvider originProvider; + + protected PrintElementOriginator printElementOriginator; + + /** + * + */ + private boolean isPrintWhenExpressionNull = true; + private boolean isPrintWhenTrue = true; + private boolean isToPrint = true; + private boolean isReprinted; + private boolean isAlreadyPrinted; + private Collection dependantElements = new ArrayList<>(); + private int relativeY; + private int collapsedHeightAbove; + private int collapsedHeightBelow; + /** + * Keeps total stretch height, including forced stretch after honoring the stretchType property of the element. + */ + private int stretchHeight; + /** + * Keeps the natural stretch height calculated during element prepare. + */ + private int prepareHeight; + + private int x; + private int y; + private int width; + private int height; + + private boolean isValueRepeating; + + protected byte currentEvaluation; + + // used by elements that support evaluationTime=Auto + protected Map delayedEvaluationsMap; + + protected JRFillElementContainer conditionalStylesContainer; + protected FillContainerContext fillContainerContext; + + protected JRStyle initStyle; + protected JRStyle exprStyle; + protected JRStyle currentStyle; + + /** + * Flag indicating whether the element is shrinkable. + * @see #setShrinkable(boolean) + */ + private boolean shrinkable; + + protected JRPropertiesMap staticProperties; + protected JRPropertiesMap staticTransferProperties; + protected JRPropertiesMap dynamicProperties; + protected JRPropertiesMap mergedProperties; + + protected boolean hasDynamicPopulateTemplateStyle; + protected Boolean defaultPopulateTemplateStyle; + + /** + * + * + private JRElement topElementInGroup; + private JRElement bottomElementInGroup; + + + /** + * + */ + protected JRFillElement( + JRBaseFiller filler, + JRElement element, + JRFillObjectFactory factory + ) + { + factory.put(element, this); + + this.parent = element; + this.filler = filler; + this.expressionEvaluator = factory.getExpressionEvaluator(); + this.defaultStyleProvider = factory.getDefaultStyleProvider(); + + printElementOriginator = filler.assignElementId(this); + + /* */ + printWhenGroupChanges = factory.getGroup(element.getPrintWhenGroupChanges()); + elementGroup = (JRFillElementGroup)factory.getVisitResult(element.getElementGroup()); + + x = element.getX(); + y = element.getY(); + width = element.getWidth(); + height = element.getHeight(); + + staticProperties = element.hasProperties() ? element.getPropertiesMap().cloneProperties() : null; + staticTransferProperties = findStaticTransferProperties(); + mergedProperties = staticProperties; + + JRPropertyExpression[] elementPropertyExpressions = element.getPropertyExpressions(); + propertyExpressions = elementPropertyExpressions == null ? new ArrayList<>(0) + : new ArrayList<>(Arrays.asList(elementPropertyExpressions)); + + dynamicTransferProperties = findDynamicTransferProperties(); + + factory.registerDelayedStyleSetter(this, parent); + + initStyleProviders(); + lookForPartProperty(staticProperties); + + hasDynamicPopulateTemplateStyle = hasDynamicProperty(PROPERTY_ELEMENT_TEMPLATE_POPULATE_STYLE); + } + + + protected JRFillElement(JRFillElement element, JRFillCloneFactory factory) + { + factory.put(element, this); + + this.parent = element.parent; + this.filler = element.filler; + this.expressionEvaluator = element.expressionEvaluator; + this.defaultStyleProvider = element.defaultStyleProvider; + this.originProvider = element.originProvider; + + printElementOriginator = element.printElementOriginator; + + /* */ + printWhenGroupChanges = element.printWhenGroupChanges; + elementGroup = (JRFillElementGroup) factory.getClone((JRFillElementGroup) element.getElementGroup()); + + x = element.getX(); + y = element.getY(); + width = element.getWidth(); + height = element.getHeight(); + + templates = element.templates; + + initStyle = element.initStyle; + + shrinkable = element.shrinkable; + + staticProperties = element.staticProperties == null ? null : element.staticProperties.cloneProperties(); + staticTransferProperties = element.staticTransferProperties; + mergedProperties = staticProperties; + this.propertyExpressions = new ArrayList<>(element.propertyExpressions); + this.dynamicTransferProperties = element.dynamicTransferProperties; + + // we need a style provider context for this element instance + initStyleProviders(); + + this.hasDynamicPopulateTemplateStyle = element.hasDynamicPopulateTemplateStyle; + this.defaultPopulateTemplateStyle = element.defaultPopulateTemplateStyle; + } + + private JRPropertiesMap findStaticTransferProperties() + { + if (staticProperties == null) + { + return null; + } + + String[] propertyNames = staticProperties.getPropertyNames(); + List prefixes = filler.getPrintTransferPropertyPrefixes(); + JRPropertiesMap transferProperties = new JRPropertiesMap(); + for (int i = 0; i < propertyNames.length; i++) + { + for (String prefix : prefixes) + { + String prop = propertyNames[i]; + if (prop.startsWith(prefix)) + { + transferProperties.setProperty(prop, staticProperties.getProperty(prop)); + break; + } + } + } + return transferProperties.isEmpty() ? null : transferProperties; + } + + private List findDynamicTransferProperties() + { + if (propertyExpressions.isEmpty()) + { + return null; + } + + List prefixes = filler.getPrintTransferPropertyPrefixes(); + List transferProperties = new ArrayList<>(propertyExpressions.size()); + for (JRPropertyExpression propertyExpression : propertyExpressions) + { + String propertyName = propertyExpression.getName(); + for (String prefix : prefixes) + { + if (propertyName.startsWith(prefix)) + { + transferProperties.add(propertyName); + break; + } + } + } + return transferProperties; + } + + private void lookForPartProperty(JRPropertiesMap properties) + { + if (properties != null && properties.getProperty(PrintPart.ELEMENT_PROPERTY_PART_NAME) != null) + { + filler.getFillContext().setDetectParts(true); + } + } + + @Override + public JRDefaultStyleProvider getDefaultStyleProvider() + { + return defaultStyleProvider; + } + + /** + * + */ + protected StyleResolver getStyleResolver() + { + return getDefaultStyleProvider().getStyleResolver(); + } + + @Override + public UUID getUUID() + { + return parent.getUUID(); + } + + @Override + public String getKey() + { + return parent.getKey(); + } + + @Override + public PositionTypeEnum getPositionTypeValue() + { + return parent.getPositionTypeValue();//FIXME optimize this by consolidating style properties + } + + @Override + public void setPositionType(PositionTypeEnum positionType) + { + throw new UnsupportedOperationException(); + } + + @Override + public StretchTypeEnum getStretchTypeValue() + { + return parent.getStretchTypeValue(); + } + + @Override + public void setStretchType(StretchTypeEnum stretchType) + { + throw new UnsupportedOperationException(); + } + + @Override + public boolean isPrintRepeatedValues() + { + return parent.isPrintRepeatedValues(); + } + + @Override + public void setPrintRepeatedValues(boolean isPrintRepeatedValues) + { + } + + @Override + public ModeEnum getModeValue() + { + return getStyleResolver().getMode(this, ModeEnum.OPAQUE); + } + + @Override + public ModeEnum getOwnModeValue() + { + return providerStyle == null || providerStyle.getOwnModeValue() == null ? parent.getOwnModeValue() : providerStyle.getOwnModeValue(); + } + + @Override + public void setMode(ModeEnum modeValue) + { + } + + @Override + public int getX() + { + return x; + } + + @Override + public void setX(int x) + { + this.x = x; + } + + /** + * + */ + public void setY(int y) + { + this.y = y; + } + + @Override + public int getY() + { + return y; + } + + @Override + public int getWidth() + { + return width; + } + + @Override + public void setWidth(int width) + { + this.width = width; + } + + /** + * + */ + public void setHeight(int height) + { + this.height = height; + } + + @Override + public int getHeight() + { + return height; + } + + @Override + public boolean isRemoveLineWhenBlank() + { + return parent.isRemoveLineWhenBlank(); + } + + @Override + public void setRemoveLineWhenBlank(boolean isRemoveLine) + { + } + + @Override + public boolean isPrintInFirstWholeBand() + { + return parent.isPrintInFirstWholeBand(); + } + + @Override + public void setPrintInFirstWholeBand(boolean isPrint) + { + } + + @Override + public boolean isPrintWhenDetailOverflows() + { + return parent.isPrintWhenDetailOverflows(); + } + + @Override + public void setPrintWhenDetailOverflows(boolean isPrint) + { + } + + @Override + public Color getForecolor() + { + return getStyleResolver().getForecolor(this); + } + + @Override + public Color getOwnForecolor() + { + return providerStyle == null || providerStyle.getOwnForecolor() == null ? parent.getOwnForecolor() : providerStyle.getOwnForecolor(); + } + + @Override + public void setForecolor(Color forecolor) + { + } + + @Override + public Color getBackcolor() + { + return getStyleResolver().getBackcolor(this); + } + + @Override + public Color getOwnBackcolor() + { + return providerStyle == null || providerStyle.getOwnBackcolor() == null ? parent.getOwnBackcolor() : providerStyle.getOwnBackcolor(); + } + + @Override + public void setBackcolor(Color backcolor) + { + } + + @Override + public JRExpression getStyleExpression() + { + return parent.getStyleExpression(); + } + + @Override + public JRExpression getPrintWhenExpression() + { + return parent.getPrintWhenExpression(); + } + + @Override + public JRGroup getPrintWhenGroupChanges() + { + return printWhenGroupChanges; + } + + @Override + public JRElementGroup getElementGroup() + { + return elementGroup; + } + + /** + * + */ + protected boolean isPrintWhenExpressionNull() + { + return isPrintWhenExpressionNull; + } + + /** + * + */ + protected void setPrintWhenExpressionNull(boolean isPrintWhenExpressionNull) + { + this.isPrintWhenExpressionNull = isPrintWhenExpressionNull; + } + + /** + * + */ + protected boolean isPrintWhenTrue() + { + return isPrintWhenTrue; + } + + /** + * + */ + protected void setPrintWhenTrue(boolean isPrintWhenTrue) + { + this.isPrintWhenTrue = isPrintWhenTrue; + } + + /** + * + */ + public boolean isToPrint() + { + return isToPrint; + } + + /** + * + */ + protected void setToPrint(boolean isToPrint) + { + this.isToPrint = isToPrint; + } + + /** + * + */ + protected boolean isReprinted() + { + return isReprinted; + } + + /** + * + */ + protected void setReprinted(boolean isReprinted) + { + this.isReprinted = isReprinted; + } + + /** + * + */ + public boolean isAlreadyPrinted() + { + return isAlreadyPrinted; + } + + /** + * + */ + public void setAlreadyPrinted(boolean isAlreadyPrinted) + { + this.isAlreadyPrinted = isAlreadyPrinted; + } + + /** + * + */ + protected JRElement[] getGroupElements() + { + JRElement[] groupElements = null; + + if (elementGroup != null) + { + groupElements = elementGroup.getElements(); + } + + return groupElements; + } + + /** + * + */ + protected Collection getDependantElements() + { + return dependantElements; + } + + /** + * + */ + protected void addDependantElement(JRFillElement element) + { + dependantElements.add(element); + } + + /** + * + */ + protected int getRelativeY() + { + return relativeY; + } + + /** + * + */ + protected void setRelativeY(int relativeY) + { + this.relativeY = relativeY; + } + + /** + * + */ + protected int getCollapsedHeightAbove() + { + return collapsedHeightAbove; + } + + /** + * + */ + protected void setCollapsedHeightAbove(int collapsedHeightAbove) + { + this.collapsedHeightAbove = collapsedHeightAbove; + } + + /** + * + */ + protected int getCollapsedHeightBelow() + { + return collapsedHeightBelow; + } + + /** + * + */ + protected void setCollapsedHeightBelow(int collapsedHeightBelow) + { + this.collapsedHeightBelow = collapsedHeightBelow; + } + + /** + * + */ + public int getStretchHeight() + { + return stretchHeight; + } + + /** + * + */ + protected void setStretchHeight(int stretchHeight) + { + if (stretchHeight > getHeight() || (shrinkable && isRemoveLineWhenBlank())) + { + this.stretchHeight = stretchHeight; + } + else + { + this.stretchHeight = getHeight(); + } + } + + /** + * + */ + public int getPrepareHeight() + { + return prepareHeight; + } + + /** + * Element height is calculated in two phases. + * First, the element stretches on its own during prepare, to accommodate all its content. + * This is the natural stretch and we keep track of the calculated height in prepareHeight. + * Secondly, the element stretches further in accordance with its stretchType property. + * This forced stretch occurs at a later time and the amount of stretch is kept in stretchHeight. + */ + protected void setPrepareHeight(int prepareHeight) + { + this.prepareHeight = prepareHeight; + + setStretchHeight(prepareHeight); + } + + /** + * + */ + protected JRFillBand getBand() + { + return band; + } + + /** + * + */ + protected void setBand(JRFillBand band) + { + this.band = band; + + if (this.originProvider == null) + { + setOriginProvider(band); + } + } + + + /** + * + */ + protected void initStyleProviders() + { + List styleProviderFactories = filler.getJasperReportsContext().getExtensions(StyleProviderFactory.class); + if (styleProviderFactories != null && styleProviderFactories.size() > 0) + { + FillStyleProviderContext styleProviderContext = new FillStyleProviderContext(this); + for (StyleProviderFactory styleProviderFactory : styleProviderFactories) + { + StyleProvider styleProvider = styleProviderFactory.getStyleProvider(styleProviderContext, filler.getJasperReportsContext()); + if (styleProvider != null) + { + if (styleProviders == null) + { + styleProviders = new ArrayList<>(); + } + styleProviders.add(styleProvider); + } + } + } + } + + + /** + * + */ + protected void reset() + { + relativeY = y; + collapsedHeightAbove = 0; + collapsedHeightBelow = 0; + stretchHeight = height; + prepareHeight = height; + + if (elementGroup != null) + { + elementGroup.reset(); + } + } + + protected void setCurrentEvaluation(byte evaluation) + { + currentEvaluation = evaluation; + } + + /** + * + */ + protected abstract void evaluate( + byte evaluation + ) throws JRException; + + + /** + * + */ + protected void evaluateStyle( + byte evaluation + ) throws JRException + { + exprStyle = null; + + JRExpression styleExpression = getStyleExpression(); + if (styleExpression != null) + { + String styleName = (String)evaluateExpression(styleExpression, evaluation); + if (styleName != null) + { + exprStyle = getFiller().factory.stylesMap.getStyle(styleName); + if (exprStyle == null) + { + throw + new JRRuntimeException( + JRFillObjectFactory.EXCEPTION_MESSAGE_KEY_STYLE_NOT_FOUND, + new Object[]{styleName} + ); + } + else + { + conditionalStylesContainer.collectConditionalStyle(exprStyle); + } + } + } + + if (exprStyle != null && isEvaluateNow()) + { + conditionalStylesContainer.evaluateConditionalStyle(exprStyle, evaluation); + initStyle = exprStyle; + } + + providerStyle = null; + + if (styleProviders != null && styleProviders.size() > 0) + { + for (StyleProvider styleProvider : styleProviders) + { + JRStyle style = styleProvider.getStyle(evaluation); + if (style != null) + { + if (providerStyle == null) + { + providerStyle = new JRBaseStyle(); + } + StyleUtil.appendStyle(providerStyle, style); + } + } + } + } + + protected TimeZone getTimeZone() + { + return filler.getTimeZone(); + } + + /** + * + */ + protected void evaluatePrintWhenExpression( + byte evaluation + ) throws JRException + { + boolean isExprNull = true; + boolean isExprTrue = false; + + JRExpression expression = getPrintWhenExpression(); + if (expression != null) + { + isExprNull = false; + Boolean printWhenExpressionValue = (Boolean) evaluateExpression(expression, evaluation); + if (printWhenExpressionValue == null) + { + isExprTrue = false; + } + else + { + isExprTrue = printWhenExpressionValue; + } + } + + setPrintWhenExpressionNull(isExprNull); + setPrintWhenTrue(isExprTrue); + } + + + /** + * + */ + protected abstract void rewind() throws JRException; + + + /** + * + */ + protected abstract JRPrintElement fill() throws JRException; + + protected JRTemplateElement getElementTemplate() + { + JRTemplateElement template = null; + JRStyle style = null; + + if (providerStyle == null) + { + // no style provider has been used so we can use cache template per style below + style = getStyle(); + template = getTemplate(style); + } + + if (template == null) + { + template = createElementTemplate(); + transferProperties(template); + + if (toPopulateTemplateStyle()) + { + template.populateStyle(); + } + + // deduplicate to previously created identical objects + template = filler.fillContext.deduplicate(template); + + if (providerStyle == null) + { + registerTemplate(style, template); + } + } + return template; + } + + protected abstract JRTemplateElement createElementTemplate(); + + protected boolean toPopulateTemplateStyle() + { + if (defaultPopulateTemplateStyle == null) + { + defaultPopulateTemplateStyle = filler.getPropertiesUtil().getBooleanProperty( + PROPERTY_ELEMENT_TEMPLATE_POPULATE_STYLE, false, + parent, filler.getMainDataset()); + } + + boolean populate = defaultPopulateTemplateStyle; + if (hasDynamicPopulateTemplateStyle) + { + String populateProp = getDynamicProperties().getProperty( + PROPERTY_ELEMENT_TEMPLATE_POPULATE_STYLE); + if (populateProp != null) + { + populate = JRPropertiesUtil.asBoolean(populateProp); + } + } + return populate; + } + + /** + * + */ + protected boolean prepare( + int availableHeight, + boolean isOverflow + ) throws JRException + { + if ( + isPrintWhenExpressionNull() || + ( !isPrintWhenExpressionNull() && + isPrintWhenTrue() ) + ) + { + setToPrint(true); + } + else + { + setToPrint(false); + } + + setReprinted(false); + + return false; + } + + + /** + * @deprecated To be removed. + */ + protected void _stretchElement(int bandStretch) + { + switch (getStretchTypeValue()) + { + case RELATIVE_TO_BAND_HEIGHT : + case CONTAINER_HEIGHT : + case CONTAINER_BOTTOM : + { + _stretchElementToHeight(getHeight() + bandStretch); + break; + } + case RELATIVE_TO_TALLEST_OBJECT : + case ELEMENT_GROUP_HEIGHT : + case ELEMENT_GROUP_BOTTOM : + { + if (elementGroup != null) + { + //setStretchHeight(getHeight() + getStretchHeightDiff()); + _stretchElementToHeight(getHeight() + elementGroup.getStretchHeightDiff()); + } + + break; + } + case NO_STRETCH : + default : + { + break; + } + } + } + + /** + * @deprecated To be removed. + */ + protected void _stretchElementToHeight(int stretchHeight) + { + if (stretchHeight > getStretchHeight()) + { + setStretchHeight(stretchHeight); + } + } + + + /** + * + */ + @SuppressWarnings("deprecation") + protected boolean stretchElement(int containerStretch) + { + boolean applied = false; + switch (getStretchTypeValue()) + { + case RELATIVE_TO_BAND_HEIGHT : + case CONTAINER_HEIGHT : + case CONTAINER_BOTTOM : + { + applied = stretchElementToContainer(containerStretch); + break; + } + case RELATIVE_TO_TALLEST_OBJECT : + case ELEMENT_GROUP_HEIGHT : + case ELEMENT_GROUP_BOTTOM : + { + applied = stretchElementToElementGroup(); + break; + } + case NO_STRETCH : + default : + { + break; + } + } + return applied; + } + + + /** + * + */ + @SuppressWarnings("deprecation") + protected boolean stretchElementToContainer(int containerStretch)//TODO subtract firstY? + { + boolean applied = false; + switch (getStretchTypeValue()) + { + case RELATIVE_TO_BAND_HEIGHT : + case CONTAINER_HEIGHT : + { + applied = stretchElementToHeight(getHeight() + containerStretch); + break; + } + case CONTAINER_BOTTOM : + { + applied = stretchElementToHeight(getY() - getRelativeY() + getHeight() + containerStretch); + break; + } + default : + } + return applied; + } + + + /** + * + */ + @SuppressWarnings("deprecation") + protected boolean stretchElementToElementGroup() + { + boolean applied = false; + if (elementGroup != null) + { + switch (getStretchTypeValue()) + { + case RELATIVE_TO_TALLEST_OBJECT : + case ELEMENT_GROUP_HEIGHT : + { + applied = stretchElementToHeight(getHeight() + elementGroup.getStretchHeightDiff()); + break; + } + case ELEMENT_GROUP_BOTTOM : + { + applied = stretchElementToHeight(getY() - getRelativeY() + getHeight() + elementGroup.getStretchHeightDiff()); + break; + } + default : + } + } + return applied; + } + + + /** + * This method returns a boolean signaling if any stretch change occurred. + * It does not say which amount of stretch was applied, but that is OK, because the only place where this is checked + * is during frame cascading stretch, where the stretchHeight field of the frame (set here) is used directly. + */ + protected boolean stretchElementToHeight(int stretchHeight) + { + // cannot force the element to shrink below its natural stretch calculated during prepare; + // such situation could occur when the container breaks and overflows + boolean applied = false; + if (stretchHeight > getPrepareHeight()) + { + // any new stretchHeight that is greater than element's natural growth is fine + setStretchHeight(stretchHeight); + applied = true; + } + return applied; + } + + + /** + * @deprecated To be removed. + */ + protected void _moveDependantElements() + { + Collection elements = getDependantElements(); + if (elements != null && elements.size() > 0) + { + for (JRFillElement element : elements) + { + int newRelativeY = + getRelativeY() + getStretchHeight() //pusher element current bottom edge + + (element.getY() - (getY() + getHeight())); //design time distance between elements; difference between float element top edge and pusher element bottom edge + + if (newRelativeY > element.getRelativeY()) + { + element.setRelativeY(newRelativeY); + } + } + } + } + + + /** + * + */ + protected void moveDependantElements() { + Collection elements = getDependantElements(); + if (elements != null && !elements.isEmpty()) { + int offset = getRelativeY() + getStretchHeight() - getY() - getHeight() + getCollapsedHeightAbove(); + for (JRFillElement element : elements) { + int newRelativeY = offset + element.getY() - element.getCollapsedHeightAbove(); + if (newRelativeY > element.getRelativeY()) { + element.setRelativeY(newRelativeY); + } + } + } + } + + + /** + * Resolves an element. + * + * @param element the element + * @param evaluation the evaluation type + */ + protected abstract void resolveElement(JRPrintElement element, byte evaluation) throws JRException; + + protected void performDelayedEvaluation(JRPrintElement element, byte evaluation) + throws JRException + { + evaluateProperties(evaluation); + evaluateStyle(evaluation); + + boolean updateTemplate = false; + + JRStyle printStyle = element.getStyle(); + if (isDelayedStyleEvaluation() || exprStyle != null) + { + if (exprStyle != null) + { + initStyle = exprStyle; + } + + JRStyle elementStyle = initStyle; + if (elementStyle == null) + { + elementStyle = filler.getDefaultStyle(); + } + + if (elementStyle != null) + { + JRStyle evaluatedStyle = conditionalStylesContainer.evaluateConditionalStyle( + elementStyle, evaluation); + // if the evaluated style differs from the existing style + if (evaluatedStyle != printStyle) + { + // set the evaluated style as element style + printStyle = evaluatedStyle; + + updateTemplate = true; + } + } + } + + // set the current element style + this.currentStyle = printStyle; + + resolveElement(element, evaluation); + + if (updateTemplate || providerStyle != null + || delayedEvaluationUpdatesTemplate()) + { + // get/create an element template that corresponds to the + // current style + JRTemplateElement newTemplate = getElementTemplate(); + ((JRTemplatePrintElement) element).updateElementTemplate( + newTemplate); + } + + // reset the current style + this.currentStyle = null; + //this.providerStyle = null; + } + + protected boolean delayedEvaluationUpdatesTemplate() + { + return false; + } + + + /** + * Evaluates an expression. + * + * @param expression the expression + * @param evaluation the evaluation type + * @return the evaluation result + * @throws JRException + */ + public final Object evaluateExpression(JRExpression expression, byte evaluation) throws JRException + { + return expressionEvaluator.evaluate(expression, evaluation); + } + + + /** + * Decides whether the value for this element is repeating. + *

+ * Dynamic elements should call {@link #setValueRepeating(boolean) setValueRepeating(boolean)} on + * {@link #evaluate(byte) evaluate(byte)}. Static elements don't have to do anything, this method + * will return true by default. + * + * @return whether the value for this element is repeating + * @see #setValueRepeating(boolean) + */ + protected boolean isValueRepeating() + { + return isValueRepeating; + } + + + /** + * Sets the repeating flag for this element. + *

+ * This method should be called by dynamic elements on {@link #evaluate(byte) evaluate(byte)}. + * + * @param isValueRepeating whether the value of the element is repeating + * @see #isValueRepeating() + */ + protected void setValueRepeating(boolean isValueRepeating) + { + this.isValueRepeating = isValueRepeating; + } + + + protected JRFillVariable getVariable(String variableName) + { + return filler.getVariable(variableName); + } + + + protected JRFillField getField(String fieldName) + { + return filler.getField(fieldName); + } + + // default for elements not supporting evaluationTime + protected EvaluationTimeEnum getEvaluationTimeValue() + { + return EvaluationTimeEnum.NOW; + } + + /** + * Resolves an element. + * + * @param element the element + * @param evaluation the evaluation type + * @param evaluationTime the current evaluation time + */ + protected void resolveElement(JRPrintElement element, byte evaluation, JREvaluationTime evaluationTime) throws JRException + { + EvaluationTimeEnum evaluationTimeType = getEvaluationTimeValue(); + switch (evaluationTimeType) + { + case NOW: + break; + case AUTO: + delayedEvaluate((JRRecordedValuesPrintElement) element, evaluationTime, evaluation); + break; + default: + performDelayedEvaluation(element, evaluation); + break; + } + } + + private static class DelayedEvaluations implements Serializable + { + private static final long serialVersionUID = JRConstants.SERIAL_VERSION_UID; + + final Set fields; + final Set variables; + + DelayedEvaluations() + { + fields = new HashSet<>(); + variables = new HashSet<>(); + } + } + + protected void initDelayedEvaluations() + { + if (getEvaluationTimeValue() == EvaluationTimeEnum.AUTO && delayedEvaluationsMap == null) + { + delayedEvaluationsMap = new HashMap<>(); + collectDelayedEvaluations(); + } + } + + protected void collectDelayedEvaluations() + { + if (isDelayedStyleEvaluation()) + { + collectStyleDelayedEvaluations(); + collectStyleProviderDelayedEvaluations(); + } + } + + protected void collectStyleDelayedEvaluations() + { + JRStyle elementStyle = initStyle; + if (elementStyle == null) + { + elementStyle = filler.getDefaultStyle(); + } + + if (elementStyle != null) + { + JRStyle style = elementStyle; + while (style != null) + { + collectDelayedEvaluations(style); + + // proceed to the parent style + style = style.getStyle(); + } + } + } + + protected void collectDelayedEvaluations(JRStyle style) + { + JRConditionalStyle[] conditionalStyles = style.getConditionalStyles(); + // collect delayed evaluations from conditional style expressions + if (conditionalStyles != null && conditionalStyles.length > 0) + { + for (int i = 0; i < conditionalStyles.length; i++) + { + collectDelayedEvaluations( + conditionalStyles[i].getConditionExpression()); + } + } + } + + + protected void collectDelayedEvaluations(JRExpression expression) + { + if (expression != null) + { + JRExpressionChunk[] chunks = expression.getChunks(); + if (chunks != null) + { + for (int i = 0; i < chunks.length; i++) + { + JRExpressionChunk chunk = chunks[i]; + switch (chunk.getType()) + { + case JRExpressionChunk.TYPE_FIELD: + { + DelayedEvaluations delayedEvaluations = getDelayedEvaluations(JREvaluationTime.EVALUATION_TIME_NOW); + delayedEvaluations.fields.add(chunk.getText()); + break; + } + case JRExpressionChunk.TYPE_VARIABLE: + { + JREvaluationTime time = autogetVariableEvaluationTime(chunk.getText()); + DelayedEvaluations delayedEvaluations = getDelayedEvaluations(time); + delayedEvaluations.variables.add(chunk.getText()); + break; + } + default: + } + } + } + } + } + + + protected void collectStyleProviderDelayedEvaluations() + { + if (styleProviders != null && styleProviders.size() > 0) + { + for (StyleProvider styleProvider : styleProviders) + { + String[] fields = styleProvider.getFields(); + if (fields != null && fields.length > 0) + { + DelayedEvaluations delayedEvaluations = getDelayedEvaluations(JREvaluationTime.EVALUATION_TIME_NOW); + for (String field : fields) + { + delayedEvaluations.fields.add(field); + } + } + String[] variables = styleProvider.getVariables(); + if (variables != null && variables.length > 0) + { + for (String variable : variables) + { + JREvaluationTime time = autogetVariableEvaluationTime(variable); + DelayedEvaluations delayedEvaluations = getDelayedEvaluations(time); + delayedEvaluations.variables.add(variable); + } + } + } + } + } + + + private DelayedEvaluations getDelayedEvaluations(JREvaluationTime time) + { + DelayedEvaluations delayedEvaluations = delayedEvaluationsMap.get(time); + if (delayedEvaluations == null) + { + delayedEvaluations = new DelayedEvaluations(); + delayedEvaluationsMap.put(time, delayedEvaluations); + } + return delayedEvaluations; + } + + + private JREvaluationTime autogetVariableEvaluationTime(String variableName) + { + JRFillVariable variable = getVariable(variableName); + JREvaluationTime evaluationTime; + switch (variable.getResetTypeValue()) + { + case REPORT: + evaluationTime = JREvaluationTime.EVALUATION_TIME_REPORT; + break; + case MASTER: + evaluationTime = JREvaluationTime.EVALUATION_TIME_MASTER; + break; + case PAGE: + evaluationTime = JREvaluationTime.EVALUATION_TIME_PAGE; + break; + case COLUMN: + evaluationTime = JREvaluationTime.EVALUATION_TIME_COLUMN; + break; + case GROUP: + evaluationTime = JREvaluationTime.getGroupEvaluationTime(variable.getResetGroup().getName()); + break; + default: + evaluationTime = JREvaluationTime.EVALUATION_TIME_NOW; + break; + } + + if (!evaluationTime.equals(JREvaluationTime.EVALUATION_TIME_NOW) && + band.isNowEvaluationTime(evaluationTime)) + { + evaluationTime = JREvaluationTime.EVALUATION_TIME_NOW; + } + + if (variable.getCalculationValue() == CalculationEnum.SYSTEM && + evaluationTime.equals(JREvaluationTime.EVALUATION_TIME_NOW) && + band.isVariableUsedInReturns(variableName)) + { + evaluationTime = JREvaluationTime.getBandEvaluationTime(band); + } + + return evaluationTime; + } + + + protected void initDelayedEvaluationPrint(JRRecordedValuesPrintElement printElement) throws JRException + { + for (Iterator it = delayedEvaluationsMap.keySet().iterator(); it.hasNext();) + { + JREvaluationTime evaluationTime = it.next(); + if (!evaluationTime.equals(JREvaluationTime.EVALUATION_TIME_NOW)) + { + filler.addBoundElement(this, printElement, evaluationTime); + } + } + + printElement.initRecordedValues(delayedEvaluationsMap.keySet()); + + if (delayedEvaluationsMap.containsKey(JREvaluationTime.EVALUATION_TIME_NOW)) + { + delayedEvaluate(printElement, JREvaluationTime.EVALUATION_TIME_NOW, currentEvaluation); + } + } + + + protected void delayedEvaluate(JRRecordedValuesPrintElement printElement, JREvaluationTime evaluationTime, byte evaluation) throws JRException + { + JRRecordedValues recordedValues = printElement.getRecordedValues(); + if (!recordedValues.lastEvaluationTime()) + { + DelayedEvaluations delayedEvaluations = delayedEvaluationsMap.get(evaluationTime); + + for (Iterator it = delayedEvaluations.fields.iterator(); it.hasNext();) + { + String fieldName = it.next(); + JRFillField field = getField(fieldName); + recordedValues.recordFieldValue(fieldName, field.getValue(evaluation)); + } + + for (Iterator it = delayedEvaluations.variables.iterator(); it.hasNext();) + { + String variableName = it.next(); + JRFillVariable variable = getVariable(variableName); + recordedValues.recordVariableValue(variableName, variable.getValue(evaluation)); + } + } + + recordedValues.doneEvaluation(evaluationTime); + + if (recordedValues.finishedEvaluations()) + { + overwriteWithRecordedValues(recordedValues, evaluation); + performDelayedEvaluation(printElement, evaluation); + restoreValues(recordedValues, evaluation); + printElement.deleteRecordedValues(); + } + } + + + private void overwriteWithRecordedValues(JRRecordedValues recordedValues, byte evaluation) + { + Map fieldValues = recordedValues.getRecordedFieldValues(); + if (fieldValues != null) + { + for (Iterator> it = fieldValues.entrySet().iterator(); it.hasNext();) + { + Map.Entry entry = it.next(); + String fieldName = entry.getKey(); + Object fieldValue = entry.getValue(); + JRFillField field = getField(fieldName); + field.overwriteValue(fieldValue, evaluation); + } + } + + Map variableValues = recordedValues.getRecordedVariableValues(); + if (variableValues != null) + { + for (Iterator> it = variableValues.entrySet().iterator(); it.hasNext();) + { + Map.Entry entry = it.next(); + String variableName = entry.getKey(); + Object variableValue = entry.getValue(); + JRFillVariable variable = getVariable(variableName); + variable.overwriteValue(variableValue, evaluation); + } + } + } + + private void restoreValues(JRRecordedValues recordedValues, byte evaluation) + { + Map fieldValues = recordedValues.getRecordedFieldValues(); + if (fieldValues != null) + { + for (Iterator it = fieldValues.keySet().iterator(); it.hasNext();) + { + String fieldName = it.next(); + JRFillField field = getField(fieldName); + field.restoreValue(evaluation); + } + } + + Map variableValues = recordedValues.getRecordedVariableValues(); + if (variableValues != null) + { + for (Iterator it = variableValues.keySet().iterator(); it.hasNext();) + { + String variableName = it.next(); + JRFillVariable variable = getVariable(variableName); + variable.restoreValue(evaluation); + } + } + } + + /** + * + */ + public void setConditionalStylesContainer(JRFillElementContainer conditionalStylesContainer) + { + this.conditionalStylesContainer = conditionalStylesContainer; + if (fillContainerContext == null) + { + fillContainerContext = conditionalStylesContainer; + } + } + + /** + * + */ + public JRFillElementContainer getConditionalStylesContainer() + { + return conditionalStylesContainer; + } + + @Override + public JRStyle getStyle() + { + // the current style overrides other style objects + if (currentStyle != null) + { + return currentStyle; + } + + JRStyle crtStyle = initStyle; + + boolean isUsingDefaultStyle = false; + + if (crtStyle == null) + { + crtStyle = filler.getDefaultStyle(); + isUsingDefaultStyle = true; + } + + JRStyle evalStyle = crtStyle; + + if (conditionalStylesContainer != null) + { + evalStyle = conditionalStylesContainer.getEvaluatedConditionalStyle(crtStyle); + } + if (isUsingDefaultStyle && evalStyle == crtStyle) + { + evalStyle = null; + } + + return evalStyle; + } + + /** + * + */ + protected JRTemplateElement getTemplate(JRStyle style) + { + return templates.get(style); + } + + /** + * + */ + protected void registerTemplate(JRStyle style, JRTemplateElement template) + { + templates.put(style, template); + } + + + /** + * Indicates whether an element is shrinkable. + *

+ * This flag is only effective when {@link #isRemoveLineWhenBlank() isRemoveLineWhenBlank} is also set. + * + * @param shrinkable whether the element is shrinkable + */ + protected final void setShrinkable(boolean shrinkable) + { + this.shrinkable = shrinkable; + } + + + /** + * Called when the stretch height of an element is final so that + * the element can perform any adjustments. + * @deprecated To be removed. + */ + protected void stretchHeightFinal() + { + // nothing + } + + + protected boolean isEvaluateNow() + { + boolean evaluateNow; + switch (getEvaluationTimeValue()) + { + case NOW: + evaluateNow = true; + break; + + case AUTO: + evaluateNow = isAutoEvaluateNow(); + break; + + default: + evaluateNow = false; + break; + } + return evaluateNow; + } + + + protected boolean isAutoEvaluateNow() + { + return delayedEvaluationsMap == null || delayedEvaluationsMap.isEmpty() + || (delayedEvaluationsMap.size() == 1 + && delayedEvaluationsMap.containsKey(JREvaluationTime.EVALUATION_TIME_NOW)); + } + + + protected boolean isEvaluateAuto() + { + return getEvaluationTimeValue() == EvaluationTimeEnum.AUTO && !isAutoEvaluateNow(); + } + + @Override + public String getStyleNameReference() + { + return null; + } + + @Override + public void setStyle(JRStyle style) + { + initStyle = style; + if (conditionalStylesContainer != null) + { + conditionalStylesContainer.collectConditionalStyle(style); + } + } + + @Override + public void setStyleNameReference(String name) + { + throw new UnsupportedOperationException("Style name references not allowed at fill time"); + } + + @Override + public Object clone() + { + throw new UnsupportedOperationException(); + } + + @Override + public Object clone(JRElementGroup parentGroup) + { + throw new UnsupportedOperationException(); + } + + @Override + public JRElement clone(JRElementGroup parentGroup, int y) + { + throw new UnsupportedOperationException(); + } + + @Override + public boolean hasProperties() + { + return mergedProperties != null && mergedProperties.hasProperties(); + } + + @Override + public JRPropertiesMap getPropertiesMap() + { + return mergedProperties; + } + + @Override + public JRPropertiesHolder getParentProperties() + { + //element properties default to report properties + return filler.getMainDataset(); + } + + + @Override + public JRPropertyExpression[] getPropertyExpressions() + { + return propertyExpressions.toArray(new JRPropertyExpression[propertyExpressions.size()]); + } + + protected void transferProperties(JRTemplateElement template) + { + if (staticTransferProperties != null) + { + template.getPropertiesMap().copyOwnProperties(staticTransferProperties); + } + } + + protected void transferProperties(JRPrintElement element) + { + filler.getPropertiesUtil().transferProperties(dynamicProperties, element, + dynamicTransferProperties); + } + + protected JRPropertiesMap getEvaluatedProperties() + { + return mergedProperties; + } + + protected void evaluateProperties(byte evaluation) throws JRException + { + if (propertyExpressions.isEmpty()) + { + dynamicProperties = null; + mergedProperties = staticProperties; + } + else + { + dynamicProperties = new JRPropertiesMap(); + + for (JRPropertyExpression prop : propertyExpressions) + { + String value = (String) evaluateExpression(prop.getValueExpression(), evaluation); + //if (value != null) //for some properties such as data properties in metadata exporters, the null value is significant + { + dynamicProperties.setProperty(prop.getName(), value); + } + } + + mergedProperties = dynamicProperties.cloneProperties(); + mergedProperties.setBaseProperties(staticProperties); + + lookForPartProperty(dynamicProperties); + } + } + + protected void setOriginProvider(JROriginProvider originProvider) + { + this.originProvider = originProvider; + } + + protected JROrigin getElementOrigin() + { + JROrigin elementOrigin = null; + if (originProvider != null) + { + elementOrigin = originProvider.getOrigin(); + } + return elementOrigin; + } + + protected boolean isDelayedStyleEvaluation() + { + return filler.getPropertiesUtil().getBooleanProperty(this, + JRStyle.PROPERTY_EVALUATION_TIME_ENABLED, false); + } + + public JRBaseFiller getFiller() + { + return filler; + } + + + @Override + public boolean hasDynamicProperties() + { + return !propertyExpressions.isEmpty(); + } + + @Override + public boolean hasDynamicProperty(String name) + { + // not called very often for now so doing linear search in array + for (JRPropertyExpression prop : propertyExpressions) + { + if (prop.getName().equals(name)) + { + return true; + } + } + return false; + } + + @Override + public JRPropertiesMap getDynamicProperties() + { + return dynamicProperties; + } + + protected JRStyle getInitStyle() + { + return initStyle; + } + + protected JRElement getParent() + { + return parent; + } + + protected void addDynamicProperty(String name, JRExpression expression) + { + JRDesignPropertyExpression prop = new JRDesignPropertyExpression(); + prop.setName(name); + prop.setValueExpression(expression); + + propertyExpressions.add(prop); + // recomputing + dynamicTransferProperties = findDynamicTransferProperties(); + } + + protected void setExpressionEvaluator(JRFillExpressionEvaluator expressionEvaluator) + { + this.expressionEvaluator = expressionEvaluator; + } + + + /** + * + */ + public static Integer getBookmarkLevel(Object value) throws JRException + { + Integer level = null; + + if (value != null) + { + if (value instanceof Number) + { + level = ((Number)value).intValue(); + } + else + { + try + { + level = Integer.parseInt(value.toString()); + } + catch (NumberFormatException e) + { + //do nothing + } + } + + if (level == null || level < 0) + { + throw + new JRException( + EXCEPTION_MESSAGE_KEY_INVALID_BOOKMARK_LEVEL, + new Object[] {value} + ); + } + } + + return level; + } } diff --git a/jasperreports/src/net/sf/jasperreports/engine/fill/JRFillElementContainer.java b/jasperreports/src/net/sf/jasperreports/engine/fill/JRFillElementContainer.java index 90bdaa5e1c..4c5dbfc498 100644 --- a/jasperreports/src/net/sf/jasperreports/engine/fill/JRFillElementContainer.java +++ b/jasperreports/src/net/sf/jasperreports/engine/fill/JRFillElementContainer.java @@ -57,1256 +57,1256 @@ */ public abstract class JRFillElementContainer extends JRFillElementGroup implements FillContainerContext { - protected JRBaseFiller filler; - - private JRFillElement[] ySortedElements; - private JRFillElement[] stretchElements; - private JRFillElement[] bandBottomElements; - private JRFillElement[] removableElements; - - protected boolean willOverflowWithElements; - protected boolean willOverflowWithWhiteSpace; - protected boolean isOverflow; - protected boolean currentOverflowWithElements; - protected boolean currentOverflowWithWhiteSpace; - private boolean currentOverflowAllowed; - - private int stretchHeight; - private int firstY; - protected boolean atLeastOneElementIsToPrint; - - protected final JRFillExpressionEvaluator expressionEvaluator; - - protected JRFillElement[] deepElements; - - /** - * - */ - protected Set stylesToEvaluate = new HashSet<>(); - protected Map evaluatedStyles = new HashMap<>(); - - protected boolean hasPrintWhenOverflowElement; - - private final boolean legacyElementStretchEnabled; - - - protected JRFillElementContainer(JRBaseFiller filler, JRElementGroup container, JRFillObjectFactory factory) - { - super(container, factory); - - expressionEvaluator = factory.getExpressionEvaluator(); - initDeepElements(); - - this.filler = filler; - - @SuppressWarnings("deprecation") - boolean depFlag = filler.getFillContext().isLegacyElementStretchEnabled(); - legacyElementStretchEnabled = depFlag; - } - - protected JRFillElementContainer(JRFillElementContainer container, JRFillCloneFactory factory) - { - super(container, factory); - - expressionEvaluator = container.expressionEvaluator; - initDeepElements(); - - this.filler = container.filler; - - @SuppressWarnings("deprecation") - boolean depFlag = filler.getFillContext().isLegacyElementStretchEnabled(); - legacyElementStretchEnabled = depFlag; - } - - - protected void initDeepElements() - { - if (elements == null) - { - deepElements = new JRFillElement[0]; - } - else - { - List deepElementsList = new ArrayList<>(elements.length); - collectDeepElements(elements, deepElementsList); - deepElements = new JRFillElement[deepElementsList.size()]; - deepElementsList.toArray(deepElements); - } - } - - private static void collectDeepElements(JRElement[] elements, List deepElementsList) - { - for (int i = 0; i < elements.length; i++) - { - JRElement element = elements[i]; - deepElementsList.add((JRFillElement)element); - - if (element instanceof JRFillFrame) - { - JRFrame frame = (JRFrame) element; - collectDeepElements(frame.getElements(), deepElementsList); - } - } - } - - /** - * @deprecated To be removed. - */ - protected final void _initElements() - { - hasPrintWhenOverflowElement = false; - - if (elements != null && elements.length > 0) - { - List sortedElemsList = new ArrayList<>(); - List stretchElemsList = new ArrayList<>(); - List bandBottomElemsList = new ArrayList<>(); - List removableElemsList = new ArrayList<>(); - - topElementInGroup = null; - bottomElementInGroup = null; - - for (JRFillElement element : elements) - { - sortedElemsList.add(element); - - if (element.getPositionTypeValue() == PositionTypeEnum.FIX_RELATIVE_TO_BOTTOM) - { - bandBottomElemsList.add(element); - } - - if (element.getStretchTypeValue() != StretchTypeEnum.NO_STRETCH) - { - stretchElemsList.add(element); - } - - if (element.isRemoveLineWhenBlank()) - { - removableElemsList.add(element); - } - - if (element.isPrintWhenDetailOverflows()) - { - hasPrintWhenOverflowElement = true; - } - - if ( - topElementInGroup == null || - ( - element.getY() + element.getHeight() < - topElementInGroup.getY() + topElementInGroup.getHeight()) - ) - { - topElementInGroup = element; - } - - if ( - bottomElementInGroup == null || - ( - element.getY() + element.getHeight() > - bottomElementInGroup.getY() + bottomElementInGroup.getHeight()) - ) - { - bottomElementInGroup = element; - } - } - - /* */ - Collections.sort(sortedElemsList, new JRYComparator()); - ySortedElements = new JRFillElement[elements.length]; - sortedElemsList.toArray(ySortedElements); - - /* */ - stretchElements = new JRFillElement[stretchElemsList.size()]; - stretchElemsList.toArray(stretchElements); - - /* */ - bandBottomElements = new JRFillElement[bandBottomElemsList.size()]; - bandBottomElemsList.toArray(bandBottomElements); - - /* */ - removableElements = new JRFillElement[removableElemsList.size()]; - removableElemsList.toArray(removableElements); - } - - /* */ - setDependentElements(); - } - - protected final void initElements() - { - if (isLegacyElementStretchEnabled()) - { - _initElements(); - return; - } - - hasPrintWhenOverflowElement = false; - - if (elements != null && elements.length > 0) - { - List stretchElemsList = new ArrayList<>(); - List bandBottomElemsList = new ArrayList<>(); - List removableElemsList = new ArrayList<>(); - - JRYComparator yComparator = new JRYComparator(); - - /* */ - ySortedElements = Arrays.copyOf(elements, elements.length); - Arrays.sort(ySortedElements, yComparator); - - topElementInGroup = null; - bottomElementInGroup = null; - - for (JRFillElement element : ySortedElements) - { - if (element.getPositionTypeValue() == PositionTypeEnum.FIX_RELATIVE_TO_BOTTOM) - { - bandBottomElemsList.add(element); - } - - if (element.getStretchTypeValue() != StretchTypeEnum.NO_STRETCH) - { - stretchElemsList.add(element); - } - - if (element.isRemoveLineWhenBlank()) - { - removableElemsList.add(element); - } - - if (element.isPrintWhenDetailOverflows()) - { - hasPrintWhenOverflowElement = true; - } - - if ( - topElementInGroup == null || - ( - element.getY() + element.getHeight() < - topElementInGroup.getY() + topElementInGroup.getHeight()) - ) - { - topElementInGroup = element; - } - - if ( - bottomElementInGroup == null || - ( - element.getY() + element.getHeight() > - bottomElementInGroup.getY() + bottomElementInGroup.getHeight()) - ) - { - bottomElementInGroup = element; - } - } - - /* */ - stretchElements = new JRFillElement[stretchElemsList.size()]; - stretchElemsList.toArray(stretchElements); - - /* */ - bandBottomElements = new JRFillElement[bandBottomElemsList.size()]; - bandBottomElemsList.toArray(bandBottomElements); - - /* */ - removableElements = new JRFillElement[removableElemsList.size()]; - removableElemsList.toArray(removableElements); - } - - /* */ - setDependentElements(); - } - - /** - * - */ - private void setDependentElements() { - if (ySortedElements != null && ySortedElements.length > 0) { - outerLoop: - for (int i = 0; i < ySortedElements.length - 1; i++) { - JRFillElement iElem = ySortedElements[i]; - boolean isBreakElem = iElem instanceof JRFillBreak; - int iElemX = iElem.getX(); - int iElemWidth = iElem.getWidth(); - int iElemRight = iElemX + iElemWidth; - int iElemBottom = iElem.getY() + iElem.getHeight(); - for (int j = i + 1; j < ySortedElements.length; j++) { - JRFillElement jElem = ySortedElements[j]; - int jElemX = jElem.getX(); - int jElemWidth = jElem.getWidth(); - int jElemRight = jElemX + jElemWidth; - PositionTypeEnum positionType = jElem.getPositionTypeValue(); - if (((isBreakElem && positionType == PositionTypeEnum.FIX_RELATIVE_TO_TOP) - || positionType == PositionTypeEnum.FLOAT) - && iElemBottom <= jElem.getY() - && iElemWidth + jElemWidth > Math.max(iElemRight, jElemRight) - Math.min(iElemX, jElemX)) { - iElem.addDependantElement(jElem); - if (jElemX <= iElemX && jElemRight >= iElemRight) { - continue outerLoop; - } - } - } - } - } - } - - - /** - * - */ - protected void evaluate(byte evaluation) throws JRException - { - //evaluatePrintWhenExpression(evaluation); - - //if ( - // (isPrintWhenExpressionNull() || - // (!isPrintWhenExpressionNull() && - // isPrintWhenTrue())) - // ) - //{ - JRElement[] allElements = getElements(); - if (allElements != null && allElements.length > 0) - { - for(int i = 0; i < allElements.length; i++) - { - JRFillElement element = (JRFillElement)allElements[i]; - element.setCurrentEvaluation(evaluation); - element.evaluate(evaluation); - } - } - //} - } - - - /** - * - */ - protected void resetElements() - { - if (ySortedElements != null && ySortedElements.length > 0) - { - for(int i = 0; i < ySortedElements.length; i++) - { - JRFillElement element = ySortedElements[i]; - - element.reset(); - - if (!isOverflow) - { - element.setAlreadyPrinted(false); - } - } - } - } - - - /** - * Indicates whether the elements in this container will overflow. - * - * @return whether this container will overflow - */ - public boolean willOverflow() - { - return willOverflowWithElements || willOverflowWithWhiteSpace; - } - - - protected void initFill() - { - isOverflow = willOverflow(); - firstY = 0; - atLeastOneElementIsToPrint = false; - } - - - /** - * @deprecated To be removed. - */ - protected void _prepareElements( - int availableHeight, - boolean isOverflowAllowed - ) throws JRException - { - currentOverflowWithElements = false; - currentOverflowWithWhiteSpace = false; - currentOverflowAllowed = isOverflowAllowed; - - int calculatedStretchHeight = getContainerHeight(); - - firstY = isOverflow ? getActualContainerHeight() : 0; - atLeastOneElementIsToPrint = false; - boolean isFirstYFound = false; - - if (ySortedElements != null && ySortedElements.length > 0) - { - for(int i = 0; i < ySortedElements.length; i++) - { - JRFillElement element = ySortedElements[i]; - - currentOverflowWithElements = - element.prepare( - availableHeight + getElementFirstY(element), - isOverflow - ) - || currentOverflowWithElements; - - element._moveDependantElements(); - - if (element.isToPrint()) - { - if (isOverflow) - { - if (element.isReprinted()) - { - firstY = 0; - } - else if (!isFirstYFound) - { - firstY = element.getY(); - } - isFirstYFound = true; - } - - atLeastOneElementIsToPrint = true; - - int spaceToBottom = getContainerHeight() - element.getY() - element.getHeight(); - if (spaceToBottom < 0) - { - spaceToBottom = 0; - } - - if (calculatedStretchHeight < element.getRelativeY() + element.getStretchHeight() + spaceToBottom) - { - calculatedStretchHeight = element.getRelativeY() + element.getStretchHeight() + spaceToBottom; - } - } - } - } - - if (calculatedStretchHeight > availableHeight + firstY) - { - currentOverflowWithWhiteSpace = true; - } - - // stretchHeight includes firstY, which is subtracted in fillElements - if (currentOverflowWithElements || currentOverflowWithWhiteSpace) - { - stretchHeight = availableHeight + firstY; - } - else - { - stretchHeight = calculatedStretchHeight; - } - - willOverflowWithElements = currentOverflowWithElements && isOverflowAllowed; - willOverflowWithWhiteSpace = currentOverflowWithWhiteSpace && isOverflowAllowed; - } - - - /** - * - */ - protected void prepareElements( - int availableHeight, - boolean isOverflowAllowed - ) throws JRException - { - if (isLegacyElementStretchEnabled()) - { - _prepareElements(availableHeight, isOverflowAllowed); - return; - } - - currentOverflowWithElements = false; - currentOverflowWithWhiteSpace = false; - currentOverflowAllowed = isOverflowAllowed; - - firstY = isOverflow ? getActualContainerHeight() : 0; - atLeastOneElementIsToPrint = false; - boolean isFirstYFound = false; - - if (ySortedElements != null && ySortedElements.length > 0) - { - for (JRFillElement element : ySortedElements) - { - if (currentOverflowWithElements && isOverflowAllowed && element.getY() > firstY + availableHeight) { - break; - } - currentOverflowWithElements = - element.prepare( - availableHeight + getElementFirstY(element), - isOverflow - ) - || currentOverflowWithElements; - - // it does not seem to make sense for elements that do not print because of their isToPrint() returning false, - // to push other dependent elements, but it was always like that; furthermore, such disappearing elements are pushed by - // other elements and also participate in white space collapse later on, so it is somewhat fair to also allow them - // to push others - element.moveDependantElements(); - - if (element.isToPrint()) - { - if (isOverflow) - { - if (element.isReprinted()) - { - firstY = 0; - } - else if (!isFirstYFound) - { - firstY = element.getY(); - } - isFirstYFound = true; - } - - atLeastOneElementIsToPrint = true; - } - } - } - - // normally, we should add firstY here, because it says below stretchHeight contains firstY; - // this initialization matters when band overflows with white space and no element is rendered on the next page; - // but we don't add it because, historically, bands did not preserve the white space when overflowing, unlike frames for example, - // which re-render at their design height even when overflowing with white space - stretchHeight = getContainerHeight();// + firstY; - - // certain elements have stretched to their natural height, while others have been moved in the process; - // we are now ready to calculate the stretch height of the current container, so that we can use that for - // moving elements to bottom, before attempting to remove blank elements; - // ATTENTION: this calculation needed to be in a separate ySortedElement loop as the above one, because - // it needs to take into consideration the displacement of dependent elements made after each element prepare - prepareStretchHeight(availableHeight, isOverflowAllowed); - - moveBandBottomElements(); - - // removing blank elements and thus collapsing white space performs both - // element height shrinking and relative Y repositioning, also changing the current - // container stretch height - removeBlankElements(); - - // we are first stretching elements relative to group, because they do not need container height and might - // actually cause the container to stretch further - stretchElementsToElementGroup(); - - // recalculating container stretch height to account for element group stretching, just before triggering - // container related stretch - prepareStretchHeight(availableHeight, isOverflowAllowed); - - moveBandBottomElements(); - - // container based element stretching is the last one to be performed - stretchElementsToContainer(); - } - - /** - * - */ - protected void prepareStretchHeight( - int availableHeight, - boolean isOverflowAllowed - ) throws JRException - { - int calculatedStretchHeight = calculateStretchHeight(); - - if (calculatedStretchHeight > availableHeight + firstY) - { - currentOverflowWithWhiteSpace = true; - } - - // stretchHeight includes firstY, which is subtracted in fillElements - if (currentOverflowWithElements || currentOverflowWithWhiteSpace) - { - stretchHeight = availableHeight + firstY; - } - else - { - stretchHeight = calculatedStretchHeight; - } - - willOverflowWithElements = currentOverflowWithElements && isOverflowAllowed; - willOverflowWithWhiteSpace = currentOverflowWithWhiteSpace && isOverflowAllowed; - } - - /** - * - */ - protected int calculateStretchHeight() throws JRException - { - int calculatedStretchHeight = -1; - - if (ySortedElements != null && ySortedElements.length > 0) - { - int containerHeight = getContainerHeight(); - - for (JRFillElement element : ySortedElements) - { - if (element.isToPrint()) - { - int spaceToBottom = containerHeight - (element.getY() + element.getHeight()) - element.getCollapsedHeightBelow(); - if (spaceToBottom < 0) - { - spaceToBottom = 0; - } - - if (calculatedStretchHeight < element.getRelativeY() + element.getStretchHeight() + spaceToBottom) - { - calculatedStretchHeight = element.getRelativeY() + element.getStretchHeight() + spaceToBottom; - } - } - } - } - - if (calculatedStretchHeight < 0) - { - // there was no element printing; so trying to preserve stretchHeight - calculatedStretchHeight = stretchHeight; - } - - return calculatedStretchHeight; - } - - /** - * - */ - public boolean isLegacyElementStretchEnabled() - { - return legacyElementStretchEnabled; - } - - @Override - public boolean isCurrentOverflow() - { - return currentOverflowWithElements || currentOverflowWithWhiteSpace; - } - - @Override - public boolean isCurrentOverflowAllowed() - { - return currentOverflowAllowed; - } - - private int getElementFirstY(JRFillElement element) - { - int elemFirstY; - if (!isOverflow || hasPrintWhenOverflowElement) - { - elemFirstY = 0; - } - else if (element.getY() >= firstY) - { - elemFirstY = firstY; - } - else - { - elemFirstY = element.getY(); - } - return elemFirstY; - } - - /** - * @deprecated To be removed. - */ - protected void _setStretchHeight(int stretchHeight) - { - if (stretchHeight > this.stretchHeight) - { - this.stretchHeight = stretchHeight; - } - } - - /** - * This method is deprecated and is going to be removed. - * Not marked as deprecated to avoid deprecation warnings. - */ - @SuppressWarnings("deprecation") - protected void stretchElements() - { - if (stretchElements != null && stretchElements.length > 0) - { - for(int i = 0; i < stretchElements.length; i++) - { - JRFillElement element = stretchElements[i]; - - element._stretchElement(stretchHeight - getContainerHeight());//TODO subtract firstY? - - element._moveDependantElements(); - } - } - - if (ySortedElements != null && ySortedElements.length > 0) - { - for(int i = 0; i < ySortedElements.length; i++) - { - JRFillElement element = ySortedElements[i]; - - element.stretchHeightFinal(); - } - } - } - - protected void setStretchHeight(int stretchHeight) - { - if (isLegacyElementStretchEnabled()) - { - _setStretchHeight(stretchHeight); - return; - } - - this.stretchHeight = stretchHeight; - } - - /** - * - */ - protected void stretchElementsToElementGroup() - { - if (stretchElements != null && stretchElements.length > 0) - { - for (int i = 0; i < stretchElements.length; i++) - { - JRFillElement element = stretchElements[i]; - - if (element.isToPrint()) - { - boolean applied = element.stretchElementToElementGroup(); - - if (applied) - { - element.moveDependantElements(); - } - } - } - } - } - - /** - * - */ - protected void stretchElementsToContainer() - { - if (stretchElements != null && stretchElements.length > 0) - { - int containerStretch = stretchHeight - getContainerHeight(); - - for (int i = 0; i < stretchElements.length; i++) - { - JRFillElement element = stretchElements[i]; - - if (element.isToPrint()) - { - boolean applied = element.stretchElementToContainer(containerStretch); - - if (applied) - { - element.moveDependantElements(); - } - } - } - } - } - - - protected int getStretchHeight() - { - return stretchHeight; - } - - - /** - * - */ - protected void moveBandBottomElements() - { - //if (!willOverflow) - //{ - if (bandBottomElements != null && bandBottomElements.length > 0) - { - for (int i = 0; i < bandBottomElements.length; i++) - { - JRFillElement element = bandBottomElements[i]; - - if (element.isToPrint()) - { - // band bottom elements do not print if there will be an overflow - if (currentOverflowWithElements || currentOverflowWithWhiteSpace) - { - currentOverflowWithElements = true; - } - - element.setToPrint(!((currentOverflowWithElements || willOverflowWithWhiteSpace) && currentOverflowAllowed));// don't use willOverflow() method as it is overridden at least in bands - } - - if (element.isToPrint()) - { - element.setRelativeY( - element.getY() + stretchHeight - getActualContainerHeight() - ); - } - } - } - //} - } - - - /** - * @deprecated To be removed. - */ - protected void _removeBlankElements() - { - JRElement[] remElems = removableElements; - if (remElems != null && remElems.length > 0) - { - JRElement[] elems = ySortedElements; - - for(int i = 0; i < remElems.length; i++) - { - JRFillElement iElem = (JRFillElement)remElems[i]; - - int blankHeight; - if (iElem.isToPrint()) - { - blankHeight = iElem.getHeight() - iElem.getStretchHeight(); - } - else - { - blankHeight = iElem.getHeight();//FIXME subreports that stretch and then don't print, will not remove all space - } - - if ( - blankHeight > 0 && - iElem.getRelativeY() + iElem.getStretchHeight() <= stretchHeight && - iElem.getRelativeY() >= firstY - ) - { - int blankY = iElem.getRelativeY() + iElem.getHeight() - blankHeight; - boolean isToRemove = true; - - for(int j = 0; j < elems.length; j++) - { - JRFillElement jElem = (JRFillElement)elems[j]; - - if (iElem != jElem && jElem.isToPrint()) - { - int top = - Math.min(blankY, jElem.getRelativeY()); - int bottom = - Math.max( - blankY + blankHeight, - jElem.getRelativeY() + jElem.getStretchHeight() - ); - - if (blankHeight + jElem.getStretchHeight() > bottom - top) - { - isToRemove = false; - break; - } - } - } - - if (isToRemove) - { - for(int j = 0; j < elems.length; j++) - { - JRFillElement jElem = (JRFillElement)elems[j]; - - if (jElem.getRelativeY() >= blankY + blankHeight) - { - jElem.setRelativeY(jElem.getRelativeY() - blankHeight); - } - } - - stretchHeight = stretchHeight - blankHeight; - } - } - } - } - } - - - /** - * - */ - protected void removeBlankElements() - { - if (isLegacyElementStretchEnabled()) - { - _removeBlankElements(); - return; - } - - if (removableElements != null && removableElements.length > 0) - { - for (JRFillElement remElem : removableElements) - { - int blankHeight; - if (remElem.isToPrint()) - { - blankHeight = remElem.getHeight() - remElem.getStretchHeight(); - } - else - { - blankHeight = remElem.getHeight();//FIXME subreports that stretch and then don't print, will not remove all space - } - - if ( - blankHeight > 0 && - remElem.getRelativeY() + remElem.getStretchHeight() <= stretchHeight && - remElem.getRelativeY() >= firstY - ) - { - int blankY = remElem.getRelativeY() + remElem.getHeight() - blankHeight; - boolean isToRemove = true; - - for (JRFillElement jElem : ySortedElements) - { - if (remElem != jElem && jElem.isToPrint()) - { - int top = - Math.min(blankY, jElem.getRelativeY()); - int bottom = - Math.max( - blankY + blankHeight, - jElem.getRelativeY() + jElem.getStretchHeight() - ); - - if (blankHeight + jElem.getStretchHeight() > bottom - top) - { - isToRemove = false; - break; - } - } - } - - if (isToRemove) - { - for (JRFillElement jElem : ySortedElements) - { - if (jElem.getRelativeY() + jElem.getStretchHeight() <= blankY) - { - jElem.setCollapsedHeightBelow(jElem.getCollapsedHeightBelow() + blankHeight); - } - - if (jElem.getRelativeY() >= blankY + blankHeight) - { - jElem.setCollapsedHeightAbove(jElem.getCollapsedHeightAbove() + blankHeight); - jElem.setRelativeY(jElem.getRelativeY() - blankHeight); - } - } - - stretchHeight = stretchHeight - blankHeight; - } - } - } - } - } - - - /** - * Fills the elements from this container into a print element container. - * - * @param printContainer the print element container - * @throws JRException - */ - public void fillElements(JRPrintElementContainer printContainer) throws JRException - { - //int maxStretch = 0; - //int stretch = 0; - int maxWidth = 0; - JRElement[] allElements = getElements(); - if (allElements != null && allElements.length > 0) - { - for(int i = 0; i < allElements.length; i++) - { - JRFillElement element = (JRFillElement)allElements[i]; - - element.setRelativeY(element.getRelativeY() - firstY); - - if (element.getRelativeY() + element.getStretchHeight() > stretchHeight - firstY) - { - element.setToPrint(false); - } - - element.setAlreadyPrinted(element.isToPrint() || element.isAlreadyPrinted()); - - if (element.isToPrint()) - { - JRPrintElement printElement = element.fill(); - //printElement.setY(printElement.getY() - firstY); - - if (printElement != null) - { - //FIXME not all elements affect height - //stretch = printElement.getY() + firstY + printElement.getHeight() - element.getY() - element.getHeight(); - //if (stretch > maxStretch) - //{ - // maxStretch = stretch; - //} - printContainer.addElement(printElement); - if (printElement.getX() + printElement.getWidth() > maxWidth) - { - maxWidth = printElement.getX() + printElement.getWidth(); - } - } - - if (element instanceof JRFillSubreport) - { - JRFillSubreport subreport = (JRFillSubreport)element; - - List styles = subreport.subreportFiller.getJasperPrint().getStylesList(); - for(int j = 0; j < styles.size(); j++) - { - filler.addPrintStyle(styles.get(j)); - } - - List origins = subreport.subreportFiller.getJasperPrint().getOriginsList(); - for(int j = 0; j < origins.size(); j++) - { - filler.getJasperPrint().addOrigin(origins.get(j)); - } - - Collection printElements = subreport.getPrintElements(); - addSubElements(printContainer, element, printElements); - if (subreport.getX() + subreport.getPrintContentsWidth() > maxWidth) - { - maxWidth = subreport.getX() + subreport.getPrintContentsWidth(); - } - - subreport.subreportPageFilled(); - } - - // crosstabs do not return a fill() element - if (element instanceof JRFillCrosstab) - { - JRFillCrosstab crosstab = (JRFillCrosstab) element; - List printElements = crosstab.getPrintElements(); - addSubElements(printContainer, element, printElements); - if (crosstab.getX() + crosstab.getPrintElementsWidth() > maxWidth) - { - maxWidth = crosstab.getX() + crosstab.getPrintElementsWidth(); - } - } - } - } - } - - //printBand.setHeight(getHeight() + maxStretch - firstY); - printContainer.setHeight(stretchHeight - firstY); - printContainer.setContentsWidth(maxWidth); - } - - - protected void addSubElements(JRPrintElementContainer printContainer, JRFillElement element, - Collection printElements) - { - if (printContainer instanceof OffsetElementsContainer) - { - // adding the subelements as whole lists to bands so that we don't need - // another virtualized list at print band level - ((OffsetElementsContainer) printContainer).addOffsetElements(printElements, - element.getX(), element.getRelativeY()); - } - else - { - if (printElements != null && printElements.size() > 0) - { - for(Iterator it = printElements.iterator(); it.hasNext();) - { - JRPrintElement printElement =it.next(); - printElement.setX(element.getX() + printElement.getX()); - printElement.setY(element.getRelativeY() + printElement.getY()); - printContainer.addElement(printElement); - } - } - } - } - - - /** - * - */ - protected void rewind() throws JRException - { - if (ySortedElements != null && ySortedElements.length > 0) - { - for(int i = 0; i < ySortedElements.length; i++) - { - JRFillElement element = ySortedElements[i]; - - element.rewind(); - - element.setAlreadyPrinted(false); - } - } - - willOverflowWithElements = false; - willOverflowWithWhiteSpace = false; - } - - protected int getFirstY() - { - return firstY; - } - - - /** - * Returns the actual height of the element container. - * Some element containers such as frames have a larger calculated container height, resulting from content being placed beyond container declared height. - * - * @return the height of the element container - */ - protected abstract int getActualContainerHeight(); - - - /** - * Returns the height of the element container. - * - * @return the height of the element container - */ - protected abstract int getContainerHeight(); - - - /** - * Find all styles containing conditional styles which are referenced by elements in this band. - */ - protected void initConditionalStyles() - { - filler.addDefaultStyleListener(new JRBaseFiller.DefaultStyleListener(){ - @Override - public void defaultStyleSet(JRStyle style) - { - collectConditionalStyle(style); - } - }); - - for (int i = 0; i < deepElements.length; i++) - { - JRStyle style = deepElements[i].initStyle; - collectConditionalStyle(style); - } - - if (deepElements.length > 0) - { - for(int i = 0; i < deepElements.length; i++) - { - deepElements[i].setConditionalStylesContainer(this); - } - } - } - - protected void collectConditionalStyle(JRStyle style) - { - if (style != null)// && style.getConditionalStyles() != null) - { - stylesToEvaluate.add(style); - } - } - - - protected void evaluateConditionalStyles(byte evaluation) throws JRException - { - for (Iterator it = stylesToEvaluate.iterator(); it.hasNext();) - { - evaluateConditionalStyle(it.next(), evaluation); - } - } - - - protected JRStyle evaluateConditionalStyle(JRStyle initialStyle, byte evaluation) throws JRException - { - JRStyle consolidatedStyle = initialStyle; - - StringBuilder code = new StringBuilder(); - List condStylesToApply = new ArrayList<>(); - - boolean anyTrue = buildConsolidatedStyle(initialStyle, evaluation, code, condStylesToApply); - - if (anyTrue) - { - String consolidatedStyleName = initialStyle.getName() + "|" + code.toString(); - consolidatedStyle = filler.getJasperPrint().getStylesMap().get(consolidatedStyleName); - if (consolidatedStyle == null) - { - JRBaseStyle style = new JRBaseStyle(initialStyle.getDefaultStyleProvider(), consolidatedStyleName); - for (int j = condStylesToApply.size() - 1; j >= 0; j--) - { - StyleUtil.appendStyle(style, condStylesToApply.get(j)); - } - - // deduplicate to previously created identical instances - style = filler.fillContext.deduplicate(style); - filler.addPrintStyle(style); - - consolidatedStyle = style; - } - } - - evaluatedStyles.put(initialStyle, consolidatedStyle); - - return consolidatedStyle; - } - - - protected boolean buildConsolidatedStyle(JRStyle style, byte evaluation, StringBuilder code, List condStylesToApply) throws JRException - { - boolean anyTrue = false; - - JRConditionalStyle[] conditionalStyles = style.getConditionalStyles(); - if (conditionalStyles != null && conditionalStyles.length > 0) - { - for (int j = 0; j < conditionalStyles.length; j++) - { - JRConditionalStyle conditionalStyle = conditionalStyles[j]; - Boolean expressionValue = - (Boolean) expressionEvaluator.evaluate( - conditionalStyle.getConditionExpression(), - evaluation - ); - - boolean condition; - if (expressionValue == null) - { - condition = false; - } - else - { - condition = expressionValue; - } - - code.append(condition ? '1' : '0'); - anyTrue = anyTrue | condition; - - if (condition) - { - condStylesToApply.add(conditionalStyle); - } - } - } - - condStylesToApply.add(style); - - if (style.getStyle() != null) - { - anyTrue = anyTrue | buildConsolidatedStyle(style.getStyle(), evaluation, code, condStylesToApply); - } - return anyTrue; - } - - - public JRStyle getEvaluatedConditionalStyle(JRStyle parentStyle) - { - return evaluatedStyles.get(parentStyle); - } - - protected final void setElementOriginProvider(JROriginProvider originProvider) - { - if (originProvider != null) - { - for (int i = 0; i < deepElements.length; i++) - { - deepElements[i].setOriginProvider(originProvider); - } - } - } + protected JRBaseFiller filler; + + private JRFillElement[] ySortedElements; + private JRFillElement[] stretchElements; + private JRFillElement[] bandBottomElements; + private JRFillElement[] removableElements; + + protected boolean willOverflowWithElements; + protected boolean willOverflowWithWhiteSpace; + protected boolean isOverflow; + protected boolean currentOverflowWithElements; + protected boolean currentOverflowWithWhiteSpace; + private boolean currentOverflowAllowed; + + private int stretchHeight; + private int firstY; + protected boolean atLeastOneElementIsToPrint; + + protected final JRFillExpressionEvaluator expressionEvaluator; + + protected JRFillElement[] deepElements; + + /** + * + */ + protected Set stylesToEvaluate = new HashSet<>(); + protected Map evaluatedStyles = new HashMap<>(); + + protected boolean hasPrintWhenOverflowElement; + + private final boolean legacyElementStretchEnabled; + + + protected JRFillElementContainer(JRBaseFiller filler, JRElementGroup container, JRFillObjectFactory factory) + { + super(container, factory); + + expressionEvaluator = factory.getExpressionEvaluator(); + initDeepElements(); + + this.filler = filler; + + @SuppressWarnings("deprecation") + boolean depFlag = filler.getFillContext().isLegacyElementStretchEnabled(); + legacyElementStretchEnabled = depFlag; + } + + protected JRFillElementContainer(JRFillElementContainer container, JRFillCloneFactory factory) + { + super(container, factory); + + expressionEvaluator = container.expressionEvaluator; + initDeepElements(); + + this.filler = container.filler; + + @SuppressWarnings("deprecation") + boolean depFlag = filler.getFillContext().isLegacyElementStretchEnabled(); + legacyElementStretchEnabled = depFlag; + } + + + protected void initDeepElements() + { + if (elements == null) + { + deepElements = new JRFillElement[0]; + } + else + { + List deepElementsList = new ArrayList<>(elements.length); + collectDeepElements(elements, deepElementsList); + deepElements = new JRFillElement[deepElementsList.size()]; + deepElementsList.toArray(deepElements); + } + } + + private static void collectDeepElements(JRElement[] elements, List deepElementsList) + { + for (int i = 0; i < elements.length; i++) + { + JRElement element = elements[i]; + deepElementsList.add((JRFillElement)element); + + if (element instanceof JRFillFrame) + { + JRFrame frame = (JRFrame) element; + collectDeepElements(frame.getElements(), deepElementsList); + } + } + } + + /** + * @deprecated To be removed. + */ + protected final void _initElements() + { + hasPrintWhenOverflowElement = false; + + if (elements != null && elements.length > 0) + { + List sortedElemsList = new ArrayList<>(); + List stretchElemsList = new ArrayList<>(); + List bandBottomElemsList = new ArrayList<>(); + List removableElemsList = new ArrayList<>(); + + topElementInGroup = null; + bottomElementInGroup = null; + + for (JRFillElement element : elements) + { + sortedElemsList.add(element); + + if (element.getPositionTypeValue() == PositionTypeEnum.FIX_RELATIVE_TO_BOTTOM) + { + bandBottomElemsList.add(element); + } + + if (element.getStretchTypeValue() != StretchTypeEnum.NO_STRETCH) + { + stretchElemsList.add(element); + } + + if (element.isRemoveLineWhenBlank()) + { + removableElemsList.add(element); + } + + if (element.isPrintWhenDetailOverflows()) + { + hasPrintWhenOverflowElement = true; + } + + if ( + topElementInGroup == null || + ( + element.getY() + element.getHeight() < + topElementInGroup.getY() + topElementInGroup.getHeight()) + ) + { + topElementInGroup = element; + } + + if ( + bottomElementInGroup == null || + ( + element.getY() + element.getHeight() > + bottomElementInGroup.getY() + bottomElementInGroup.getHeight()) + ) + { + bottomElementInGroup = element; + } + } + + /* */ + Collections.sort(sortedElemsList, new JRYComparator()); + ySortedElements = new JRFillElement[elements.length]; + sortedElemsList.toArray(ySortedElements); + + /* */ + stretchElements = new JRFillElement[stretchElemsList.size()]; + stretchElemsList.toArray(stretchElements); + + /* */ + bandBottomElements = new JRFillElement[bandBottomElemsList.size()]; + bandBottomElemsList.toArray(bandBottomElements); + + /* */ + removableElements = new JRFillElement[removableElemsList.size()]; + removableElemsList.toArray(removableElements); + } + + /* */ + setDependentElements(); + } + + protected final void initElements() + { + if (isLegacyElementStretchEnabled()) + { + _initElements(); + return; + } + + hasPrintWhenOverflowElement = false; + + if (elements != null && elements.length > 0) + { + List stretchElemsList = new ArrayList<>(); + List bandBottomElemsList = new ArrayList<>(); + List removableElemsList = new ArrayList<>(); + + JRYComparator yComparator = new JRYComparator(); + + /* */ + ySortedElements = Arrays.copyOf(elements, elements.length); + Arrays.sort(ySortedElements, yComparator); + + topElementInGroup = null; + bottomElementInGroup = null; + + for (JRFillElement element : ySortedElements) + { + if (element.getPositionTypeValue() == PositionTypeEnum.FIX_RELATIVE_TO_BOTTOM) + { + bandBottomElemsList.add(element); + } + + if (element.getStretchTypeValue() != StretchTypeEnum.NO_STRETCH) + { + stretchElemsList.add(element); + } + + if (element.isRemoveLineWhenBlank()) + { + removableElemsList.add(element); + } + + if (element.isPrintWhenDetailOverflows()) + { + hasPrintWhenOverflowElement = true; + } + + if ( + topElementInGroup == null || + ( + element.getY() + element.getHeight() < + topElementInGroup.getY() + topElementInGroup.getHeight()) + ) + { + topElementInGroup = element; + } + + if ( + bottomElementInGroup == null || + ( + element.getY() + element.getHeight() > + bottomElementInGroup.getY() + bottomElementInGroup.getHeight()) + ) + { + bottomElementInGroup = element; + } + } + + /* */ + stretchElements = new JRFillElement[stretchElemsList.size()]; + stretchElemsList.toArray(stretchElements); + + /* */ + bandBottomElements = new JRFillElement[bandBottomElemsList.size()]; + bandBottomElemsList.toArray(bandBottomElements); + + /* */ + removableElements = new JRFillElement[removableElemsList.size()]; + removableElemsList.toArray(removableElements); + } + + /* */ + setDependentElements(); + } + + /** + * + */ + private void setDependentElements() { + if (ySortedElements != null && ySortedElements.length > 0) { + outerLoop: + for (int i = 0; i < ySortedElements.length - 1; i++) { + JRFillElement iElem = ySortedElements[i]; + boolean isBreakElem = iElem instanceof JRFillBreak; + int iElemX = iElem.getX(); + int iElemWidth = iElem.getWidth(); + int iElemRight = iElemX + iElemWidth; + int iElemBottom = iElem.getY() + iElem.getHeight(); + for (int j = i + 1; j < ySortedElements.length; j++) { + JRFillElement jElem = ySortedElements[j]; + int jElemX = jElem.getX(); + int jElemWidth = jElem.getWidth(); + int jElemRight = jElemX + jElemWidth; + PositionTypeEnum positionType = jElem.getPositionTypeValue(); + if (((isBreakElem && positionType == PositionTypeEnum.FIX_RELATIVE_TO_TOP) + || positionType == PositionTypeEnum.FLOAT) + && iElemBottom <= jElem.getY() + && iElemWidth + jElemWidth > Math.max(iElemRight, jElemRight) - Math.min(iElemX, jElemX)) { + iElem.addDependantElement(jElem); + if (jElemX <= iElemX && jElemRight >= iElemRight) { + continue outerLoop; + } + } + } + } + } + } + + + /** + * + */ + protected void evaluate(byte evaluation) throws JRException + { + //evaluatePrintWhenExpression(evaluation); + + //if ( + // (isPrintWhenExpressionNull() || + // (!isPrintWhenExpressionNull() && + // isPrintWhenTrue())) + // ) + //{ + JRElement[] allElements = getElements(); + if (allElements != null && allElements.length > 0) + { + for(int i = 0; i < allElements.length; i++) + { + JRFillElement element = (JRFillElement)allElements[i]; + element.setCurrentEvaluation(evaluation); + element.evaluate(evaluation); + } + } + //} + } + + + /** + * + */ + protected void resetElements() + { + if (ySortedElements != null && ySortedElements.length > 0) + { + for(int i = 0; i < ySortedElements.length; i++) + { + JRFillElement element = ySortedElements[i]; + + element.reset(); + + if (!isOverflow) + { + element.setAlreadyPrinted(false); + } + } + } + } + + + /** + * Indicates whether the elements in this container will overflow. + * + * @return whether this container will overflow + */ + public boolean willOverflow() + { + return willOverflowWithElements || willOverflowWithWhiteSpace; + } + + + protected void initFill() + { + isOverflow = willOverflow(); + firstY = 0; + atLeastOneElementIsToPrint = false; + } + + + /** + * @deprecated To be removed. + */ + protected void _prepareElements( + int availableHeight, + boolean isOverflowAllowed + ) throws JRException + { + currentOverflowWithElements = false; + currentOverflowWithWhiteSpace = false; + currentOverflowAllowed = isOverflowAllowed; + + int calculatedStretchHeight = getContainerHeight(); + + firstY = isOverflow ? getActualContainerHeight() : 0; + atLeastOneElementIsToPrint = false; + boolean isFirstYFound = false; + + if (ySortedElements != null && ySortedElements.length > 0) + { + for(int i = 0; i < ySortedElements.length; i++) + { + JRFillElement element = ySortedElements[i]; + + currentOverflowWithElements = + element.prepare( + availableHeight + getElementFirstY(element), + isOverflow + ) + || currentOverflowWithElements; + + element._moveDependantElements(); + + if (element.isToPrint()) + { + if (isOverflow) + { + if (element.isReprinted()) + { + firstY = 0; + } + else if (!isFirstYFound) + { + firstY = element.getY(); + } + isFirstYFound = true; + } + + atLeastOneElementIsToPrint = true; + + int spaceToBottom = getContainerHeight() - element.getY() - element.getHeight(); + if (spaceToBottom < 0) + { + spaceToBottom = 0; + } + + if (calculatedStretchHeight < element.getRelativeY() + element.getStretchHeight() + spaceToBottom) + { + calculatedStretchHeight = element.getRelativeY() + element.getStretchHeight() + spaceToBottom; + } + } + } + } + + if (calculatedStretchHeight > availableHeight + firstY) + { + currentOverflowWithWhiteSpace = true; + } + + // stretchHeight includes firstY, which is subtracted in fillElements + if (currentOverflowWithElements || currentOverflowWithWhiteSpace) + { + stretchHeight = availableHeight + firstY; + } + else + { + stretchHeight = calculatedStretchHeight; + } + + willOverflowWithElements = currentOverflowWithElements && isOverflowAllowed; + willOverflowWithWhiteSpace = currentOverflowWithWhiteSpace && isOverflowAllowed; + } + + + /** + * + */ + protected void prepareElements( + int availableHeight, + boolean isOverflowAllowed + ) throws JRException + { + if (isLegacyElementStretchEnabled()) + { + _prepareElements(availableHeight, isOverflowAllowed); + return; + } + + currentOverflowWithElements = false; + currentOverflowWithWhiteSpace = false; + currentOverflowAllowed = isOverflowAllowed; + + firstY = isOverflow ? getActualContainerHeight() : 0; + atLeastOneElementIsToPrint = false; + boolean isFirstYFound = false; + + if (ySortedElements != null && ySortedElements.length > 0) + { + for (JRFillElement element : ySortedElements) + { + if (currentOverflowWithElements && isOverflowAllowed && element.getY() > firstY + availableHeight) { + break; + } + currentOverflowWithElements = + element.prepare( + availableHeight + getElementFirstY(element), + isOverflow + ) + || currentOverflowWithElements; + + // it does not seem to make sense for elements that do not print because of their isToPrint() returning false, + // to push other dependent elements, but it was always like that; furthermore, such disappearing elements are pushed by + // other elements and also participate in white space collapse later on, so it is somewhat fair to also allow them + // to push others + element.moveDependantElements(); + + if (element.isToPrint()) + { + if (isOverflow) + { + if (element.isReprinted()) + { + firstY = 0; + } + else if (!isFirstYFound) + { + firstY = element.getY(); + } + isFirstYFound = true; + } + + atLeastOneElementIsToPrint = true; + } + } + } + + // normally, we should add firstY here, because it says below stretchHeight contains firstY; + // this initialization matters when band overflows with white space and no element is rendered on the next page; + // but we don't add it because, historically, bands did not preserve the white space when overflowing, unlike frames for example, + // which re-render at their design height even when overflowing with white space + stretchHeight = getContainerHeight();// + firstY; + + // certain elements have stretched to their natural height, while others have been moved in the process; + // we are now ready to calculate the stretch height of the current container, so that we can use that for + // moving elements to bottom, before attempting to remove blank elements; + // ATTENTION: this calculation needed to be in a separate ySortedElement loop as the above one, because + // it needs to take into consideration the displacement of dependent elements made after each element prepare + prepareStretchHeight(availableHeight, isOverflowAllowed); + + moveBandBottomElements(); + + // removing blank elements and thus collapsing white space performs both + // element height shrinking and relative Y repositioning, also changing the current + // container stretch height + removeBlankElements(); + + // we are first stretching elements relative to group, because they do not need container height and might + // actually cause the container to stretch further + stretchElementsToElementGroup(); + + // recalculating container stretch height to account for element group stretching, just before triggering + // container related stretch + prepareStretchHeight(availableHeight, isOverflowAllowed); + + moveBandBottomElements(); + + // container based element stretching is the last one to be performed + stretchElementsToContainer(); + } + + /** + * + */ + protected void prepareStretchHeight( + int availableHeight, + boolean isOverflowAllowed + ) throws JRException + { + int calculatedStretchHeight = calculateStretchHeight(); + + if (calculatedStretchHeight > availableHeight + firstY) + { + currentOverflowWithWhiteSpace = true; + } + + // stretchHeight includes firstY, which is subtracted in fillElements + if (currentOverflowWithElements || currentOverflowWithWhiteSpace) + { + stretchHeight = availableHeight + firstY; + } + else + { + stretchHeight = calculatedStretchHeight; + } + + willOverflowWithElements = currentOverflowWithElements && isOverflowAllowed; + willOverflowWithWhiteSpace = currentOverflowWithWhiteSpace && isOverflowAllowed; + } + + /** + * + */ + protected int calculateStretchHeight() throws JRException + { + int calculatedStretchHeight = -1; + + if (ySortedElements != null && ySortedElements.length > 0) + { + int containerHeight = getContainerHeight(); + + for (JRFillElement element : ySortedElements) + { + if (element.isToPrint()) + { + int spaceToBottom = containerHeight - (element.getY() + element.getHeight()) - element.getCollapsedHeightBelow(); + if (spaceToBottom < 0) + { + spaceToBottom = 0; + } + + if (calculatedStretchHeight < element.getRelativeY() + element.getStretchHeight() + spaceToBottom) + { + calculatedStretchHeight = element.getRelativeY() + element.getStretchHeight() + spaceToBottom; + } + } + } + } + + if (calculatedStretchHeight < 0) + { + // there was no element printing; so trying to preserve stretchHeight + calculatedStretchHeight = stretchHeight; + } + + return calculatedStretchHeight; + } + + /** + * + */ + public boolean isLegacyElementStretchEnabled() + { + return legacyElementStretchEnabled; + } + + @Override + public boolean isCurrentOverflow() + { + return currentOverflowWithElements || currentOverflowWithWhiteSpace; + } + + @Override + public boolean isCurrentOverflowAllowed() + { + return currentOverflowAllowed; + } + + private int getElementFirstY(JRFillElement element) + { + int elemFirstY; + if (!isOverflow || hasPrintWhenOverflowElement) + { + elemFirstY = 0; + } + else if (element.getY() >= firstY) + { + elemFirstY = firstY; + } + else + { + elemFirstY = element.getY(); + } + return elemFirstY; + } + + /** + * @deprecated To be removed. + */ + protected void _setStretchHeight(int stretchHeight) + { + if (stretchHeight > this.stretchHeight) + { + this.stretchHeight = stretchHeight; + } + } + + /** + * This method is deprecated and is going to be removed. + * Not marked as deprecated to avoid deprecation warnings. + */ + @SuppressWarnings("deprecation") + protected void stretchElements() + { + if (stretchElements != null && stretchElements.length > 0) + { + for(int i = 0; i < stretchElements.length; i++) + { + JRFillElement element = stretchElements[i]; + + element._stretchElement(stretchHeight - getContainerHeight());//TODO subtract firstY? + + element._moveDependantElements(); + } + } + + if (ySortedElements != null && ySortedElements.length > 0) + { + for(int i = 0; i < ySortedElements.length; i++) + { + JRFillElement element = ySortedElements[i]; + + element.stretchHeightFinal(); + } + } + } + + protected void setStretchHeight(int stretchHeight) + { + if (isLegacyElementStretchEnabled()) + { + _setStretchHeight(stretchHeight); + return; + } + + this.stretchHeight = stretchHeight; + } + + /** + * + */ + protected void stretchElementsToElementGroup() + { + if (stretchElements != null && stretchElements.length > 0) + { + for (int i = 0; i < stretchElements.length; i++) + { + JRFillElement element = stretchElements[i]; + + if (element.isToPrint()) + { + boolean applied = element.stretchElementToElementGroup(); + + if (applied) + { + element.moveDependantElements(); + } + } + } + } + } + + /** + * + */ + protected void stretchElementsToContainer() + { + if (stretchElements != null && stretchElements.length > 0) + { + int containerStretch = stretchHeight - getContainerHeight(); + + for (int i = 0; i < stretchElements.length; i++) + { + JRFillElement element = stretchElements[i]; + + if (element.isToPrint()) + { + boolean applied = element.stretchElementToContainer(containerStretch); + + if (applied) + { + element.moveDependantElements(); + } + } + } + } + } + + + protected int getStretchHeight() + { + return stretchHeight; + } + + + /** + * + */ + protected void moveBandBottomElements() + { + //if (!willOverflow) + //{ + if (bandBottomElements != null && bandBottomElements.length > 0) + { + for (int i = 0; i < bandBottomElements.length; i++) + { + JRFillElement element = bandBottomElements[i]; + + if (element.isToPrint()) + { + // band bottom elements do not print if there will be an overflow + if (currentOverflowWithElements || currentOverflowWithWhiteSpace) + { + currentOverflowWithElements = true; + } + + element.setToPrint(!((currentOverflowWithElements || willOverflowWithWhiteSpace) && currentOverflowAllowed));// don't use willOverflow() method as it is overridden at least in bands + } + + if (element.isToPrint()) + { + element.setRelativeY( + element.getY() + stretchHeight - getActualContainerHeight() + ); + } + } + } + //} + } + + + /** + * @deprecated To be removed. + */ + protected void _removeBlankElements() + { + JRElement[] remElems = removableElements; + if (remElems != null && remElems.length > 0) + { + JRElement[] elems = ySortedElements; + + for(int i = 0; i < remElems.length; i++) + { + JRFillElement iElem = (JRFillElement)remElems[i]; + + int blankHeight; + if (iElem.isToPrint()) + { + blankHeight = iElem.getHeight() - iElem.getStretchHeight(); + } + else + { + blankHeight = iElem.getHeight();//FIXME subreports that stretch and then don't print, will not remove all space + } + + if ( + blankHeight > 0 && + iElem.getRelativeY() + iElem.getStretchHeight() <= stretchHeight && + iElem.getRelativeY() >= firstY + ) + { + int blankY = iElem.getRelativeY() + iElem.getHeight() - blankHeight; + boolean isToRemove = true; + + for(int j = 0; j < elems.length; j++) + { + JRFillElement jElem = (JRFillElement)elems[j]; + + if (iElem != jElem && jElem.isToPrint()) + { + int top = + Math.min(blankY, jElem.getRelativeY()); + int bottom = + Math.max( + blankY + blankHeight, + jElem.getRelativeY() + jElem.getStretchHeight() + ); + + if (blankHeight + jElem.getStretchHeight() > bottom - top) + { + isToRemove = false; + break; + } + } + } + + if (isToRemove) + { + for(int j = 0; j < elems.length; j++) + { + JRFillElement jElem = (JRFillElement)elems[j]; + + if (jElem.getRelativeY() >= blankY + blankHeight) + { + jElem.setRelativeY(jElem.getRelativeY() - blankHeight); + } + } + + stretchHeight = stretchHeight - blankHeight; + } + } + } + } + } + + + /** + * + */ + protected void removeBlankElements() + { + if (isLegacyElementStretchEnabled()) + { + _removeBlankElements(); + return; + } + + if (removableElements != null && removableElements.length > 0) + { + for (JRFillElement remElem : removableElements) + { + int blankHeight; + if (remElem.isToPrint()) + { + blankHeight = remElem.getHeight() - remElem.getStretchHeight(); + } + else + { + blankHeight = remElem.getHeight();//FIXME subreports that stretch and then don't print, will not remove all space + } + + if ( + blankHeight > 0 && + remElem.getRelativeY() + remElem.getStretchHeight() <= stretchHeight && + remElem.getRelativeY() >= firstY + ) + { + int blankY = remElem.getRelativeY() + remElem.getHeight() - blankHeight; + boolean isToRemove = true; + + for (JRFillElement jElem : ySortedElements) + { + if (remElem != jElem && jElem.isToPrint()) + { + int top = + Math.min(blankY, jElem.getRelativeY()); + int bottom = + Math.max( + blankY + blankHeight, + jElem.getRelativeY() + jElem.getStretchHeight() + ); + + if (blankHeight + jElem.getStretchHeight() > bottom - top) + { + isToRemove = false; + break; + } + } + } + + if (isToRemove) + { + for (JRFillElement jElem : ySortedElements) + { + if (jElem.getRelativeY() + jElem.getStretchHeight() <= blankY) + { + jElem.setCollapsedHeightBelow(jElem.getCollapsedHeightBelow() + blankHeight); + } + + if (jElem.getRelativeY() >= blankY + blankHeight) + { + jElem.setCollapsedHeightAbove(jElem.getCollapsedHeightAbove() + blankHeight); + jElem.setRelativeY(jElem.getRelativeY() - blankHeight); + } + } + + stretchHeight = stretchHeight - blankHeight; + } + } + } + } + } + + + /** + * Fills the elements from this container into a print element container. + * + * @param printContainer the print element container + * @throws JRException + */ + public void fillElements(JRPrintElementContainer printContainer) throws JRException + { + //int maxStretch = 0; + //int stretch = 0; + int maxWidth = 0; + JRElement[] allElements = getElements(); + if (allElements != null && allElements.length > 0) + { + for(int i = 0; i < allElements.length; i++) + { + JRFillElement element = (JRFillElement)allElements[i]; + + element.setRelativeY(element.getRelativeY() - firstY); + + if (element.getRelativeY() + element.getStretchHeight() > stretchHeight - firstY) + { + element.setToPrint(false); + } + + element.setAlreadyPrinted(element.isToPrint() || element.isAlreadyPrinted()); + + if (element.isToPrint()) + { + JRPrintElement printElement = element.fill(); + //printElement.setY(printElement.getY() - firstY); + + if (printElement != null) + { + //FIXME not all elements affect height + //stretch = printElement.getY() + firstY + printElement.getHeight() - element.getY() - element.getHeight(); + //if (stretch > maxStretch) + //{ + // maxStretch = stretch; + //} + printContainer.addElement(printElement); + if (printElement.getX() + printElement.getWidth() > maxWidth) + { + maxWidth = printElement.getX() + printElement.getWidth(); + } + } + + if (element instanceof JRFillSubreport) + { + JRFillSubreport subreport = (JRFillSubreport)element; + + List styles = subreport.subreportFiller.getJasperPrint().getStylesList(); + for(int j = 0; j < styles.size(); j++) + { + filler.addPrintStyle(styles.get(j)); + } + + List origins = subreport.subreportFiller.getJasperPrint().getOriginsList(); + for(int j = 0; j < origins.size(); j++) + { + filler.getJasperPrint().addOrigin(origins.get(j)); + } + + Collection printElements = subreport.getPrintElements(); + addSubElements(printContainer, element, printElements); + if (subreport.getX() + subreport.getPrintContentsWidth() > maxWidth) + { + maxWidth = subreport.getX() + subreport.getPrintContentsWidth(); + } + + subreport.subreportPageFilled(); + } + + // crosstabs do not return a fill() element + if (element instanceof JRFillCrosstab) + { + JRFillCrosstab crosstab = (JRFillCrosstab) element; + List printElements = crosstab.getPrintElements(); + addSubElements(printContainer, element, printElements); + if (crosstab.getX() + crosstab.getPrintElementsWidth() > maxWidth) + { + maxWidth = crosstab.getX() + crosstab.getPrintElementsWidth(); + } + } + } + } + } + + //printBand.setHeight(getHeight() + maxStretch - firstY); + printContainer.setHeight(stretchHeight - firstY); + printContainer.setContentsWidth(maxWidth); + } + + + protected void addSubElements(JRPrintElementContainer printContainer, JRFillElement element, + Collection printElements) + { + if (printContainer instanceof OffsetElementsContainer) + { + // adding the subelements as whole lists to bands so that we don't need + // another virtualized list at print band level + ((OffsetElementsContainer) printContainer).addOffsetElements(printElements, + element.getX(), element.getRelativeY()); + } + else + { + if (printElements != null && printElements.size() > 0) + { + for(Iterator it = printElements.iterator(); it.hasNext();) + { + JRPrintElement printElement =it.next(); + printElement.setX(element.getX() + printElement.getX()); + printElement.setY(element.getRelativeY() + printElement.getY()); + printContainer.addElement(printElement); + } + } + } + } + + + /** + * + */ + protected void rewind() throws JRException + { + if (ySortedElements != null && ySortedElements.length > 0) + { + for(int i = 0; i < ySortedElements.length; i++) + { + JRFillElement element = ySortedElements[i]; + + element.rewind(); + + element.setAlreadyPrinted(false); + } + } + + willOverflowWithElements = false; + willOverflowWithWhiteSpace = false; + } + + protected int getFirstY() + { + return firstY; + } + + + /** + * Returns the actual height of the element container. + * Some element containers such as frames have a larger calculated container height, resulting from content being placed beyond container declared height. + * + * @return the height of the element container + */ + protected abstract int getActualContainerHeight(); + + + /** + * Returns the height of the element container. + * + * @return the height of the element container + */ + protected abstract int getContainerHeight(); + + + /** + * Find all styles containing conditional styles which are referenced by elements in this band. + */ + protected void initConditionalStyles() + { + filler.addDefaultStyleListener(new JRBaseFiller.DefaultStyleListener(){ + @Override + public void defaultStyleSet(JRStyle style) + { + collectConditionalStyle(style); + } + }); + + for (int i = 0; i < deepElements.length; i++) + { + JRStyle style = deepElements[i].initStyle; + collectConditionalStyle(style); + } + + if (deepElements.length > 0) + { + for(int i = 0; i < deepElements.length; i++) + { + deepElements[i].setConditionalStylesContainer(this); + } + } + } + + protected void collectConditionalStyle(JRStyle style) + { + if (style != null)// && style.getConditionalStyles() != null) + { + stylesToEvaluate.add(style); + } + } + + + protected void evaluateConditionalStyles(byte evaluation) throws JRException + { + for (Iterator it = stylesToEvaluate.iterator(); it.hasNext();) + { + evaluateConditionalStyle(it.next(), evaluation); + } + } + + + protected JRStyle evaluateConditionalStyle(JRStyle initialStyle, byte evaluation) throws JRException + { + JRStyle consolidatedStyle = initialStyle; + + StringBuilder code = new StringBuilder(); + List condStylesToApply = new ArrayList<>(); + + boolean anyTrue = buildConsolidatedStyle(initialStyle, evaluation, code, condStylesToApply); + + if (anyTrue) + { + String consolidatedStyleName = initialStyle.getName() + "|" + code.toString(); + consolidatedStyle = filler.getJasperPrint().getStylesMap().get(consolidatedStyleName); + if (consolidatedStyle == null) + { + JRBaseStyle style = new JRBaseStyle(initialStyle.getDefaultStyleProvider(), consolidatedStyleName); + for (int j = condStylesToApply.size() - 1; j >= 0; j--) + { + StyleUtil.appendStyle(style, condStylesToApply.get(j)); + } + + // deduplicate to previously created identical instances + style = filler.fillContext.deduplicate(style); + filler.addPrintStyle(style); + + consolidatedStyle = style; + } + } + + evaluatedStyles.put(initialStyle, consolidatedStyle); + + return consolidatedStyle; + } + + + protected boolean buildConsolidatedStyle(JRStyle style, byte evaluation, StringBuilder code, List condStylesToApply) throws JRException + { + boolean anyTrue = false; + + JRConditionalStyle[] conditionalStyles = style.getConditionalStyles(); + if (conditionalStyles != null && conditionalStyles.length > 0) + { + for (int j = 0; j < conditionalStyles.length; j++) + { + JRConditionalStyle conditionalStyle = conditionalStyles[j]; + Boolean expressionValue = + (Boolean) expressionEvaluator.evaluate( + conditionalStyle.getConditionExpression(), + evaluation + ); + + boolean condition; + if (expressionValue == null) + { + condition = false; + } + else + { + condition = expressionValue; + } + + code.append(condition ? '1' : '0'); + anyTrue = anyTrue | condition; + + if (condition) + { + condStylesToApply.add(conditionalStyle); + } + } + } + + condStylesToApply.add(style); + + if (style.getStyle() != null) + { + anyTrue = anyTrue | buildConsolidatedStyle(style.getStyle(), evaluation, code, condStylesToApply); + } + return anyTrue; + } + + + public JRStyle getEvaluatedConditionalStyle(JRStyle parentStyle) + { + return evaluatedStyles.get(parentStyle); + } + + protected final void setElementOriginProvider(JROriginProvider originProvider) + { + if (originProvider != null) + { + for (int i = 0; i < deepElements.length; i++) + { + deepElements[i].setOriginProvider(originProvider); + } + } + } }