package net.sf.jasperreports.engine.fill; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import net.sf.jasperreports.engine.JRConditionalStyle; import net.sf.jasperreports.engine.JRElement; import net.sf.jasperreports.engine.JRElementGroup; import net.sf.jasperreports.engine.JRException; import net.sf.jasperreports.engine.JRFrame; import net.sf.jasperreports.engine.JROrigin; import net.sf.jasperreports.engine.JRPrintElement; import net.sf.jasperreports.engine.JRPrintElementContainer; import net.sf.jasperreports.engine.JRReportFont; import net.sf.jasperreports.engine.JRStyle; import net.sf.jasperreports.engine.base.JRBaseStyle; import net.sf.jasperreports.engine.util.JRStyleResolver; public abstract class JRFillElementContainer extends JRFillElementGroup { protected JRBaseFiller filler; private JRFillElement[] ySortedElements = null; private JRFillElement[] stretchElements = null; private JRFillElement[] bandBottomElements = null; private JRFillElement[] removableElements = null; private boolean willOverflow = false; protected boolean isOverflow = false; private int stretchHeight = 0; private int firstY = 0; protected JRFillElement firstYElement = null; protected final JRFillExpressionEvaluator expressionEvaluator; protected JRFillElement[] deepElements; protected Set stylesToEvaluate = new HashSet(); protected Map evaluatedStyles = new HashMap(); protected boolean hasPrintWhenOverflowElement; protected JRFillElementContainer(JRBaseFiller filler, JRElementGroup container, JRFillObjectFactory factory) { super(container, factory); this.expressionEvaluator = factory.getExpressionEvaluator(); initDeepElements(); this.filler = filler; } protected JRFillElementContainer(JRFillElementContainer container, JRFillCloneFactory factory) { super(container, factory); this.expressionEvaluator = container.expressionEvaluator; initDeepElements(); this.filler = container.filler; } private void initDeepElements() { if (this.elements == null) { this.deepElements = new JRFillElement[0]; } else { List deepElementsList = new ArrayList(this.elements.length); collectDeepElements((JRElement[])this.elements, deepElementsList); this.deepElements = new JRFillElement[deepElementsList.size()]; deepElementsList.toArray((Object[])this.deepElements); } } private static void collectDeepElements(JRElement[] elements, List deepElementsList) { for (int i = 0; i < elements.length; i++) { JRElement element = elements[i]; deepElementsList.add(element); if (element instanceof JRFillFrame) { JRFrame frame = (JRFrame)element; collectDeepElements(frame.getElements(), deepElementsList); } } } protected final void initElements() { this.hasPrintWhenOverflowElement = false; if (this.elements != null && this.elements.length > 0) { List sortedElemsList = new ArrayList(); List stretchElemsList = new ArrayList(); List bandBottomElemsList = new ArrayList(); List removableElemsList = new ArrayList(); for (int i = 0; i < this.elements.length; i++) { JRFillElement element = this.elements[i]; sortedElemsList.add(element); if (element.getPositionType() == 3) bandBottomElemsList.add(element); if (element.getStretchType() != 0) stretchElemsList.add(element); if (element.isRemoveLineWhenBlank()) removableElemsList.add(element); if (element.isPrintWhenDetailOverflows()) this.hasPrintWhenOverflowElement = true; } Collections.sort(sortedElemsList, new JRYComparator()); this.ySortedElements = new JRFillElement[this.elements.length]; sortedElemsList.toArray(this.ySortedElements); this.stretchElements = new JRFillElement[stretchElemsList.size()]; stretchElemsList.toArray(this.stretchElements); this.bandBottomElements = new JRFillElement[bandBottomElemsList.size()]; bandBottomElemsList.toArray(this.bandBottomElements); this.removableElements = new JRFillElement[removableElemsList.size()]; removableElemsList.toArray(this.removableElements); } setDependentElements(); setElementsBandBottomY(); } protected final void setElementsBandBottomY() { if (this.elements != null && this.elements.length > 0) for (int i = 0; i < this.elements.length; i++) this.elements[i].setBandBottomY(getContainerHeight() - this.elements[i].getY() - this.elements[i].getHeight()); } private void setDependentElements() { if (this.ySortedElements != null && this.ySortedElements.length > 0) for (int i = 0; i < this.ySortedElements.length - 1; i++) { JRFillElement iElem = this.ySortedElements[i]; boolean isBreakElem = iElem instanceof JRFillBreak; for (int j = i + 1; j < this.ySortedElements.length; j++) { JRFillElement jElem = this.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.getPositionType() == 2) || jElem.getPositionType() == 1) && iElem.getY() + iElem.getHeight() <= jElem.getY() && iElem.getWidth() + jElem.getWidth() > right - left) iElem.addDependantElement(jElem); } } } protected void evaluate(byte evaluation) throws JRException { 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 (this.ySortedElements != null && this.ySortedElements.length > 0) for (int i = 0; i < this.ySortedElements.length; i++) { JRFillElement element = this.ySortedElements[i]; element.reset(); if (!this.isOverflow) element.setAlreadyPrinted(false); } } protected boolean willOverflow() { return this.willOverflow; } protected void initFill() { this.isOverflow = this.willOverflow; this.firstY = 0; this.firstYElement = null; } protected void prepareElements(int availableStretchHeight, boolean isOverflowAllowed) throws JRException { boolean tmpWillOverflow = false; int maxBandStretch = 0; int bandStretch = 0; this.firstY = this.isOverflow ? getContainerHeight() : 0; this.firstYElement = null; boolean isFirstYFound = false; if (this.ySortedElements != null && this.ySortedElements.length > 0) for (int i = 0; i < this.ySortedElements.length; i++) { JRFillElement element = this.ySortedElements[i]; tmpWillOverflow = (element.prepare(availableStretchHeight + getElementFirstY(element), this.isOverflow) || tmpWillOverflow); element.moveDependantElements(); if (element.isToPrint()) { if (this.isOverflow) { if (element.isReprinted()) { this.firstY = 0; } else if (!isFirstYFound) { this.firstY = element.getY(); } isFirstYFound = true; } this.firstYElement = element; bandStretch = element.getRelativeY() + element.getStretchHeight() - getContainerHeight() + element.getBandBottomY(); if (bandStretch > maxBandStretch) maxBandStretch = bandStretch; } } if (maxBandStretch > availableStretchHeight + this.firstY) tmpWillOverflow = true; if (tmpWillOverflow) { this.stretchHeight = getContainerHeight() + availableStretchHeight; } else { this.stretchHeight = getContainerHeight() + maxBandStretch; } this.willOverflow = (tmpWillOverflow && isOverflowAllowed); } private int getElementFirstY(JRFillElement element) { int elemFirstY; if (!this.isOverflow || this.hasPrintWhenOverflowElement) { elemFirstY = 0; } else if (element.getY() >= this.firstY) { elemFirstY = this.firstY; } else { elemFirstY = element.getY(); } return elemFirstY; } protected void setStretchHeight(int stretchHeight) { if (stretchHeight > this.stretchHeight) this.stretchHeight = stretchHeight; } protected void stretchElements() { if (this.stretchElements != null && this.stretchElements.length > 0) for (int i = 0; i < this.stretchElements.length; i++) { JRFillElement element = this.stretchElements[i]; element.stretchElement(this.stretchHeight - getContainerHeight()); element.moveDependantElements(); } if (this.ySortedElements != null && this.ySortedElements.length > 0) for (int i = 0; i < this.ySortedElements.length; i++) { JRFillElement element = this.ySortedElements[i]; element.stretchHeightFinal(); } } protected int getStretchHeight() { return this.stretchHeight; } protected void moveBandBottomElements() { if (this.bandBottomElements != null && this.bandBottomElements.length > 0) for (int i = 0; i < this.bandBottomElements.length; i++) { JRFillElement element = this.bandBottomElements[i]; element.setRelativeY(element.getY() + this.stretchHeight - getContainerHeight()); element.setToPrint((element.isToPrint() && !this.willOverflow)); } } protected void removeBlankElements() { JRFillElement[] arrayOfJRFillElement = this.removableElements; if (arrayOfJRFillElement != null && arrayOfJRFillElement.length > 0) { JRFillElement[] arrayOfJRFillElement1 = this.ySortedElements; for (int i = 0; i < arrayOfJRFillElement.length; i++) { int blankHeight; JRFillElement iElem = arrayOfJRFillElement[i]; if (iElem.isToPrint()) { blankHeight = iElem.getHeight() - iElem.getStretchHeight(); } else { blankHeight = iElem.getHeight(); } if (blankHeight > 0 && iElem.getRelativeY() + iElem.getStretchHeight() <= this.stretchHeight && iElem.getRelativeY() >= this.firstY) { int blankY = iElem.getRelativeY() + iElem.getHeight() - blankHeight; boolean isToRemove = true; int j; for (j = 0; j < arrayOfJRFillElement1.length; j++) { JRFillElement jElem = arrayOfJRFillElement1[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 (j = 0; j < arrayOfJRFillElement1.length; j++) { JRFillElement jElem = arrayOfJRFillElement1[j]; if (jElem.getRelativeY() >= blankY + blankHeight) jElem.setRelativeY(jElem.getRelativeY() - blankHeight); } this.stretchHeight -= blankHeight; } } } } } protected void fillElements(JRPrintElementContainer printContainer) throws JRException { 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() - this.firstY); if (element.getRelativeY() + element.getStretchHeight() > this.stretchHeight) element.setToPrint(false); element.setAlreadyPrinted((element.isToPrint() || element.isAlreadyPrinted())); if (element.isToPrint()) { JRPrintElement printElement = element.fill(); if (printElement != null) { printContainer.addElement(printElement); if (element instanceof JRFillSubreport) { JRFillSubreport subreport = (JRFillSubreport)element; List fonts = subreport.subreportFiller.getJasperPrint().getFontsList(); for (int j = 0; j < fonts.size(); j++) this.filler.getJasperPrint().addFont(fonts.get(j), true); List styles = subreport.subreportFiller.getJasperPrint().getStylesList(); for (int k = 0; k < styles.size(); k++) this.filler.addPrintStyle(styles.get(k)); List origins = subreport.subreportFiller.getJasperPrint().getOriginsList(); for (int m = 0; m < origins.size(); m++) this.filler.getJasperPrint().addOrigin(origins.get(m)); Collection printElements = subreport.getPrintElements(); addSubElements(printContainer, element, printElements); } else if (element instanceof JRFillCrosstab) { List printElements = ((JRFillCrosstab)element).getPrintElements(); addSubElements(printContainer, element, printElements); } } } } printContainer.setHeight(this.stretchHeight - this.firstY); } protected void addSubElements(JRPrintElementContainer printContainer, JRFillElement element, Collection printElements) { 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 (this.ySortedElements != null && this.ySortedElements.length > 0) for (int i = 0; i < this.ySortedElements.length; i++) { JRFillElement element = this.ySortedElements[i]; element.rewind(); element.setAlreadyPrinted(false); } this.willOverflow = false; } protected int getFirstY() { return this.firstY; } protected abstract int getContainerHeight(); protected void initConditionalStyles() { this.filler.addDefaultStyleListener(new JRBaseFiller.DefaultStyleListener() { private final JRFillElementContainer this$0; public void defaultStyleSet(JRStyle style) { JRFillElementContainer.this.collectConditionalStyle(style); } }); int i; for (i = 0; i < this.deepElements.length; i++) { JRStyle style = (this.deepElements[i]).initStyle; collectConditionalStyle(style); } if (this.deepElements.length > 0) for (i = 0; i < this.deepElements.length; i++) this.deepElements[i].setConditionalStylesContainer(this); } protected void collectConditionalStyle(JRStyle style) { if (style != null) this.stylesToEvaluate.add(style); } protected void evaluateConditionalStyles(byte evaluation) throws JRException { for (Iterator it = this.stylesToEvaluate.iterator(); it.hasNext();) evaluateConditionalStyle(it.next(), evaluation); } protected JRStyle evaluateConditionalStyle(JRStyle initialStyle, byte evaluation) throws JRException { JRBaseStyle jRBaseStyle; JRStyle consolidatedStyle = initialStyle; StringBuffer code = new StringBuffer(); List condStylesToApply = new ArrayList(); boolean anyTrue = buildConsolidatedStyle(initialStyle, evaluation, code, condStylesToApply); if (anyTrue) { String consolidatedStyleName = initialStyle.getName() + code.toString(); consolidatedStyle = (JRStyle)this.filler.getJasperPrint().getStylesMap().get(consolidatedStyleName); if (consolidatedStyle == null) { jRBaseStyle = new JRBaseStyle(consolidatedStyleName); for (int j = condStylesToApply.size() - 1; j >= 0; j--) JRStyleResolver.appendStyle((JRStyle)jRBaseStyle, condStylesToApply.get(j)); this.filler.addPrintStyle((JRStyle)jRBaseStyle); } } this.evaluatedStyles.put(initialStyle, jRBaseStyle); return (JRStyle)jRBaseStyle; } protected boolean buildConsolidatedStyle(JRStyle style, byte evaluation, StringBuffer 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++) { boolean condition; JRConditionalStyle conditionalStyle = conditionalStyles[j]; Boolean expressionValue = (Boolean)this.expressionEvaluator.evaluate(conditionalStyle.getConditionExpression(), evaluation); if (expressionValue == null) { condition = false; } else { condition = expressionValue.booleanValue(); } code.append(condition ? 49 : 48); anyTrue |= condition; if (condition) condStylesToApply.add(conditionalStyle); } condStylesToApply.add(style); if (style.getStyle() != null) anyTrue |= buildConsolidatedStyle(style.getStyle(), evaluation, code, condStylesToApply); return anyTrue; } public JRStyle getEvaluatedConditionalStyle(JRStyle parentStyle) { return (JRStyle)this.evaluatedStyles.get(parentStyle); } }