/* * JasperReports - Free Java Reporting Library. * Copyright (C) 2001 - 2009 Jaspersoft Corporation. All rights reserved. * http://www.jaspersoft.com * * Unless you have purchased a commercial license agreement from Jaspersoft, * the following license terms apply: * * This program is part of JasperReports. * * JasperReports is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * JasperReports is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with JasperReports. If not, see <http://www.gnu.org/licenses/>. */ package net.sf.jasperreports.engine.fill; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.ResourceBundle; import net.sf.jasperreports.crosstabs.JRCellContents; import net.sf.jasperreports.crosstabs.JRCrosstab; import net.sf.jasperreports.crosstabs.JRCrosstabBucket; import net.sf.jasperreports.crosstabs.JRCrosstabCell; import net.sf.jasperreports.crosstabs.JRCrosstabColumnGroup; import net.sf.jasperreports.crosstabs.JRCrosstabDataset; import net.sf.jasperreports.crosstabs.JRCrosstabGroup; import net.sf.jasperreports.crosstabs.JRCrosstabMeasure; import net.sf.jasperreports.crosstabs.JRCrosstabParameter; import net.sf.jasperreports.crosstabs.JRCrosstabRowGroup; import net.sf.jasperreports.crosstabs.base.JRBaseCrosstab; import net.sf.jasperreports.crosstabs.design.JRDesignCrosstab; import net.sf.jasperreports.crosstabs.fill.JRCrosstabExpressionEvaluator; import net.sf.jasperreports.crosstabs.fill.JRFillCrosstabCell; import net.sf.jasperreports.crosstabs.fill.JRFillCrosstabColumnGroup; import net.sf.jasperreports.crosstabs.fill.JRFillCrosstabGroup; import net.sf.jasperreports.crosstabs.fill.JRFillCrosstabMeasure; import net.sf.jasperreports.crosstabs.fill.JRFillCrosstabObjectFactory; import net.sf.jasperreports.crosstabs.fill.JRFillCrosstabParameter; import net.sf.jasperreports.crosstabs.fill.JRFillCrosstabRowGroup; import net.sf.jasperreports.crosstabs.fill.calculation.BucketDefinition; import net.sf.jasperreports.crosstabs.fill.calculation.BucketingService; import net.sf.jasperreports.crosstabs.fill.calculation.CrosstabCell; import net.sf.jasperreports.crosstabs.fill.calculation.HeaderCell; import net.sf.jasperreports.crosstabs.fill.calculation.MeasureDefinition; import net.sf.jasperreports.crosstabs.fill.calculation.BucketDefinition.Bucket; import net.sf.jasperreports.crosstabs.fill.calculation.MeasureDefinition.MeasureValue; import net.sf.jasperreports.crosstabs.type.CrosstabColumnPositionEnum; import net.sf.jasperreports.crosstabs.type.CrosstabPercentageEnum; import net.sf.jasperreports.crosstabs.type.CrosstabRowPositionEnum; import net.sf.jasperreports.engine.JRElement; import net.sf.jasperreports.engine.JRException; import net.sf.jasperreports.engine.JRExpression; import net.sf.jasperreports.engine.JRExpressionChunk; import net.sf.jasperreports.engine.JRExpressionCollector; import net.sf.jasperreports.engine.JROrigin; import net.sf.jasperreports.engine.JRParameter; import net.sf.jasperreports.engine.JRPrintElement; import net.sf.jasperreports.engine.JRPrintFrame; import net.sf.jasperreports.engine.JRPrintRectangle; import net.sf.jasperreports.engine.JRRuntimeException; import net.sf.jasperreports.engine.JRVariable; import net.sf.jasperreports.engine.JRVisitor; import net.sf.jasperreports.engine.JasperCompileManager; import net.sf.jasperreports.engine.JasperReport; import net.sf.jasperreports.engine.design.JRDesignRectangle; import net.sf.jasperreports.engine.type.ModeEnum; import net.sf.jasperreports.engine.type.RunDirectionEnum; import net.sf.jasperreports.engine.util.JRProperties; import net.sf.jasperreports.engine.util.JRStyleResolver; import org.jfree.data.general.Dataset; /** * Fill-time implementation of a {@link net.sf.jasperreports.crosstabs.JRCrosstab crosstab}. * * @author Lucian Chirita (lucianc@users.sourceforge.net) * @version $Id: JRFillCrosstab.java 3657 2010-03-31 09:34:49Z teodord $ */ public class JRFillCrosstab extends JRFillElement implements JRCrosstab, JROriginProvider { final protected JRCrosstab parentCrosstab; protected JRFillCrosstabDataset dataset; protected JRFillCrosstabRowGroup[] rowGroups; protected Map rowGroupsMap; protected JRFillCrosstabColumnGroup[] columnGroups; protected Map columnGroupsMap; protected JRFillCrosstabMeasure[] measures; protected BucketingService bucketingService; protected JRFillVariable[] variables; protected Map variablesMap; protected JRFillVariable[][][] totalVariables; protected boolean[][] retrieveTotal; protected JRFillCrosstabParameter[] parameters; protected Map parametersMap; protected boolean ignoreWidth; protected JRCrosstabExpressionEvaluator crosstabEvaluator; protected JRFillCrosstabCell[][] crossCells; protected JRFillCellContents headerCell; protected JRFillCellContents whenNoDataCell; protected boolean hasData; protected HeaderCell[][] columnHeadersData; protected HeaderCell[][] rowHeadersData; protected CrosstabCell[][] cellData; protected MeasureValue[] grandTotals; private boolean percentage; private CrosstabFiller crosstabFiller; private int overflowStartPage; private List fillElements; public JRFillCrosstab(JRBaseFiller filler, JRCrosstab crosstab, JRFillObjectFactory factory) { super(filler, crosstab, factory); parentCrosstab = crosstab; loadEvaluator(filler.getJasperReport()); JRFillCrosstabObjectFactory crosstabFactory = new JRFillCrosstabObjectFactory( factory, crosstabEvaluator); crosstabFactory.setParentOriginProvider(this); headerCell = crosstabFactory.getCell(crosstab.getHeaderCell(), JRCellContents.TYPE_CROSSTAB_HEADER); copyRowGroups(crosstab, crosstabFactory); copyColumnGroups(crosstab, crosstabFactory); copyMeasures(crosstab, crosstabFactory); copyCells(crosstab, crosstabFactory); whenNoDataCell = crosstabFactory.getCell(crosstab.getWhenNoDataCell(), JRCellContents.TYPE_NO_DATA_CELL); dataset = factory.getCrosstabDataset(crosstab.getDataset(), this); copyParameters(crosstab, factory); copyVariables(crosstab, crosstabFactory); // default value from property this.ignoreWidth = JRProperties.getBooleanProperty(filler.jasperReport, PROPERTY_IGNORE_WIDTH, false); Boolean crosstabIgnoreWidth = crosstab.getIgnoreWidth(); // crosstab attribute overrides property if (crosstabIgnoreWidth != null) { this.ignoreWidth = crosstabIgnoreWidth.booleanValue(); } crosstabFiller = new CrosstabFiller(); } /** * */ public ModeEnum getModeValue() { return JRStyleResolver.getMode(this, ModeEnum.TRANSPARENT); } private void copyRowGroups(JRCrosstab crosstab, JRFillCrosstabObjectFactory factory) { JRCrosstabRowGroup[] groups = crosstab.getRowGroups(); rowGroups = new JRFillCrosstabRowGroup[groups.length]; rowGroupsMap = new HashMap(); for (int i = 0; i < groups.length; ++i) { JRFillCrosstabRowGroup group = factory.getCrosstabRowGroup(groups[i]); group.getFillHeader().setVerticalPositionType(groups[i].getPositionValue()); rowGroups[i] = group; rowGroupsMap.put(group.getName(), Integer.valueOf(i)); } } private void copyColumnGroups(JRCrosstab crosstab, JRFillCrosstabObjectFactory factory) { JRCrosstabColumnGroup[] groups = crosstab.getColumnGroups(); columnGroups = new JRFillCrosstabColumnGroup[groups.length]; columnGroupsMap = new HashMap(); for (int i = 0; i < groups.length; ++i) { JRFillCrosstabColumnGroup group = factory.getCrosstabColumnGroup(groups[i]); columnGroups[i] = group; columnGroupsMap.put(group.getName(), Integer.valueOf(i)); } } private void copyMeasures(JRCrosstab crosstab, JRFillCrosstabObjectFactory factory) { JRCrosstabMeasure[] crossMeasures = crosstab.getMeasures(); measures = new JRFillCrosstabMeasure[crossMeasures.length]; for (int i = 0; i < crossMeasures.length; i++) { measures[i] = factory.getCrosstabMeasure(crossMeasures[i]); } } private void copyParameters(JRCrosstab crosstab, JRFillObjectFactory factory) { JRCrosstabParameter[] crossParams = crosstab.getParameters(); parameters = new JRFillCrosstabParameter[crossParams.length]; parametersMap = new HashMap(); for (int i = 0; i < crossParams.length; i++) { parameters[i] = factory.getCrosstabParameter(crossParams[i]); parametersMap.put(parameters[i].getName(), parameters[i]); } } private void copyCells(JRCrosstab crosstab, JRFillCrosstabObjectFactory factory) { JRCrosstabCell[][] crosstabCells = crosstab.getCells(); crossCells = new JRFillCrosstabCell[rowGroups.length + 1][columnGroups.length + 1]; for (int i = 0; i <= rowGroups.length; ++i) { for (int j = 0; j <= columnGroups.length; ++j) { if (crosstabCells[i][j] != null) { crossCells[i][j] = factory.getCrosstabCell(crosstabCells[i][j]); } } } } private void copyVariables(JRCrosstab crosstab, JRFillObjectFactory factory) { JRVariable[] vars = crosstab.getVariables(); variables = new JRFillVariable[vars.length]; variablesMap = new HashMap(); for (int i = 0; i < variables.length; i++) { variables[i] = factory.getVariable(vars[i]); variablesMap.put(variables[i].getName(), variables[i]); } Map totalVarPos = new HashMap(); totalVariables = new JRFillVariable[rowGroups.length + 1][columnGroups.length + 1][measures.length]; for (int row = 0; row <= rowGroups.length; ++row) { JRCrosstabRowGroup rowGroup = row == rowGroups.length ? null : rowGroups[row]; for (int col = 0; col <= columnGroups.length; ++col) { JRCrosstabColumnGroup colGroup = col == columnGroups.length ? null : columnGroups[col]; if (row < rowGroups.length || col < columnGroups.length) { for (int m = 0; m < measures.length; m++) { String totalVariableName = JRDesignCrosstab.getTotalVariableName(measures[m], rowGroup, colGroup); totalVariables[row][col][m] = (JRFillVariable) variablesMap.get(totalVariableName); totalVarPos.put(totalVariableName, new int[]{row, col}); } } } } retrieveTotal = new boolean[rowGroups.length + 1][columnGroups.length + 1]; //FIXME avoid this List expressions = JRExpressionCollector.collectExpressions(filler.getJasperReport(), crosstab); for (Iterator iter = expressions.iterator(); iter.hasNext();) { JRExpression expression = (JRExpression) iter.next(); JRExpressionChunk[] chunks = expression.getChunks(); if (chunks != null) { for (int i = 0; i < chunks.length; i++) { JRExpressionChunk chunk = chunks[i]; if (chunk.getType() == JRExpressionChunk.TYPE_VARIABLE) { String varName = chunk.getText(); int[] pos = (int[]) totalVarPos.get(varName); if (pos != null) { retrieveTotal[pos[0]][pos[1]] = true; } } } } } } protected void loadEvaluator(JasperReport jasperReport) { try { JREvaluator evaluator = JasperCompileManager.loadEvaluator(jasperReport, parentCrosstab); crosstabEvaluator = new JRCrosstabExpressionEvaluator(evaluator); } catch (JRException e) { throw new JRRuntimeException("Could not load evaluator for crosstab.", e); } } private BucketingService createService(byte evaluation) throws JRException { boolean hasOrderByExpression = false; List rowBuckets = new ArrayList(rowGroups.length); for (int i = 0; i < rowGroups.length; ++i) { JRFillCrosstabRowGroup group = rowGroups[i]; rowBuckets.add(createServiceBucket(group, evaluation)); hasOrderByExpression |= group.getBucket().getOrderByExpression() != null; } List colBuckets = new ArrayList(columnGroups.length); for (int i = 0; i < columnGroups.length; ++i) { JRFillCrosstabColumnGroup group = columnGroups[i]; colBuckets.add(createServiceBucket(group, evaluation)); hasOrderByExpression |= group.getBucket().getOrderByExpression() != null; } percentage = false; List measureList = new ArrayList(measures.length); for (int i = 0; i < measures.length; ++i) { measureList.add(createServiceMeasure(measures[i])); percentage |= measures[i].getPercentageType() == CrosstabPercentageEnum.GRAND_TOTAL; } // if a group has order by expression, compute totals as they might be used // in the expression // TODO refine this if (percentage || hasOrderByExpression) { ((BucketDefinition) rowBuckets.get(0)).setComputeTotal(); ((BucketDefinition) colBuckets.get(0)).setComputeTotal(); } return new BucketingService(this, rowBuckets, colBuckets, measureList, dataset.isDataPreSorted(), retrieveTotal); } private BucketDefinition createServiceBucket(JRCrosstabGroup group, byte evaluation) throws JRException { JRCrosstabBucket bucket = group.getBucket(); Comparator comparator = null; JRExpression comparatorExpression = bucket.getComparatorExpression(); if (comparatorExpression != null) { comparator = (Comparator) evaluateExpression(comparatorExpression, evaluation); } return new BucketDefinition(bucket.getExpression().getValueClass(), bucket.getOrderByExpression(), comparator, bucket.getOrderValue(), group.getTotalPositionValue()); } private MeasureDefinition createServiceMeasure(JRFillCrosstabMeasure measure) { return new MeasureDefinition( measure.getValueClass(), measure.getCalculationValue(), measure.getIncrementerFactory()); } public Object evaluateExpression(JRExpression expression, MeasureValue[] measureValues) throws JRException { for (int i = 0; i < measures.length; i++) { Object value = measureValues[i].getValue(); measures[i].getFillVariable().setValue(value); } return crosstabEvaluator.evaluate(expression, JRExpression.EVALUATION_DEFAULT); } protected void reset() { super.reset(); for (int i = 0; i < variables.length; i++) { variables[i].setValue(null); variables[i].setInitialized(true); } fillElements = null; } protected void evaluate(byte evaluation) throws JRException { reset(); evaluatePrintWhenExpression(evaluation); evaluateProperties(evaluation); if (isPrintWhenExpressionNull() || isPrintWhenTrue()) { dataset.evaluateDatasetRun(evaluation); initEvaluator(evaluation); bucketingService.processData(); hasData = bucketingService.hasData(); if (hasData) { columnHeadersData = bucketingService.getColumnHeaders(); rowHeadersData = bucketingService.getRowHeaders(); cellData = bucketingService.getCrosstabCells(); if (percentage) { grandTotals = bucketingService.getGrandTotals(); } crosstabFiller.initCrosstab(); } overflowStartPage = 0; } } protected void initEvaluator(byte evaluation) throws JRException { Map parameterValues = JRFillSubreport.getParameterValues( filler, getParametersMapExpression(), getParameters(), evaluation, true, false,//hasResourceBundle false//hasFormatFactory ); ResourceBundle resBdl = (ResourceBundle) parameterValues.get(JRParameter.REPORT_RESOURCE_BUNDLE); if (resBdl == null) { JRFillParameter resourceBundleParam = (JRFillParameter) filler.getParametersMap().get(JRParameter.REPORT_RESOURCE_BUNDLE); parameterValues.put(JRParameter.REPORT_RESOURCE_BUNDLE, resourceBundleParam.getValue()); } parameterValues.put(JRParameter.REPORT_PARAMETERS_MAP, parameterValues); for (int i = 0; i < parameters.length; i++) { Object value = parameterValues.get(parameters[i].getName()); parameters[i].setValue(value); } crosstabEvaluator.init(parametersMap, variablesMap, filler.getWhenResourceMissingType()); } protected void initBucketingService() { if (bucketingService == null) { try { bucketingService = createService(JRExpression.EVALUATION_DEFAULT); } catch (JRException e) { throw new JRRuntimeException("Could not create bucketing service", e); } } else { bucketingService.clear(); } } protected boolean prepare(int availableHeight, boolean isOverflow) throws JRException { super.prepare(availableHeight, isOverflow); if (!isToPrint()) { return false; } if (availableHeight < getRelativeY() + getHeight()) { setToPrint(false); return true; } if (isOverflow && crosstabFiller.ended() && isAlreadyPrinted()) { if (isPrintWhenDetailOverflows()) { rewind(); setReprinted(true); } else { setStretchHeight(getHeight()); setToPrint(false); return false; } } if (isOverflow && isPrintWhenDetailOverflows()) { setReprinted(true); } crosstabFiller.fill(availableHeight - getRelativeY()); if (crosstabFiller.hasFilledRows()) { // crosstab content has been filled, rest overflowPage overflowStartPage = 0; } else { int pageCount = filler.getCurrentPageCount(); if (overflowStartPage == 0) { // first empty page overflowStartPage = pageCount; } else if (pageCount >= overflowStartPage + 2) { throw new JRRuntimeException("Crosstab has not printed anything on 3 consecutive pages, " + "likely infinite loop"); } } boolean willOverflow = crosstabFiller.willOverflow(); setStretchHeight(willOverflow ? availableHeight - getRelativeY() : crosstabFiller.getUsedHeight()); return willOverflow; } protected JRPrintElement fill() { JRPrintRectangle printRectangle = null; printRectangle = new JRTemplatePrintRectangle(getJRTemplateRectangle()); printRectangle.setX(getX()); printRectangle.setY(getRelativeY()); printRectangle.setWidth(getWidth()); printRectangle.setHeight(getStretchHeight()); fillElements = crosstabFiller.getPrintElements(); if (ignoreWidth && !fillElements.isEmpty()) { int xLimit = getXLimit(fillElements); // if the elements exceed the design crosstab width if (xLimit > getWidth()) { // increase the rectangle width printRectangle.setWidth(xLimit); if (getRunDirectionValue() == RunDirectionEnum.RTL) { // if RTL filling, move the rectangle to the left printRectangle.setX(getX() + getWidth() - xLimit); } } } if (getRunDirectionValue() == RunDirectionEnum.RTL) { mirrorPrintElements(fillElements); } return printRectangle; } protected int getXLimit(List printElements) { int limit = Integer.MIN_VALUE; for (Iterator it = printElements.iterator(); it.hasNext();) { JRPrintElement element = (JRPrintElement) it.next(); if (element.getX() + element.getWidth() > limit) { limit = element.getX() + element.getWidth(); } } return limit; } protected JRTemplateRectangle getJRTemplateRectangle() { return (JRTemplateRectangle) getElementTemplate(); } protected JRTemplateElement createElementTemplate() { JRDesignRectangle rectangle = new JRDesignRectangle(); rectangle.setKey(getKey()); rectangle.setPositionType(getPositionTypeValue()); // rectangle.setPrintRepeatedValues(isPrintRepeatedValues()); rectangle.setMode(getModeValue()); rectangle.setX(getX()); rectangle.setY(getY()); rectangle.setWidth(getWidth()); rectangle.setHeight(getHeight()); rectangle.setRemoveLineWhenBlank(isRemoveLineWhenBlank()); rectangle.setPrintInFirstWholeBand(isPrintInFirstWholeBand()); rectangle.setPrintWhenDetailOverflows(isPrintWhenDetailOverflows()); rectangle.setPrintWhenGroupChanges(getPrintWhenGroupChanges()); rectangle.setForecolor(getForecolor()); rectangle.setBackcolor(getBackcolor()); rectangle.getLinePen().setLineWidth(0f); return new JRTemplateRectangle(getElementOrigin(), filler.getJasperPrint().getDefaultStyleProvider(), rectangle); } protected void rewind() { crosstabFiller.initCrosstab(); overflowStartPage = 0; } protected List getPrintElements() { return fillElements; } protected void mirrorPrintElements(List printElements) { for (Iterator it = printElements.iterator(); it.hasNext();) { JRPrintElement element = (JRPrintElement) it.next(); int mirrorX = getWidth() - element.getX() - element.getWidth(); element.setX(mirrorX); } } protected void resolveElement(JRPrintElement element, byte evaluation) { // nothing } public void collectExpressions(JRExpressionCollector collector) { collector.collect(this); } /** * */ public void visit(JRVisitor visitor) { visitor.visitCrosstab(this); } public int getId() { return parentCrosstab.getId(); } public JRCrosstabDataset getDataset() { return dataset; } public JRCrosstabRowGroup[] getRowGroups() { return rowGroups; } public JRCrosstabColumnGroup[] getColumnGroups() { return columnGroups; } public JRCrosstabMeasure[] getMeasures() { return measures; } /** * Fill-time crosstab input dataset implementation. * * @author Lucian Chirita (lucianc@users.sourceforge.net) */ public class JRFillCrosstabDataset extends JRFillElementDataset implements JRCrosstabDataset { private Object[] bucketValues; private Object[] measureValues; public JRFillCrosstabDataset(JRCrosstabDataset dataset, JRFillObjectFactory factory) { super(dataset, factory); this.bucketValues = new Object[rowGroups.length + columnGroups.length]; this.measureValues = new Object[measures.length]; } protected void customInitialize() { initBucketingService(); } protected void customEvaluate(JRCalculator calculator) throws JRExpressionEvalException { for (int i = 0; i < rowGroups.length; i++) { bucketValues[i] = calculator.evaluate(rowGroups[i].getBucket().getExpression()); } for (int i = 0; i < columnGroups.length; ++i) { bucketValues[i + rowGroups.length] = calculator.evaluate(columnGroups[i].getBucket().getExpression()); } for (int i = 0; i < measures.length; i++) { measureValues[i] = calculator.evaluate(measures[i].getValueExpression()); } } protected void customIncrement() { try { bucketingService.addData(bucketValues, measureValues); } catch (JRException e) { throw new JRRuntimeException("Error incrementing crosstab dataset", e); } } protected Dataset getCustomDataset() { return null; } public void collectExpressions(JRExpressionCollector collector) { } public boolean isDataPreSorted() { return ((JRCrosstabDataset) parent).isDataPreSorted(); } } /** * Crosstab filler class. * * @author Lucian Chirita (lucianc@users.sourceforge.net) */ protected class CrosstabFiller { private int yOffset; private boolean willOverflow; private int[] rowHeadersXOffsets; private boolean[] columnBreakable; private boolean[] rowBreakable; private int[] columnCount; private int[] rowCount; private int[] columnXOffsets; private boolean noDataCellPrinted; private int startRowIndex; private int startColumnIndex; private int lastColumnIndex; private List columnHeaders; private List printRows; private HeaderCell[] spanHeaders; private int[] spanHeadersStart; private List rowYs = new ArrayList(); private int rowIdx; private List preparedRow = new ArrayList(); private int preparedRowHeight; private boolean printRowHeaders; private boolean printColumnHeaders; private JRFillVariable rowCountVar; private JRFillVariable colCountVar; protected CrosstabFiller() { setRowHeadersXOffsets(); printRows = new ArrayList(); rowCountVar = (JRFillVariable) variablesMap.get(JRCrosstab.VARIABLE_ROW_COUNT); colCountVar = (JRFillVariable) variablesMap.get(JRCrosstab.VARIABLE_COLUMN_COUNT); } protected void initCrosstab() { columnXOffsets = computeOffsets(columnHeadersData, columnGroups, true); columnBreakable = computeBreakableHeaders(columnHeadersData, columnGroups, columnXOffsets, true, true); columnCount = computeCounts(columnHeadersData); int[] rowYOffsets = computeOffsets(rowHeadersData, rowGroups, false); rowBreakable = computeBreakableHeaders(rowHeadersData, rowGroups, rowYOffsets, false, false); rowCount = computeCounts(rowHeadersData); spanHeaders = new HeaderCell[rowGroups.length - 1]; spanHeadersStart = new int[rowGroups.length - 1]; startRowIndex = 0; startColumnIndex = 0; lastColumnIndex = 0; noDataCellPrinted = false; } protected void setRowHeadersXOffsets() { rowHeadersXOffsets = new int[rowGroups.length + 1]; rowHeadersXOffsets[0] = 0; for (int i = 0; i < rowGroups.length; i++) { rowHeadersXOffsets[i + 1] = rowHeadersXOffsets[i] + rowGroups[i].getWidth(); } } protected int[] computeOffsets(HeaderCell[][] headersData, JRFillCrosstabGroup[] groups, boolean width) { int[] offsets = new int[headersData[0].length + 1]; offsets[0] = 0; for (int i = 0; i < headersData[0].length; i++) { int size = 0; for (int j = groups.length - 1; j >= 0; --j) { if (headersData[j][i] != null) { JRFillCellContents cell = headersData[j][i].isTotal() ? groups[j].getFillTotalHeader() : groups[j].getFillHeader(); size = cell == null ? 0 : (width ? cell.getWidth() : cell.getHeight()); break; } } offsets[i + 1] = offsets[i] + size; } return offsets; } protected boolean[] computeBreakableHeaders(HeaderCell[][] headersData, JRFillCrosstabGroup[] groups, int[] offsets, boolean width, boolean startHeaders) { boolean[] breakable = new boolean[headersData[0].length]; for (int i = 0; i < breakable.length; i++) { breakable[i] = true; } for (int j = 0; j < groups.length; ++j) { JRFillCellContents fillHeader = groups[j].getFillHeader(); if (fillHeader != null) { int size = width ? fillHeader.getWidth() : fillHeader.getHeight(); for (int i = 0; i < headersData[0].length; i++) { HeaderCell header = headersData[j][i]; if (header != null && !header.isTotal() && header.getLevelSpan() > 1) { int span = header.getLevelSpan(); if (startHeaders) { for (int k = i + 1; k < i + span && offsets[k] - offsets[i] < size; ++k) { breakable[k] = false; } } for (int k = i + span - 1; k > i && offsets[i + span] - offsets[k] < size; --k) { breakable[k] = false; } } } } } return breakable; } private int[] computeCounts(HeaderCell[][] headersData) { int[] counts = new int[headersData[0].length]; HeaderCell[] lastHeaders = headersData[headersData.length - 1]; for (int i = 0, c = 0; i < counts.length; ++i) { HeaderCell lastHeader = lastHeaders[i]; if (lastHeader != null && !lastHeader.isTotal()) { ++c; } counts[i] = c; } return counts; } protected void fill(int availableHeight) throws JRException { printRows.clear(); yOffset = 0; willOverflow = false; fillVerticalCrosstab(availableHeight); } protected boolean hasFilledRows() { return !printRows.isEmpty(); } protected boolean willOverflow() { return willOverflow; } protected int getUsedHeight() { return yOffset; } protected boolean ended() { return hasData ? (startRowIndex >= rowHeadersData[0].length && startColumnIndex >= columnHeadersData[0].length) : noDataCellPrinted; } protected void fillVerticalCrosstab(int availableHeight) throws JRException { if (!hasData) { fillNoDataCell(availableHeight); return; } printRowHeaders = startColumnIndex == 0 || isRepeatRowHeaders(); int rowHeadersXOffset = printRowHeaders ? rowHeadersXOffsets[rowGroups.length] : 0; if (startColumnIndex == lastColumnIndex) { int availableWidth = getWidth(); columnHeaders = getGroupHeaders(availableWidth - rowHeadersXOffset, columnXOffsets, columnBreakable, startColumnIndex, columnHeadersData, columnGroups); lastColumnIndex = startColumnIndex + columnHeaders.size(); if (startColumnIndex == lastColumnIndex) { throw new JRRuntimeException("Not enough space to render the crosstab."); } } printColumnHeaders = startRowIndex == 0 || isRepeatColumnHeaders(); List columnHeaderRows = null; if (printColumnHeaders) { columnHeaderRows = fillColumnHeaders(rowHeadersXOffset, availableHeight - yOffset); if (willOverflow) { return; } } int lastRowIndex = fillRows(rowHeadersXOffset, availableHeight - yOffset); if (lastRowIndex == startRowIndex) { willOverflow = true; return; } if (columnHeaderRows != null) { printRows.addAll(columnHeaderRows); } if (lastRowIndex >= rowHeadersData[0].length) { startColumnIndex = lastColumnIndex; if (startColumnIndex < columnHeadersData[0].length) { startRowIndex = lastRowIndex = 0; yOffset += getColumnBreakOffset(); fillVerticalCrosstab(availableHeight); return; } } boolean fillEnded = lastRowIndex >= rowHeadersData[0].length && lastColumnIndex >= columnHeadersData[0].length; if (fillEnded) { setStretchHeight(yOffset); } else { setStretchHeight(availableHeight); } startRowIndex = lastRowIndex; willOverflow = !fillEnded; } protected List getGroupHeaders(int available, int[] offsets, boolean[] breakable, int firstIndex, HeaderCell[][] headersData, JRFillCrosstabGroup[] groups) { List headers = new ArrayList(); int maxOffset = available + offsets[firstIndex]; int lastIndex; for (lastIndex = firstIndex; lastIndex < headersData[0].length && (ignoreWidth || offsets[lastIndex + 1] <= maxOffset); ++lastIndex) { HeaderCell[] groupHeaders = new HeaderCell[groups.length]; for (int j = 0; j < groups.length; ++j) { groupHeaders[j] = headersData[j][lastIndex]; } headers.add(groupHeaders); } if (lastIndex < headersData[0].length) { while(lastIndex > firstIndex && !breakable[lastIndex]) { --lastIndex; headers.remove(headers.size() - 1); } } if (lastIndex > firstIndex) { if (firstIndex > 0) { HeaderCell[] firstHeaders = (HeaderCell[]) headers.get(0); for (int j = 0; j < groups.length; ++j) { HeaderCell header = headersData[j][firstIndex]; if (header == null) { int spanIndex = getSpanIndex(firstIndex, j, headersData); if (spanIndex >= 0) { HeaderCell spanCell = headersData[j][spanIndex]; int headerEndIdx = spanCell.getLevelSpan() + spanIndex; if (headerEndIdx > lastIndex) { headerEndIdx = lastIndex; } firstHeaders[j] = HeaderCell.createLevelSpanCopy(spanCell, headerEndIdx - firstIndex); } } } } if (lastIndex < headersData[0].length) { for (int j = 0; j < groups.length; ++j) { HeaderCell header = headersData[j][lastIndex]; if (header == null) { int spanIndex = getSpanIndex(lastIndex, j, headersData); if (spanIndex >= firstIndex) { HeaderCell spanCell = headersData[j][spanIndex]; HeaderCell[] headerCells = (HeaderCell[]) headers.get(spanIndex - firstIndex); headerCells[j] = HeaderCell.createLevelSpanCopy(spanCell, lastIndex - spanIndex); } } } } } return headers; } protected int getSpanIndex(int i, int j, HeaderCell[][] headersData) { int spanIndex = i - 1; while (spanIndex >= 0 && headersData[j][spanIndex] == null) { --spanIndex; } if (spanIndex >= 0) { HeaderCell spanCell = headersData[j][spanIndex]; int span = spanCell.getLevelSpan(); if (span > i - spanIndex) { return spanIndex; } } return -1; } protected void fillNoDataCell(int availableHeight) throws JRException { if (whenNoDataCell == null) { noDataCellPrinted = true; } else { if (availableHeight < whenNoDataCell.getHeight()) { willOverflow = true; } else { whenNoDataCell.evaluate(JRExpression.EVALUATION_DEFAULT); whenNoDataCell.prepare(availableHeight); willOverflow = whenNoDataCell.willOverflow(); if (!willOverflow) { whenNoDataCell.setX(0); whenNoDataCell.setY(0); JRPrintFrame printCell = whenNoDataCell.fill(); List noDataRow = new ArrayList(1); noDataRow.add(printCell); addPrintRow(noDataRow); yOffset += whenNoDataCell.getPrintHeight(); noDataCellPrinted = true; } } } } protected List fillColumnHeaders(int rowHeadersXOffset, int availableHeight) throws JRException { JRFillCellContents[][] columnHeaderRows = new JRFillCellContents[columnGroups.length][lastColumnIndex - startColumnIndex + 1]; rowYs.clear(); rowYs.add(Integer.valueOf(0)); if (printRowHeaders && headerCell != null) { JRFillCellContents contents = fillHeader(availableHeight); if (willOverflow) { return null; } columnHeaderRows[columnGroups.length - 1][0] = contents; } rows: for (rowIdx = 0; rowIdx < columnGroups.length; rowIdx++) { for (int columnIdx = startColumnIndex; columnIdx < lastColumnIndex; ++columnIdx) { HeaderCell[] headers = (HeaderCell[]) columnHeaders.get(columnIdx - startColumnIndex); HeaderCell cell = headers[rowIdx]; if (cell != null) { JRFillCellContents contents = prepareColumnHeader(cell, columnIdx, rowHeadersXOffset, availableHeight); columnHeaderRows[rowIdx + cell.getDepthSpan() - 1][columnIdx - startColumnIndex + 1] = contents; if (willOverflow) { break rows; } } } int rowStretchHeight = stretchColumnHeadersRow(columnHeaderRows[rowIdx]); rowYs.add(Integer.valueOf(((Integer) rowYs.get(rowIdx)).intValue() + rowStretchHeight)); } List headerRows; if (willOverflow) { headerRows = null; releaseColumnHeaderCells(columnHeaderRows); } else { headerRows = fillColumnHeaders(columnHeaderRows); yOffset += ((Integer) rowYs.get(columnGroups.length)).intValue(); } resetVariables(); return headerRows; } private void setCountVars(int rowIdx, int colIdx) { if (rowIdx == -1) { rowCountVar.setValue(null); } else { rowCountVar.setValue(Integer.valueOf(rowCount[rowIdx])); } if (colIdx == -1) { colCountVar.setValue(null); } else { colCountVar.setValue(Integer.valueOf(columnCount[colIdx])); } } private JRFillCellContents fillHeader(int availableHeight) throws JRException { setCountVars(-1, -1); JRFillCellContents contents = headerCell.getWorkingClone(); contents.evaluate(JRExpression.EVALUATION_DEFAULT); contents.prepare(availableHeight); willOverflow = contents.willOverflow(); if (!willOverflow) { contents.setX(0); contents.setY(yOffset); contents.setVerticalSpan(columnGroups.length); contents.setHorizontalSpan(rowGroups.length); } return contents; } private JRFillCellContents prepareColumnHeader(HeaderCell cell, int columnIdx, int xOffset, int availableHeight) throws JRException { JRFillCrosstabColumnGroup group = columnGroups[rowIdx]; JRFillCellContents contents = cell.isTotal() ? group.getFillTotalHeader() : group.getFillHeader(); int width = columnXOffsets[columnIdx + cell.getLevelSpan()] - columnXOffsets[columnIdx]; int height = contents.getHeight(); if (width <= 0 || height <= 0) { return null; } JRFillCellContents preparedContents = null; int rowY = ((Integer) rowYs.get(rowIdx)).intValue(); if (availableHeight >= rowY + height) { setCountVars(-1, columnIdx); setGroupVariables(columnGroups, cell.getBucketValues()); contents = contents.getTransformedContents(width, height, group.getPositionValue(), CrosstabRowPositionEnum.TOP); boolean firstOnRow = columnIdx == startColumnIndex && (!printRowHeaders || headerCell == null); contents = contents.getBoxContents( firstOnRow && getRunDirectionValue() == RunDirectionEnum.LTR, firstOnRow && getRunDirectionValue() == RunDirectionEnum.RTL, false); contents = contents.getWorkingClone(); contents.evaluate(JRExpression.EVALUATION_DEFAULT); contents.prepare(availableHeight - rowY); if (contents.willOverflow()) { willOverflow = true; } else { contents.setX(columnXOffsets[columnIdx] - columnXOffsets[startColumnIndex] + xOffset); contents.setY(rowY + yOffset); contents.setVerticalSpan(cell.getDepthSpan()); contents.setHorizontalSpan(cell.getLevelSpan()); preparedContents = contents; } } else { willOverflow = true; } return preparedContents; } private int stretchColumnHeadersRow(JRFillCellContents[] headers) { int rowY = ((Integer) rowYs.get(rowIdx)).intValue(); int rowStretchHeight = 0; for (int j = 0; j < headers.length; j++) { JRFillCellContents contents = headers[j]; if (contents != null) { int startRowY = rowY; if (contents.getVerticalSpan() > 1) { startRowY = ((Integer) rowYs.get(rowIdx - contents.getVerticalSpan() + 1)).intValue(); } int height = contents.getPrintHeight() - rowY + startRowY; if (height > rowStretchHeight) { rowStretchHeight = height; } } } for (int j = 0; j < headers.length; j++) { JRFillCellContents contents = headers[j]; if (contents != null) { int startRowY = rowY; if (contents.getVerticalSpan() > 1) { startRowY = ((Integer) rowYs.get(rowIdx - contents.getVerticalSpan() + 1)).intValue(); } contents.stretchTo(rowStretchHeight + rowY - startRowY); } } return rowStretchHeight; } private List fillColumnHeaders(JRFillCellContents[][] columnHeaderRows) throws JRException { List headerRows = new ArrayList(columnGroups.length); for (int i = 0; i < columnHeaderRows.length; ++i) { List headerRow = new ArrayList(lastColumnIndex - startColumnIndex); headerRows.add(headerRow); for (int j = 0; j < columnHeaderRows[i].length; j++) { JRFillCellContents contents = columnHeaderRows[i][j]; if (contents != null) { headerRow.add(contents.fill()); contents.releaseWorkingClone(); } } } return headerRows; } private void releaseColumnHeaderCells(JRFillCellContents[][] columnHeaderRows) throws JRException { for (int i = 0; i < columnHeaderRows.length; ++i) { for (int j = 0; j < columnHeaderRows[i].length; j++) { JRFillCellContents contents = columnHeaderRows[i][j]; if (contents != null) { contents.rewind(); contents.releaseWorkingClone(); } } } } protected int fillRows(int xOffset, int availableHeight) throws JRException { rowYs.clear(); rowYs.add(Integer.valueOf(0)); for (rowIdx = 0; rowIdx < cellData.length - startRowIndex; ++rowIdx) { initPreparedRow(); prepareRow(xOffset, availableHeight); if (willOverflow) { break; } fillRow(); rowYs.add(Integer.valueOf(((Integer) rowYs.get(rowIdx)).intValue() + preparedRowHeight)); } if (rowIdx < cellData.length - startRowIndex)//overflow { releasePreparedRow(); if (printRowHeaders) { fillContinuingRowHeaders(xOffset, availableHeight); } } yOffset += ((Integer) rowYs.get(rowIdx)).intValue(); return rowIdx + startRowIndex; } private void initPreparedRow() { preparedRow.clear(); preparedRowHeight = 0; } private void removeFilledRows(int rowsToRemove) { if (rowsToRemove > 0) { for (int i = 0; i < rowsToRemove; ++i) { printRows.remove(printRows.size() - 1); rowYs.remove(rowYs.size() - 1); } rowIdx -= rowsToRemove; } } private void releasePreparedRow() throws JRException { for (Iterator it = preparedRow.iterator(); it.hasNext();) { JRFillCellContents cell = (JRFillCellContents) it.next(); cell.rewind(); cell.releaseWorkingClone(); } preparedRow.clear(); } private void fillRow() throws JRException { int rowY = ((Integer) rowYs.get(rowIdx)).intValue(); List rowPrints = new ArrayList(preparedRow.size()); for (Iterator it = preparedRow.iterator(); it.hasNext();) { JRFillCellContents cell = (JRFillCellContents) it.next(); int spanHeight = 0; if (cell.getVerticalSpan() > 1) { spanHeight = rowY - ((Integer) rowYs.get(rowIdx - cell.getVerticalSpan() + 1)).intValue(); } cell.stretchTo(preparedRowHeight + spanHeight); rowPrints.add(cell.fill()); cell.releaseWorkingClone(); } addPrintRow(rowPrints); } private void prepareRow(int xOffset, int availableHeight) throws JRException { for (int col = startColumnIndex; col < lastColumnIndex; ++col) { CrosstabCell data = cellData[rowIdx + startRowIndex][col]; boolean overflow = prepareDataCell(data, col, availableHeight, xOffset); if (overflow) { willOverflow = true; return; } } resetVariables(); if (printRowHeaders) { for (int j = 0; j < rowGroups.length; j++) { HeaderCell cell = rowHeadersData[j][rowIdx + startRowIndex]; int vSpan = 0; if (cell == null) { // if we have a span header if (toCloseRowHeader(j)) { cell = spanHeaders[j]; vSpan = cell.getLevelSpan(); if (spanHeadersStart[j] < startRowIndex)//continuing from the prev page { vSpan += spanHeadersStart[j] - startRowIndex; } } } else { if (cell.getLevelSpan() > 1) { spanHeaders[j] = cell; spanHeadersStart[j] = rowIdx + startRowIndex; continue; } vSpan = 1; } if (cell != null) { boolean overflow = prepareRowHeader(j, cell, vSpan, availableHeight); if (overflow) { willOverflow = true; return; } } } // successfully prepared a row, reset the span headers for (int j = 0; j < rowGroups.length; j++) { // if we have a span header if (rowHeadersData[j][rowIdx + startRowIndex] == null && toCloseRowHeader(j)) { // reset it spanHeaders[j] = null; } } resetVariables(); } } private boolean prepareDataCell(CrosstabCell data, int column, int availableHeight, int xOffset) throws JRException { int rowY = ((Integer) rowYs.get(rowIdx)).intValue(); JRFillCrosstabCell cell = crossCells[data.getRowTotalGroupIndex()][data.getColumnTotalGroupIndex()]; JRFillCellContents contents = cell == null ? null : cell.getFillContents(); if (contents == null || contents.getWidth() <= 0 || contents.getHeight() <= 0) { return false; } boolean overflow = availableHeight < rowY + contents.getHeight(); if (!overflow) { boolean leftEmpty = startColumnIndex != 0 && !isRepeatRowHeaders(); boolean topEmpty = startRowIndex != 0 && !isRepeatColumnHeaders(); setCountVars(rowIdx + startRowIndex, column); setGroupVariables(rowGroups, data.getRowBucketValues()); setGroupVariables(columnGroups, data.getColumnBucketValues()); setMeasureVariables(data); boolean firstOnRow = leftEmpty && column == startColumnIndex; contents = contents.getBoxContents( firstOnRow && getRunDirectionValue() == RunDirectionEnum.LTR, firstOnRow && getRunDirectionValue() == RunDirectionEnum.RTL, topEmpty && rowIdx == 0); contents = contents.getWorkingClone(); contents.evaluate(JRExpression.EVALUATION_DEFAULT); contents.prepare(availableHeight - rowY); preparedRow.add(contents); overflow = contents.willOverflow(); if (!overflow) { contents.setX(columnXOffsets[column] - columnXOffsets[startColumnIndex] + xOffset); contents.setY(rowY + yOffset); int rowCellHeight = contents.getPrintHeight(); if (rowCellHeight > preparedRowHeight) { preparedRowHeight = rowCellHeight; } } } return overflow; } private boolean prepareRowHeader(int rowGroup, HeaderCell cell, int vSpan, int availableHeight) throws JRException { JRFillCrosstabRowGroup group = rowGroups[rowGroup]; JRFillCellContents contents = cell.isTotal() ? group.getFillTotalHeader() : group.getFillHeader(); if (contents.getWidth() <= 0 || contents.getHeight() <= 0) { return false; } int spanHeight = 0; int headerY = ((Integer) rowYs.get(rowIdx - vSpan + 1)).intValue(); if (vSpan > 1) { spanHeight += ((Integer) rowYs.get(rowIdx)).intValue() - headerY; } int rowHeight = spanHeight + preparedRowHeight; boolean stretchContents = group.getPositionValue() == CrosstabRowPositionEnum.STRETCH; int contentsHeight = stretchContents ? rowHeight : contents.getHeight(); boolean headerOverflow = availableHeight < headerY + contentsHeight || rowHeight < contents.getHeight(); if (!headerOverflow) { setCountVars(rowIdx + startRowIndex - vSpan + 1, -1); setGroupVariables(rowGroups, cell.getBucketValues()); if (stretchContents) { contents = contents.getTransformedContents(contents.getWidth(), rowHeight, CrosstabColumnPositionEnum.LEFT, CrosstabRowPositionEnum.STRETCH); } contents = contents.getBoxContents(false, false, rowIdx + 1 == vSpan && (!printColumnHeaders || headerCell == null)); contents.getWorkingClone(); contents.evaluate(JRExpression.EVALUATION_DEFAULT); contents.prepare(availableHeight - headerY); preparedRow.add(contents); headerOverflow = contents.willOverflow(); if (!headerOverflow) { contents.setX(rowHeadersXOffsets[rowGroup]); contents.setY(headerY + yOffset); contents.setVerticalSpan(vSpan); contents.setHorizontalSpan(cell.getDepthSpan()); int rowCellHeight = contents.getPrintHeight() - spanHeight; if (rowCellHeight > preparedRowHeight) { preparedRowHeight = rowCellHeight; } } } if (headerOverflow) { removeFilledRows(vSpan - 1); } return headerOverflow; } protected boolean toCloseRowHeader(int rowGroup) { return rowGroup < rowGroups.length - 1 && spanHeaders[rowGroup] != null && spanHeaders[rowGroup].getLevelSpan() + spanHeadersStart[rowGroup] == rowIdx + startRowIndex + 1; } private void removeExceedingSpanHeaders() { for (int j = rowGroups.length - 2; j >= 0; --j) { if (spanHeaders[j] != null && spanHeadersStart[j] >= rowIdx + startRowIndex) { spanHeaders[j] = null; } } } private void setBackSpanHeaders() { for (int j = rowGroups.length - 2; j >= 0 && spanHeaders[j] == null; --j) { int spanIndex = getSpanIndex(rowIdx + startRowIndex, j, rowHeadersData); if (spanIndex >= 0) { spanHeaders[j] = rowHeadersData[j][spanIndex]; spanHeadersStart[j] = spanIndex; } } } private void fillContinuingRowHeaders(int xOffset, int availableHeight) throws JRException { boolean done = false; breakCrosstab: do { removeExceedingSpanHeaders(); if (!rowBreakable[rowIdx + startRowIndex]) { removeFilledRows(1); setBackSpanHeaders(); continue; } initPreparedRow(); //fill continuing headers for (int j = 0; j < rowGroups.length - 1; ++j) { if (spanHeaders[j] != null) { boolean headerOverflow = prepareContinuingRowHeader(j, availableHeight); if (headerOverflow) { releasePreparedRow(); continue breakCrosstab; } } } if (!preparedRow.isEmpty()) { int lastRowHeight = ((Integer) rowYs.get(rowIdx)).intValue() - ((Integer) rowYs.get(rowIdx - 1)).intValue(); if (preparedRowHeight > lastRowHeight)//need to stretch already filled row by refilling { refillLastRow(xOffset, availableHeight); } else { fillContinuingHeaders(lastRowHeight); } } done = true; } while (!done && rowIdx > 0); } private void fillContinuingHeaders(int lastRowHeight) throws JRException { int nextToLastHeaderY = ((Integer) rowYs.get(rowIdx - 1)).intValue(); List lastPrintRow = getLastPrintRow(); for (int j = 0; j < preparedRow.size(); ++j) { JRFillCellContents contents = (JRFillCellContents) preparedRow.get(j); int headerY = ((Integer) rowYs.get(rowIdx - contents.getVerticalSpan())).intValue(); contents.stretchTo(nextToLastHeaderY - headerY + lastRowHeight); lastPrintRow.add(contents.fill()); contents.releaseWorkingClone(); } } private void refillLastRow(int xOffset, int availableHeight) throws JRException { removeFilledRows(1); setBackSpanHeaders(); prepareRow(xOffset, availableHeight); fillRow(); rowYs.add(Integer.valueOf(((Integer) rowYs.get(rowIdx)).intValue() + preparedRowHeight)); ++rowIdx; } private boolean prepareContinuingRowHeader(int rowGroup, int availableHeight) throws JRException { HeaderCell cell = spanHeaders[rowGroup]; int vSpan = rowIdx + startRowIndex - spanHeadersStart[rowGroup]; if (spanHeadersStart[rowGroup] < startRowIndex)//continuing from the prev page { vSpan += spanHeadersStart[rowGroup] - startRowIndex; } int headerY = ((Integer) rowYs.get(rowIdx - vSpan)).intValue(); int lastHeaderY = ((Integer) rowYs.get(rowIdx)).intValue(); int headerHeight = lastHeaderY - headerY; int nextToLastHeaderY = ((Integer) rowYs.get(rowIdx - 1)).intValue(); int stretchHeight = nextToLastHeaderY - headerY; JRFillCrosstabRowGroup group = rowGroups[rowGroup]; JRFillCellContents contents = cell.isTotal() ? group.getFillTotalHeader() : group.getFillHeader(); boolean stretchContents = group.getPositionValue() == CrosstabRowPositionEnum.STRETCH; int contentsHeight = stretchContents ? headerHeight : contents.getHeight(); boolean headerOverflow = availableHeight < headerY + contentsHeight || headerHeight < contents.getHeight(); if (!headerOverflow) { setCountVars(rowIdx + startRowIndex - vSpan, -1); setGroupVariables(rowGroups, cell.getBucketValues()); if (stretchContents) { contents = contents.getTransformedContents(contents.getWidth(), headerHeight, CrosstabColumnPositionEnum.LEFT, CrosstabRowPositionEnum.STRETCH); } contents = contents.getBoxContents(false, false, rowIdx == vSpan && (!printColumnHeaders || headerCell == null)); contents.getWorkingClone(); contents.evaluate(JRExpression.EVALUATION_DEFAULT); contents.prepare(availableHeight - headerY); preparedRow.add(contents); headerOverflow = contents.willOverflow(); if (!headerOverflow) { contents.setX(rowHeadersXOffsets[rowGroup]); contents.setY(headerY + yOffset); contents.setVerticalSpan(vSpan); contents.setHorizontalSpan(cell.getDepthSpan()); int rowHeight = contents.getPrintHeight() - stretchHeight; if (rowHeight > preparedRowHeight) { preparedRowHeight = rowHeight; } } } if (headerOverflow) { removeFilledRows(vSpan); } return headerOverflow; } protected void addPrintRow(List printRow) { printRows.add(printRow); } protected List getLastPrintRow() { return (List) printRows.get(printRows.size() - 1); } protected List getPrintElements() { List prints = new ArrayList(); for (Iterator it = printRows.iterator(); it.hasNext();) { List rowPrints = (List) it.next(); prints.addAll(rowPrints); } Collections.sort(prints, new JRYXComparator());//FIXME make singleton comparator; same for older comparator return prints; } protected void setGroupVariables(JRFillCrosstabGroup[] groups, Bucket[] bucketValues) { for (int i = 0; i < groups.length; i++) { Object value = null; if (bucketValues[i] != null && !bucketValues[i].isTotal()) { value = bucketValues[i].getValue(); } groups[i].getFillVariable().setValue(value); } } protected void setMeasureVariables(CrosstabCell cell) { MeasureValue[] values = cell.getMesureValues(); for (int i = 0; i < measures.length; i++) { Object value = measureValue(values, i); measures[i].getFillVariable().setValue(value); } MeasureValue[][][] totals = cell.getTotals(); for (int row = 0; row <= rowGroups.length; row++) { for (int col = 0; col <= columnGroups.length; col++) { MeasureValue[] vals = totals[row][col]; if (retrieveTotal[row][col]) { for (int m = 0; m < measures.length; m++) { JRFillVariable totalVar = totalVariables[row][col][m]; Object value = measureValue(vals, m); totalVar.setValue(value); } } } } } protected Object measureValue(MeasureValue[] values, int measureIdx) { Object value; if (measures[measureIdx].getPercentageType() == CrosstabPercentageEnum.GRAND_TOTAL) { if (values[measureIdx].isInitialized()) { value = values[measureIdx].getValue(); } else { value = measures[measureIdx].getPercentageCalculator().calculatePercentage(values[measureIdx], grandTotals[measureIdx]); } } else { value = values[measureIdx].getValue(); } return value; } protected void resetVariables() { for (int i = 0; i < rowGroups.length; i++) { rowGroups[i].getFillVariable().setValue(null); } for (int i = 0; i < columnGroups.length; i++) { columnGroups[i].getFillVariable().setValue(null); } for (int i = 0; i < measures.length; i++) { measures[i].getFillVariable().setValue(null); } for (int row = 0; row <= rowGroups.length; ++row) { for (int col = 0; col <= columnGroups.length; ++col) { if (retrieveTotal[row][col]) { for (int i = 0; i < measures.length; i++) { totalVariables[row][col][i].setValue(null); } } } } } } public int getColumnBreakOffset() { return parentCrosstab.getColumnBreakOffset(); } public boolean isRepeatColumnHeaders() { return parentCrosstab.isRepeatColumnHeaders(); } public boolean isRepeatRowHeaders() { return parentCrosstab.isRepeatRowHeaders(); } public JRCrosstabCell[][] getCells() { return crossCells; } public JRCellContents getWhenNoDataCell() { return whenNoDataCell; } public JRCrosstabParameter[] getParameters() { return parameters; } public JRExpression getParametersMapExpression() { return parentCrosstab.getParametersMapExpression(); } public JRElement getElementByKey(String elementKey) { return JRBaseCrosstab.getElementByKey(this, elementKey); } public JRFillCloneable createClone(JRFillCloneFactory factory) { //not needed return null; } public JRCellContents getHeaderCell() { return headerCell; } public JRVariable[] getVariables() { return variables; } /** * @deprecated Replaced by {@link #getRunDirectionValue()}. */ public byte getRunDirection() { return parentCrosstab.getRunDirectionValue().getValue(); } /** * @deprecated Replaced by {@link #setRunDirection(RunDirectionEnum)}. */ public void setRunDirection(byte direction) { throw new UnsupportedOperationException(); } /** * */ public RunDirectionEnum getRunDirectionValue() { return parentCrosstab.getRunDirectionValue(); } /** * */ public void setRunDirection(RunDirectionEnum runDirection) { throw new UnsupportedOperationException(); } public JROrigin getOrigin() { return getElementOrigin(); } public Boolean getIgnoreWidth() { return Boolean.valueOf(ignoreWidth); } public void setIgnoreWidth(Boolean ignoreWidth) { if (ignoreWidth != null) { setIgnoreWidth(ignoreWidth.booleanValue()); } } public void setIgnoreWidth(boolean ignoreWidth) { this.ignoreWidth = ignoreWidth; } }