0001: /*
0002: * ============================================================================
0003: * GNU Lesser General Public License
0004: * ============================================================================
0005: *
0006: * JasperReports - Free Java report-generating library.
0007: * Copyright (C) 2001-2006 JasperSoft Corporation http://www.jaspersoft.com
0008: *
0009: * This library is free software; you can redistribute it and/or
0010: * modify it under the terms of the GNU Lesser General Public
0011: * License as published by the Free Software Foundation; either
0012: * version 2.1 of the License, or (at your option) any later version.
0013: *
0014: * This library is distributed in the hope that it will be useful,
0015: * but WITHOUT ANY WARRANTY; without even the implied warranty of
0016: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
0017: * Lesser General Public License for more details.
0018: *
0019: * You should have received a copy of the GNU Lesser General Public
0020: * License along with this library; if not, write to the Free Software
0021: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
0022: *
0023: * JasperSoft Corporation
0024: * 303 Second Street, Suite 450 North
0025: * San Francisco, CA 94107
0026: * http://www.jaspersoft.com
0027: */
0028: package net.sf.jasperreports.engine.fill;
0029:
0030: import java.io.IOException;
0031: import java.util.ArrayList;
0032: import java.util.Comparator;
0033: import java.util.HashMap;
0034: import java.util.Iterator;
0035: import java.util.List;
0036: import java.util.Map;
0037: import java.util.ResourceBundle;
0038:
0039: import net.sf.jasperreports.crosstabs.JRCellContents;
0040: import net.sf.jasperreports.crosstabs.JRCrosstab;
0041: import net.sf.jasperreports.crosstabs.JRCrosstabBucket;
0042: import net.sf.jasperreports.crosstabs.JRCrosstabCell;
0043: import net.sf.jasperreports.crosstabs.JRCrosstabColumnGroup;
0044: import net.sf.jasperreports.crosstabs.JRCrosstabDataset;
0045: import net.sf.jasperreports.crosstabs.JRCrosstabGroup;
0046: import net.sf.jasperreports.crosstabs.JRCrosstabMeasure;
0047: import net.sf.jasperreports.crosstabs.JRCrosstabParameter;
0048: import net.sf.jasperreports.crosstabs.JRCrosstabRowGroup;
0049: import net.sf.jasperreports.crosstabs.base.JRBaseCrosstab;
0050: import net.sf.jasperreports.crosstabs.design.JRDesignCrosstab;
0051: import net.sf.jasperreports.crosstabs.fill.JRCrosstabExpressionEvaluator;
0052: import net.sf.jasperreports.crosstabs.fill.JRFillCrosstabCell;
0053: import net.sf.jasperreports.crosstabs.fill.JRFillCrosstabColumnGroup;
0054: import net.sf.jasperreports.crosstabs.fill.JRFillCrosstabGroup;
0055: import net.sf.jasperreports.crosstabs.fill.JRFillCrosstabMeasure;
0056: import net.sf.jasperreports.crosstabs.fill.JRFillCrosstabParameter;
0057: import net.sf.jasperreports.crosstabs.fill.JRFillCrosstabRowGroup;
0058: import net.sf.jasperreports.crosstabs.fill.calculation.BucketDefinition;
0059: import net.sf.jasperreports.crosstabs.fill.calculation.BucketingService;
0060: import net.sf.jasperreports.crosstabs.fill.calculation.CrosstabCell;
0061: import net.sf.jasperreports.crosstabs.fill.calculation.HeaderCell;
0062: import net.sf.jasperreports.crosstabs.fill.calculation.MeasureDefinition;
0063: import net.sf.jasperreports.crosstabs.fill.calculation.BucketDefinition.Bucket;
0064: import net.sf.jasperreports.crosstabs.fill.calculation.MeasureDefinition.MeasureValue;
0065: import net.sf.jasperreports.engine.JRAbstractObjectFactory;
0066: import net.sf.jasperreports.engine.JRChild;
0067: import net.sf.jasperreports.engine.JRElement;
0068: import net.sf.jasperreports.engine.JRException;
0069: import net.sf.jasperreports.engine.JRExpression;
0070: import net.sf.jasperreports.engine.JRExpressionChunk;
0071: import net.sf.jasperreports.engine.JRExpressionCollector;
0072: import net.sf.jasperreports.engine.JRGraphicElement;
0073: import net.sf.jasperreports.engine.JRParameter;
0074: import net.sf.jasperreports.engine.JRPrintElement;
0075: import net.sf.jasperreports.engine.JRPrintFrame;
0076: import net.sf.jasperreports.engine.JRPrintRectangle;
0077: import net.sf.jasperreports.engine.JRRuntimeException;
0078: import net.sf.jasperreports.engine.JRStyle;
0079: import net.sf.jasperreports.engine.JRVariable;
0080: import net.sf.jasperreports.engine.JasperCompileManager;
0081: import net.sf.jasperreports.engine.JasperReport;
0082: import net.sf.jasperreports.engine.design.JRDesignRectangle;
0083: import net.sf.jasperreports.engine.util.JRStyleResolver;
0084: import net.sf.jasperreports.engine.xml.JRXmlWriter;
0085:
0086: import org.jfree.data.general.Dataset;
0087:
0088: /**
0089: * Fill-time implementation of a {@link net.sf.jasperreports.crosstabs.JRCrosstab crosstab}.
0090: *
0091: * @author Lucian Chirita (lucianc@users.sourceforge.net)
0092: * @version $Id: JRFillCrosstab.java 1828 2007-08-24 13:58:43Z teodord $
0093: */
0094: public class JRFillCrosstab extends JRFillElement implements JRCrosstab {
0095: final protected JRCrosstab parentCrosstab;
0096:
0097: protected JRFillCrosstabDataset dataset;
0098:
0099: protected JRFillCrosstabRowGroup[] rowGroups;
0100:
0101: protected Map rowGroupsMap;
0102:
0103: protected JRFillCrosstabColumnGroup[] columnGroups;
0104:
0105: protected Map columnGroupsMap;
0106:
0107: protected JRFillCrosstabMeasure[] measures;
0108:
0109: protected BucketingService bucketingService;
0110:
0111: protected JRFillVariable[] variables;
0112:
0113: protected Map variablesMap;
0114:
0115: protected JRFillVariable[][][] totalVariables;
0116: protected boolean[][] retrieveTotal;
0117:
0118: protected JRFillCrosstabParameter[] parameters;
0119:
0120: protected Map parametersMap;
0121:
0122: protected JRCrosstabExpressionEvaluator crosstabEvaluator;
0123:
0124: protected JRFillCrosstabCell[][] crossCells;
0125: protected JRFillCellContents headerCell;
0126: protected JRFillCellContents whenNoDataCell;
0127:
0128: protected boolean hasData;
0129: protected HeaderCell[][] columnHeadersData;
0130: protected HeaderCell[][] rowHeadersData;
0131: protected CrosstabCell[][] cellData;
0132: protected MeasureValue[] grandTotals;
0133:
0134: private boolean percentage;
0135:
0136: private CrosstabFiller crosstabFiller;
0137:
0138: public JRFillCrosstab(JRBaseFiller filler, JRCrosstab crosstab,
0139: JRFillObjectFactory factory) {
0140: super (filler, crosstab, factory);
0141:
0142: parentCrosstab = crosstab;
0143:
0144: loadEvaluator(filler.getJasperReport());
0145:
0146: JRFillObjectFactory crosstabFactory = new JRFillObjectFactory(
0147: factory, crosstabEvaluator);
0148:
0149: headerCell = crosstabFactory.getCell(crosstab.getHeaderCell());
0150:
0151: copyRowGroups(crosstab, crosstabFactory);
0152: copyColumnGroups(crosstab, crosstabFactory);
0153:
0154: copyMeasures(crosstab, crosstabFactory);
0155: copyCells(crosstab, crosstabFactory);
0156: whenNoDataCell = crosstabFactory.getCell(crosstab
0157: .getWhenNoDataCell());
0158:
0159: dataset = factory.getCrosstabDataset(crosstab.getDataset(),
0160: this );
0161:
0162: copyParameters(crosstab, factory);
0163: copyVariables(crosstab, crosstabFactory);
0164:
0165: crosstabFiller = new CrosstabFiller();
0166: }
0167:
0168: /**
0169: *
0170: */
0171: public byte getMode() {
0172: return JRStyleResolver.getMode(this , MODE_TRANSPARENT);
0173: }
0174:
0175: private void copyRowGroups(JRCrosstab crosstab,
0176: JRFillObjectFactory factory) {
0177: JRCrosstabRowGroup[] groups = crosstab.getRowGroups();
0178: rowGroups = new JRFillCrosstabRowGroup[groups.length];
0179: rowGroupsMap = new HashMap();
0180: for (int i = 0; i < groups.length; ++i) {
0181: JRFillCrosstabRowGroup group = factory
0182: .getCrosstabRowGroup(groups[i]);
0183: group.getFillHeader().setVerticalPositionType(
0184: groups[i].getPosition());
0185:
0186: rowGroups[i] = group;
0187: rowGroupsMap.put(group.getName(), new Integer(i));
0188: }
0189: }
0190:
0191: private void copyColumnGroups(JRCrosstab crosstab,
0192: JRFillObjectFactory factory) {
0193: JRCrosstabColumnGroup[] groups = crosstab.getColumnGroups();
0194: columnGroups = new JRFillCrosstabColumnGroup[groups.length];
0195: columnGroupsMap = new HashMap();
0196: for (int i = 0; i < groups.length; ++i) {
0197: JRFillCrosstabColumnGroup group = factory
0198: .getCrosstabColumnGroup(groups[i]);
0199: columnGroups[i] = group;
0200: columnGroupsMap.put(group.getName(), new Integer(i));
0201: }
0202: }
0203:
0204: private void copyMeasures(JRCrosstab crosstab,
0205: JRFillObjectFactory factory) {
0206: JRCrosstabMeasure[] crossMeasures = crosstab.getMeasures();
0207: measures = new JRFillCrosstabMeasure[crossMeasures.length];
0208: for (int i = 0; i < crossMeasures.length; i++) {
0209: measures[i] = factory.getCrosstabMeasure(crossMeasures[i]);
0210: }
0211: }
0212:
0213: private void copyParameters(JRCrosstab crosstab,
0214: JRFillObjectFactory factory) {
0215: JRCrosstabParameter[] crossParams = crosstab.getParameters();
0216: parameters = new JRFillCrosstabParameter[crossParams.length];
0217: parametersMap = new HashMap();
0218: for (int i = 0; i < crossParams.length; i++) {
0219: parameters[i] = factory
0220: .getCrosstabParameter(crossParams[i]);
0221: parametersMap.put(parameters[i].getName(), parameters[i]);
0222: }
0223: }
0224:
0225: private void copyCells(JRCrosstab crosstab,
0226: JRFillObjectFactory factory) {
0227: JRCrosstabCell[][] crosstabCells = crosstab.getCells();
0228: crossCells = new JRFillCrosstabCell[rowGroups.length + 1][columnGroups.length + 1];
0229: for (int i = 0; i <= rowGroups.length; ++i) {
0230: for (int j = 0; j <= columnGroups.length; ++j) {
0231: if (crosstabCells[i][j] != null) {
0232: crossCells[i][j] = factory
0233: .getCrosstabCell(crosstabCells[i][j]);
0234: }
0235: }
0236: }
0237: }
0238:
0239: private void copyVariables(JRCrosstab crosstab,
0240: JRFillObjectFactory factory) {
0241: JRVariable[] vars = crosstab.getVariables();
0242: variables = new JRFillVariable[vars.length];
0243: variablesMap = new HashMap();
0244: for (int i = 0; i < variables.length; i++) {
0245: variables[i] = factory.getVariable(vars[i]);
0246: variablesMap.put(variables[i].getName(), variables[i]);
0247: }
0248:
0249: Map totalVarPos = new HashMap();
0250: totalVariables = new JRFillVariable[rowGroups.length + 1][columnGroups.length + 1][measures.length];
0251: for (int row = 0; row <= rowGroups.length; ++row) {
0252: JRCrosstabRowGroup rowGroup = row == rowGroups.length ? null
0253: : rowGroups[row];
0254: for (int col = 0; col <= columnGroups.length; ++col) {
0255: JRCrosstabColumnGroup colGroup = col == columnGroups.length ? null
0256: : columnGroups[col];
0257:
0258: if (row < rowGroups.length || col < columnGroups.length) {
0259: for (int m = 0; m < measures.length; m++) {
0260: String totalVariableName = JRDesignCrosstab
0261: .getTotalVariableName(measures[m],
0262: rowGroup, colGroup);
0263: totalVariables[row][col][m] = (JRFillVariable) variablesMap
0264: .get(totalVariableName);
0265: totalVarPos.put(totalVariableName, new int[] {
0266: row, col });
0267: }
0268: }
0269: }
0270: }
0271:
0272: retrieveTotal = new boolean[rowGroups.length + 1][columnGroups.length + 1];
0273:
0274: //FIXME avoid this
0275: List expressions = JRExpressionCollector.collectExpressions(
0276: filler.getJasperReport(), crosstab);
0277: for (Iterator iter = expressions.iterator(); iter.hasNext();) {
0278: JRExpression expression = (JRExpression) iter.next();
0279: JRExpressionChunk[] chunks = expression.getChunks();
0280: if (chunks != null) {
0281: for (int i = 0; i < chunks.length; i++) {
0282: JRExpressionChunk chunk = chunks[i];
0283: if (chunk.getType() == JRExpressionChunk.TYPE_VARIABLE) {
0284: String varName = chunk.getText();
0285: int[] pos = (int[]) totalVarPos.get(varName);
0286: if (pos != null) {
0287: retrieveTotal[pos[0]][pos[1]] = true;
0288: }
0289: }
0290: }
0291: }
0292: }
0293: }
0294:
0295: protected void loadEvaluator(JasperReport jasperReport) {
0296: try {
0297: JREvaluator evaluator = JasperCompileManager.loadEvaluator(
0298: jasperReport, parentCrosstab);
0299: crosstabEvaluator = new JRCrosstabExpressionEvaluator(
0300: evaluator);
0301: } catch (JRException e) {
0302: throw new JRRuntimeException(
0303: "Could not load evaluator for crosstab.", e);
0304: }
0305: }
0306:
0307: private BucketingService createService(byte evaluation)
0308: throws JRException {
0309: List rowBuckets = new ArrayList(rowGroups.length);
0310: for (int i = 0; i < rowGroups.length; ++i) {
0311: rowBuckets
0312: .add(createServiceBucket(rowGroups[i], evaluation));
0313: }
0314:
0315: List colBuckets = new ArrayList(columnGroups.length);
0316: for (int i = 0; i < columnGroups.length; ++i) {
0317: colBuckets.add(createServiceBucket(columnGroups[i],
0318: evaluation));
0319: }
0320:
0321: percentage = false;
0322: List measureList = new ArrayList(measures.length);
0323: for (int i = 0; i < measures.length; ++i) {
0324: measureList.add(createServiceMeasure(measures[i]));
0325: percentage |= measures[i].getPercentageOfType() == JRCrosstabMeasure.PERCENTAGE_TYPE_GRAND_TOTAL;
0326: }
0327:
0328: if (percentage) {
0329: ((BucketDefinition) rowBuckets.get(0)).setComputeTotal();
0330: ((BucketDefinition) colBuckets.get(0)).setComputeTotal();
0331: }
0332:
0333: return new BucketingService(rowBuckets, colBuckets,
0334: measureList, dataset.isDataPreSorted(), retrieveTotal);
0335: }
0336:
0337: private BucketDefinition createServiceBucket(JRCrosstabGroup group,
0338: byte evaluation) throws JRException {
0339: JRCrosstabBucket bucket = group.getBucket();
0340:
0341: Comparator comparator = null;
0342: JRExpression comparatorExpression = bucket
0343: .getComparatorExpression();
0344: if (comparatorExpression != null) {
0345: comparator = (Comparator) evaluateExpression(
0346: comparatorExpression, evaluation);
0347: }
0348:
0349: byte totalPosition = group.getTotalPosition();
0350: return new BucketDefinition(bucket.getExpression()
0351: .getValueClass(), comparator, bucket.getOrder(),
0352: totalPosition);
0353: }
0354:
0355: private MeasureDefinition createServiceMeasure(
0356: JRFillCrosstabMeasure measure) {
0357: return new MeasureDefinition(measure.getValueClass(), measure
0358: .getCalculation(), measure.getIncrementerFactory());
0359: }
0360:
0361: protected void reset() {
0362: super .reset();
0363:
0364: for (int i = 0; i < variables.length; i++) {
0365: variables[i].setValue(null);
0366: variables[i].setInitialized(true);
0367: }
0368: }
0369:
0370: protected void evaluate(byte evaluation) throws JRException {
0371: reset();
0372:
0373: evaluatePrintWhenExpression(evaluation);
0374:
0375: if (isPrintWhenExpressionNull() || isPrintWhenTrue()) {
0376: dataset.evaluateDatasetRun(evaluation);
0377:
0378: initEvaluator(evaluation);
0379:
0380: bucketingService.processData();
0381:
0382: hasData = bucketingService.hasData();
0383:
0384: if (hasData) {
0385: columnHeadersData = bucketingService.getColumnHeaders();
0386: rowHeadersData = bucketingService.getRowHeaders();
0387: cellData = bucketingService.getCrosstabCells();
0388: if (percentage) {
0389: grandTotals = bucketingService.getGrandTotals();
0390: }
0391:
0392: crosstabFiller.initCrosstab();
0393: }
0394: }
0395: }
0396:
0397: protected void initEvaluator(byte evaluation) throws JRException {
0398: Map parameterValues = JRFillSubreport.getParameterValues(
0399: filler, getParametersMapExpression(), getParameters(),
0400: evaluation, true, false,//hasResourceBundle
0401: false//hasFormatFactory
0402: );
0403:
0404: ResourceBundle resBdl = (ResourceBundle) parameterValues
0405: .get(JRParameter.REPORT_RESOURCE_BUNDLE);
0406: if (resBdl == null) {
0407: JRFillParameter resourceBundleParam = (JRFillParameter) filler
0408: .getParametersMap().get(
0409: JRParameter.REPORT_RESOURCE_BUNDLE);
0410: parameterValues.put(JRParameter.REPORT_RESOURCE_BUNDLE,
0411: resourceBundleParam.getValue());
0412: }
0413:
0414: parameterValues.put(JRParameter.REPORT_PARAMETERS_MAP,
0415: parameterValues);
0416:
0417: for (int i = 0; i < parameters.length; i++) {
0418: Object value = parameterValues.get(parameters[i].getName());
0419: parameters[i].setValue(value);
0420: }
0421:
0422: crosstabEvaluator.init(parametersMap, variablesMap, filler
0423: .getWhenResourceMissingType());
0424: }
0425:
0426: protected void initBucketingService() {
0427: if (bucketingService == null) {
0428: try {
0429: bucketingService = createService(JRExpression.EVALUATION_TIME_NOW);
0430: } catch (JRException e) {
0431: throw new JRRuntimeException(
0432: "Could not create bucketing service", e);
0433: }
0434: } else {
0435: bucketingService.clear();
0436: }
0437: }
0438:
0439: protected boolean prepare(int availableStretchHeight,
0440: boolean isOverflow) throws JRException {
0441: super .prepare(availableStretchHeight, isOverflow);
0442:
0443: if (!isToPrint()) {
0444: return false;
0445: }
0446:
0447: if (availableStretchHeight < getRelativeY() - getY()
0448: - getBandBottomY()) {
0449: setToPrint(false);
0450: return true;
0451: }
0452:
0453: if (isOverflow && crosstabFiller.ended() && isAlreadyPrinted()) {
0454: if (isPrintWhenDetailOverflows()) {
0455: rewind();
0456: setReprinted(true);
0457: } else {
0458: setStretchHeight(getHeight());
0459: setToPrint(false);
0460:
0461: return false;
0462: }
0463: }
0464:
0465: if (isOverflow && isPrintWhenDetailOverflows()) {
0466: setReprinted(true);
0467: }
0468:
0469: int availableHeight = getHeight() + availableStretchHeight
0470: - getRelativeY() + getY() + getBandBottomY();
0471: crosstabFiller.fill(availableHeight);
0472:
0473: boolean willOverflow = crosstabFiller.willOverflow();
0474: setStretchHeight(willOverflow ? availableHeight
0475: : crosstabFiller.getUsedHeight());
0476:
0477: return willOverflow;
0478: }
0479:
0480: protected JRPrintElement fill() {
0481: JRPrintRectangle printRectangle = null;
0482:
0483: printRectangle = new JRTemplatePrintRectangle(
0484: getJRTemplateRectangle());
0485: printRectangle.setX(getX());
0486: printRectangle.setY(getRelativeY());
0487: printRectangle.setWidth(getWidth());
0488: printRectangle.setHeight(getStretchHeight());
0489:
0490: return printRectangle;
0491: }
0492:
0493: protected JRTemplateRectangle getJRTemplateRectangle() {
0494: JRStyle style = getStyle();
0495: JRTemplateRectangle template = (JRTemplateRectangle) getTemplate(style);
0496: if (template == null) {
0497: JRDesignRectangle rectangle = new JRDesignRectangle();
0498:
0499: rectangle.setKey(getKey());
0500: rectangle.setPositionType(getPositionType());
0501: // rectangle.setPrintRepeatedValues(isPrintRepeatedValues());
0502: rectangle.setMode(getMode());
0503: rectangle.setX(getX());
0504: rectangle.setY(getY());
0505: rectangle.setWidth(getWidth());
0506: rectangle.setHeight(getHeight());
0507: rectangle.setRemoveLineWhenBlank(isRemoveLineWhenBlank());
0508: rectangle
0509: .setPrintInFirstWholeBand(isPrintInFirstWholeBand());
0510: rectangle
0511: .setPrintWhenDetailOverflows(isPrintWhenDetailOverflows());
0512: rectangle
0513: .setPrintWhenGroupChanges(getPrintWhenGroupChanges());
0514: rectangle.setForecolor(getForecolor());
0515: rectangle.setBackcolor(getBackcolor());
0516: rectangle.setPen(JRGraphicElement.PEN_NONE);
0517:
0518: template = new JRTemplateRectangle(filler.getJasperPrint()
0519: .getDefaultStyleProvider(), rectangle);
0520:
0521: registerTemplate(style, template);
0522: }
0523:
0524: return template;
0525: }
0526:
0527: protected void rewind() {
0528: crosstabFiller.initCrosstab();
0529: }
0530:
0531: protected List getPrintElements() {
0532: List printElements = crosstabFiller.getPrintElements();
0533:
0534: if (getRunDirection() == RUN_DIRECTION_RTL) {
0535: mirrorPrintElements(printElements);
0536: }
0537:
0538: return printElements;
0539: }
0540:
0541: protected void mirrorPrintElements(List printElements) {
0542: for (Iterator it = printElements.iterator(); it.hasNext();) {
0543: JRPrintElement element = (JRPrintElement) it.next();
0544: int mirrorX = getWidth() - element.getX()
0545: - element.getWidth();
0546: element.setX(mirrorX);
0547: }
0548: }
0549:
0550: protected void resolveElement(JRPrintElement element,
0551: byte evaluation) {
0552: // nothing
0553: }
0554:
0555: public void collectExpressions(JRExpressionCollector collector) {
0556: collector.collect(this );
0557: }
0558:
0559: public JRChild getCopy(JRAbstractObjectFactory factory) {
0560: return factory.getCrosstab(this );
0561: }
0562:
0563: public void writeXml(JRXmlWriter writer) throws IOException {
0564: writer.writeCrosstab(this );
0565: }
0566:
0567: public int getId() {
0568: return parentCrosstab.getId();
0569: }
0570:
0571: public JRCrosstabDataset getDataset() {
0572: return dataset;
0573: }
0574:
0575: public JRCrosstabRowGroup[] getRowGroups() {
0576: return rowGroups;
0577: }
0578:
0579: public JRCrosstabColumnGroup[] getColumnGroups() {
0580: return columnGroups;
0581: }
0582:
0583: public JRCrosstabMeasure[] getMeasures() {
0584: return measures;
0585: }
0586:
0587: /**
0588: * Fill-time crosstab input dataset implementation.
0589: *
0590: * @author Lucian Chirita (lucianc@users.sourceforge.net)
0591: */
0592: public class JRFillCrosstabDataset extends JRFillElementDataset
0593: implements JRCrosstabDataset {
0594: private Object[] bucketValues;
0595:
0596: private Object[] measureValues;
0597:
0598: public JRFillCrosstabDataset(JRCrosstabDataset dataset,
0599: JRFillObjectFactory factory) {
0600: super (dataset, factory);
0601:
0602: this .bucketValues = new Object[rowGroups.length
0603: + columnGroups.length];
0604: this .measureValues = new Object[measures.length];
0605: }
0606:
0607: protected void customInitialize() {
0608: initBucketingService();
0609: }
0610:
0611: protected void customEvaluate(JRCalculator calculator)
0612: throws JRExpressionEvalException {
0613: for (int i = 0; i < rowGroups.length; i++) {
0614: bucketValues[i] = calculator.evaluate(rowGroups[i]
0615: .getBucket().getExpression());
0616: }
0617:
0618: for (int i = 0; i < columnGroups.length; ++i) {
0619: bucketValues[i + rowGroups.length] = calculator
0620: .evaluate(columnGroups[i].getBucket()
0621: .getExpression());
0622: }
0623:
0624: for (int i = 0; i < measures.length; i++) {
0625: measureValues[i] = calculator.evaluate(measures[i]
0626: .getValueExpression());
0627: }
0628: }
0629:
0630: protected void customIncrement() {
0631: try {
0632: bucketingService.addData(bucketValues, measureValues);
0633: } catch (JRException e) {
0634: throw new JRRuntimeException(
0635: "Error incrementing crosstab dataset", e);
0636: }
0637: }
0638:
0639: protected Dataset getCustomDataset() {
0640: return null;
0641: }
0642:
0643: public void collectExpressions(JRExpressionCollector collector) {
0644: }
0645:
0646: public boolean isDataPreSorted() {
0647: return ((JRCrosstabDataset) parent).isDataPreSorted();
0648: }
0649: }
0650:
0651: /**
0652: * Crosstab filler class.
0653: *
0654: * @author Lucian Chirita (lucianc@users.sourceforge.net)
0655: */
0656: protected class CrosstabFiller {
0657: private int yOffset;
0658: private boolean willOverflow;
0659:
0660: private int[] rowHeadersXOffsets;
0661:
0662: private boolean[] columnBreakable;
0663: private boolean[] rowBreakable;
0664: private int[] columnCount;
0665: private int[] rowCount;
0666: private int[] columnXOffsets;
0667:
0668: private boolean noDataCellPrinted;
0669:
0670: private int startRowIndex;
0671: private int startColumnIndex;
0672: private int lastColumnIndex;
0673: private List columnHeaders;
0674:
0675: private List printRows;
0676:
0677: private HeaderCell[] spanHeaders;
0678: private int[] spanHeadersStart;
0679:
0680: private List rowYs = new ArrayList();
0681: private int rowIdx;
0682:
0683: private List preparedRow = new ArrayList();
0684: private int preparedRowHeight;
0685:
0686: private boolean printRowHeaders;
0687: private boolean printColumnHeaders;
0688:
0689: private JRFillVariable rowCountVar;
0690: private JRFillVariable colCountVar;
0691:
0692: protected CrosstabFiller() {
0693: setRowHeadersXOffsets();
0694:
0695: printRows = new ArrayList();
0696:
0697: rowCountVar = (JRFillVariable) variablesMap
0698: .get(JRCrosstab.VARIABLE_ROW_COUNT);
0699: colCountVar = (JRFillVariable) variablesMap
0700: .get(JRCrosstab.VARIABLE_COLUMN_COUNT);
0701: }
0702:
0703: protected void initCrosstab() {
0704: columnXOffsets = computeOffsets(columnHeadersData,
0705: columnGroups, true);
0706: columnBreakable = computeBreakableHeaders(
0707: columnHeadersData, columnGroups, columnXOffsets,
0708: true, true);
0709: columnCount = computeCounts(columnHeadersData);
0710:
0711: int[] rowYOffsets = computeOffsets(rowHeadersData,
0712: rowGroups, false);
0713: rowBreakable = computeBreakableHeaders(rowHeadersData,
0714: rowGroups, rowYOffsets, false, false);
0715: rowCount = computeCounts(rowHeadersData);
0716:
0717: spanHeaders = new HeaderCell[rowGroups.length - 1];
0718: spanHeadersStart = new int[rowGroups.length - 1];
0719:
0720: startRowIndex = 0;
0721: startColumnIndex = 0;
0722: lastColumnIndex = 0;
0723: noDataCellPrinted = false;
0724: }
0725:
0726: protected void setRowHeadersXOffsets() {
0727: rowHeadersXOffsets = new int[rowGroups.length + 1];
0728: rowHeadersXOffsets[0] = 0;
0729: for (int i = 0; i < rowGroups.length; i++) {
0730: rowHeadersXOffsets[i + 1] = rowHeadersXOffsets[i]
0731: + rowGroups[i].getWidth();
0732: }
0733: }
0734:
0735: protected int[] computeOffsets(HeaderCell[][] headersData,
0736: JRFillCrosstabGroup[] groups, boolean width) {
0737: int[] offsets = new int[headersData[0].length + 1];
0738: offsets[0] = 0;
0739: for (int i = 0; i < headersData[0].length; i++) {
0740: int size = 0;
0741: for (int j = groups.length - 1; j >= 0; --j) {
0742: if (headersData[j][i] != null) {
0743: JRFillCellContents cell = headersData[j][i]
0744: .isTotal() ? groups[j]
0745: .getFillTotalHeader() : groups[j]
0746: .getFillHeader();
0747: size = cell == null ? 0 : (width ? cell
0748: .getWidth() : cell.getHeight());
0749: break;
0750: }
0751: }
0752:
0753: offsets[i + 1] = offsets[i] + size;
0754: }
0755:
0756: return offsets;
0757: }
0758:
0759: protected boolean[] computeBreakableHeaders(
0760: HeaderCell[][] headersData,
0761: JRFillCrosstabGroup[] groups, int[] offsets,
0762: boolean width, boolean startHeaders) {
0763: boolean[] breakable = new boolean[headersData[0].length];
0764: for (int i = 0; i < breakable.length; i++) {
0765: breakable[i] = true;
0766: }
0767:
0768: for (int j = 0; j < groups.length; ++j) {
0769: JRFillCellContents fillHeader = groups[j]
0770: .getFillHeader();
0771:
0772: if (fillHeader != null) {
0773: int size = width ? fillHeader.getWidth()
0774: : fillHeader.getHeight();
0775:
0776: for (int i = 0; i < headersData[0].length; i++) {
0777: HeaderCell header = headersData[j][i];
0778: if (header != null && !header.isTotal()
0779: && header.getLevelSpan() > 1) {
0780: int span = header.getLevelSpan();
0781:
0782: if (startHeaders) {
0783: for (int k = i + 1; k < i + span
0784: && offsets[k] - offsets[i] < size; ++k) {
0785: breakable[k] = false;
0786: }
0787: }
0788:
0789: for (int k = i + span - 1; k > i
0790: && offsets[i + span] - offsets[k] < size; --k) {
0791: breakable[k] = false;
0792: }
0793: }
0794: }
0795: }
0796: }
0797:
0798: return breakable;
0799: }
0800:
0801: private int[] computeCounts(HeaderCell[][] headersData) {
0802: int[] counts = new int[headersData[0].length];
0803:
0804: HeaderCell[] lastHeaders = headersData[headersData.length - 1];
0805: for (int i = 0, c = 0; i < counts.length; ++i) {
0806: HeaderCell lastHeader = lastHeaders[i];
0807: if (lastHeader != null && !lastHeader.isTotal()) {
0808: ++c;
0809: }
0810:
0811: counts[i] = c;
0812: }
0813:
0814: return counts;
0815: }
0816:
0817: protected void fill(int availableHeight) throws JRException {
0818: printRows.clear();
0819:
0820: yOffset = 0;
0821: willOverflow = false;
0822:
0823: fillVerticalCrosstab(availableHeight);
0824: }
0825:
0826: protected boolean willOverflow() {
0827: return willOverflow;
0828: }
0829:
0830: protected int getUsedHeight() {
0831: return yOffset;
0832: }
0833:
0834: protected boolean ended() {
0835: return hasData ? (startRowIndex >= rowHeadersData[0].length && startColumnIndex >= columnHeadersData[0].length)
0836: : noDataCellPrinted;
0837: }
0838:
0839: protected void fillVerticalCrosstab(int availableHeight)
0840: throws JRException {
0841: if (!hasData) {
0842: fillNoDataCell(availableHeight);
0843: return;
0844: }
0845:
0846: printRowHeaders = startColumnIndex == 0
0847: || isRepeatRowHeaders();
0848: int rowHeadersXOffset = printRowHeaders ? rowHeadersXOffsets[rowGroups.length]
0849: : 0;
0850:
0851: if (startColumnIndex == lastColumnIndex) {
0852: int availableWidth = getWidth();
0853:
0854: columnHeaders = getGroupHeaders(availableWidth
0855: - rowHeadersXOffset, columnXOffsets,
0856: columnBreakable, startColumnIndex,
0857: columnHeadersData, columnGroups);
0858: lastColumnIndex = startColumnIndex
0859: + columnHeaders.size();
0860:
0861: if (startColumnIndex == lastColumnIndex) {
0862: throw new JRRuntimeException(
0863: "Not enough space to render the crosstab.");
0864: }
0865: }
0866:
0867: printColumnHeaders = startRowIndex == 0
0868: || isRepeatColumnHeaders();
0869: List columnHeaderRows = null;
0870: if (printColumnHeaders) {
0871: columnHeaderRows = fillColumnHeaders(rowHeadersXOffset,
0872: availableHeight - yOffset);
0873: if (willOverflow) {
0874: return;
0875: }
0876: }
0877:
0878: int lastRowIndex = fillRows(rowHeadersXOffset,
0879: availableHeight - yOffset);
0880:
0881: if (lastRowIndex == startRowIndex) {
0882: willOverflow = true;
0883: return;
0884: }
0885:
0886: if (columnHeaderRows != null) {
0887: printRows.addAll(columnHeaderRows);
0888: }
0889:
0890: if (lastRowIndex >= rowHeadersData[0].length) {
0891: startColumnIndex = lastColumnIndex;
0892:
0893: if (startColumnIndex < columnHeadersData[0].length) {
0894: startRowIndex = lastRowIndex = 0;
0895:
0896: yOffset += getColumnBreakOffset();
0897: fillVerticalCrosstab(availableHeight);
0898: return;
0899: }
0900: }
0901:
0902: boolean fillEnded = lastRowIndex >= rowHeadersData[0].length
0903: && lastColumnIndex >= columnHeadersData[0].length;
0904: if (fillEnded) {
0905: setStretchHeight(yOffset);
0906: } else {
0907: setStretchHeight(availableHeight);
0908: }
0909:
0910: startRowIndex = lastRowIndex;
0911:
0912: willOverflow = !fillEnded;
0913: }
0914:
0915: protected List getGroupHeaders(int available, int[] offsets,
0916: boolean[] breakable, int firstIndex,
0917: HeaderCell[][] headersData, JRFillCrosstabGroup[] groups) {
0918: List headers = new ArrayList();
0919:
0920: int maxOffset = available + offsets[firstIndex];
0921: int lastIndex;
0922: for (lastIndex = firstIndex; lastIndex < headersData[0].length
0923: && offsets[lastIndex + 1] <= maxOffset; ++lastIndex) {
0924: HeaderCell[] groupHeaders = new HeaderCell[groups.length];
0925:
0926: for (int j = 0; j < groups.length; ++j) {
0927: groupHeaders[j] = headersData[j][lastIndex];
0928: }
0929:
0930: headers.add(groupHeaders);
0931: }
0932:
0933: if (lastIndex < headersData[0].length) {
0934: while (lastIndex > firstIndex && !breakable[lastIndex]) {
0935: --lastIndex;
0936: headers.remove(headers.size() - 1);
0937: }
0938: }
0939:
0940: if (lastIndex > firstIndex) {
0941: if (firstIndex > 0) {
0942: HeaderCell[] firstHeaders = (HeaderCell[]) headers
0943: .get(0);
0944:
0945: for (int j = 0; j < groups.length; ++j) {
0946: HeaderCell header = headersData[j][firstIndex];
0947:
0948: if (header == null) {
0949: int spanIndex = getSpanIndex(firstIndex, j,
0950: headersData);
0951: if (spanIndex >= 0) {
0952: HeaderCell spanCell = headersData[j][spanIndex];
0953: int headerEndIdx = spanCell
0954: .getLevelSpan()
0955: + spanIndex;
0956: if (headerEndIdx > lastIndex) {
0957: headerEndIdx = lastIndex;
0958: }
0959: firstHeaders[j] = HeaderCell
0960: .createLevelSpanCopy(spanCell,
0961: headerEndIdx
0962: - firstIndex);
0963: }
0964: }
0965: }
0966: }
0967:
0968: if (lastIndex < headersData[0].length) {
0969: for (int j = 0; j < groups.length; ++j) {
0970: HeaderCell header = headersData[j][lastIndex];
0971:
0972: if (header == null) {
0973: int spanIndex = getSpanIndex(lastIndex, j,
0974: headersData);
0975: if (spanIndex >= firstIndex) {
0976: HeaderCell spanCell = headersData[j][spanIndex];
0977: HeaderCell[] headerCells = (HeaderCell[]) headers
0978: .get(spanIndex - firstIndex);
0979: headerCells[j] = HeaderCell
0980: .createLevelSpanCopy(spanCell,
0981: lastIndex - spanIndex);
0982: }
0983: }
0984: }
0985: }
0986: }
0987:
0988: return headers;
0989: }
0990:
0991: protected int getSpanIndex(int i, int j,
0992: HeaderCell[][] headersData) {
0993: int spanIndex = i - 1;
0994: while (spanIndex >= 0 && headersData[j][spanIndex] == null) {
0995: --spanIndex;
0996: }
0997:
0998: if (spanIndex >= 0) {
0999: HeaderCell spanCell = headersData[j][spanIndex];
1000: int span = spanCell.getLevelSpan();
1001:
1002: if (span > i - spanIndex) {
1003: return spanIndex;
1004: }
1005: }
1006:
1007: return -1;
1008: }
1009:
1010: protected void fillNoDataCell(int availableHeight)
1011: throws JRException {
1012: if (whenNoDataCell == null) {
1013: noDataCellPrinted = true;
1014: } else {
1015: if (availableHeight < whenNoDataCell.getHeight()) {
1016: willOverflow = true;
1017: } else {
1018: whenNoDataCell
1019: .evaluate(JRExpression.EVALUATION_DEFAULT);
1020: whenNoDataCell.prepare(availableHeight
1021: - whenNoDataCell.getHeight());
1022:
1023: willOverflow = whenNoDataCell.willOverflow();
1024:
1025: if (!willOverflow) {
1026: whenNoDataCell.setX(0);
1027: whenNoDataCell.setY(0);
1028:
1029: JRPrintFrame printCell = whenNoDataCell.fill();
1030: List noDataRow = new ArrayList(1);
1031: noDataRow.add(printCell);
1032: addPrintRow(noDataRow);
1033:
1034: yOffset += whenNoDataCell.getPrintHeight();
1035: noDataCellPrinted = true;
1036: }
1037: }
1038: }
1039: }
1040:
1041: protected List fillColumnHeaders(int rowHeadersXOffset,
1042: int availableHeight) throws JRException {
1043: JRFillCellContents[][] columnHeaderRows = new JRFillCellContents[columnGroups.length][lastColumnIndex
1044: - startColumnIndex + 1];
1045:
1046: rowYs.clear();
1047: rowYs.add(new Integer(0));
1048:
1049: if (printRowHeaders && headerCell != null) {
1050: JRFillCellContents contents = fillHeader(availableHeight);
1051:
1052: if (willOverflow) {
1053: return null;
1054: }
1055:
1056: columnHeaderRows[columnGroups.length - 1][0] = contents;
1057: }
1058:
1059: rows: for (rowIdx = 0; rowIdx < columnGroups.length; rowIdx++) {
1060: for (int columnIdx = startColumnIndex; columnIdx < lastColumnIndex; ++columnIdx) {
1061: HeaderCell[] headers = (HeaderCell[]) columnHeaders
1062: .get(columnIdx - startColumnIndex);
1063: HeaderCell cell = headers[rowIdx];
1064:
1065: if (cell != null) {
1066: JRFillCellContents contents = prepareColumnHeader(
1067: cell, columnIdx, rowHeadersXOffset,
1068: availableHeight);
1069: columnHeaderRows[rowIdx + cell.getDepthSpan()
1070: - 1][columnIdx - startColumnIndex + 1] = contents;
1071:
1072: if (willOverflow) {
1073: break rows;
1074: }
1075: }
1076: }
1077:
1078: int rowStretchHeight = stretchColumnHeadersRow(columnHeaderRows[rowIdx]);
1079: rowYs.add(new Integer(((Integer) rowYs.get(rowIdx))
1080: .intValue()
1081: + rowStretchHeight));
1082: }
1083:
1084: List headerRows;
1085: if (willOverflow) {
1086: headerRows = null;
1087: releaseColumnHeaderCells(columnHeaderRows);
1088: } else {
1089: headerRows = fillColumnHeaders(columnHeaderRows);
1090: yOffset += ((Integer) rowYs.get(columnGroups.length))
1091: .intValue();
1092: }
1093:
1094: resetVariables();
1095:
1096: return headerRows;
1097: }
1098:
1099: private void setCountVars(int rowIdx, int colIdx) {
1100: if (rowIdx == -1) {
1101: rowCountVar.setValue(null);
1102: } else {
1103: rowCountVar.setValue(new Integer(rowCount[rowIdx]));
1104: }
1105:
1106: if (colIdx == -1) {
1107: colCountVar.setValue(null);
1108: } else {
1109: colCountVar.setValue(new Integer(columnCount[colIdx]));
1110: }
1111: }
1112:
1113: private JRFillCellContents fillHeader(int availableHeight)
1114: throws JRException {
1115: setCountVars(-1, -1);
1116:
1117: JRFillCellContents contents = headerCell.getWorkingClone();
1118: contents.evaluate(JRExpression.EVALUATION_DEFAULT);
1119: contents.prepare(availableHeight - headerCell.getHeight());
1120:
1121: willOverflow = contents.willOverflow();
1122:
1123: if (!willOverflow) {
1124: contents.setX(0);
1125: contents.setY(yOffset);
1126: contents.setVerticalSpan(columnGroups.length);
1127: }
1128: return contents;
1129: }
1130:
1131: private JRFillCellContents prepareColumnHeader(HeaderCell cell,
1132: int columnIdx, int xOffset, int availableHeight)
1133: throws JRException {
1134: JRFillCrosstabColumnGroup group = columnGroups[rowIdx];
1135: JRFillCellContents contents = cell.isTotal() ? group
1136: .getFillTotalHeader() : group.getFillHeader();
1137:
1138: int width = columnXOffsets[columnIdx + cell.getLevelSpan()]
1139: - columnXOffsets[columnIdx];
1140: int height = contents.getHeight();
1141:
1142: if (width <= 0 || height <= 0) {
1143: return null;
1144: }
1145:
1146: JRFillCellContents preparedContents = null;
1147:
1148: int rowY = ((Integer) rowYs.get(rowIdx)).intValue();
1149: int cellAvailableStretch = availableHeight - rowY - height;
1150:
1151: if (cellAvailableStretch >= 0) {
1152: setCountVars(-1, columnIdx);
1153: setGroupVariables(columnGroups, cell.getBucketValues());
1154:
1155: contents = contents.getTransformedContents(width,
1156: height, group.getPosition(),
1157: JRCellContents.POSITION_Y_TOP);
1158: boolean firstOnRow = columnIdx == startColumnIndex
1159: && (!printRowHeaders || headerCell == null);
1160: contents = contents
1161: .getBoxContents(
1162: firstOnRow
1163: && getRunDirection() == RUN_DIRECTION_LTR,
1164: firstOnRow
1165: && getRunDirection() == RUN_DIRECTION_RTL,
1166: false);
1167: contents = contents.getWorkingClone();
1168:
1169: contents.evaluate(JRExpression.EVALUATION_DEFAULT);
1170: contents.prepare(cellAvailableStretch);
1171:
1172: if (contents.willOverflow()) {
1173: willOverflow = true;
1174: } else {
1175: contents.setX(columnXOffsets[columnIdx]
1176: - columnXOffsets[startColumnIndex]
1177: + xOffset);
1178: contents.setY(rowY + yOffset);
1179: contents.setVerticalSpan(cell.getDepthSpan());
1180:
1181: preparedContents = contents;
1182: }
1183: } else {
1184: willOverflow = true;
1185: }
1186:
1187: return preparedContents;
1188: }
1189:
1190: private int stretchColumnHeadersRow(JRFillCellContents[] headers) {
1191: int rowY = ((Integer) rowYs.get(rowIdx)).intValue();
1192:
1193: int rowStretchHeight = 0;
1194: for (int j = 0; j < headers.length; j++) {
1195: JRFillCellContents contents = headers[j];
1196:
1197: if (contents != null) {
1198: int startRowY = rowY;
1199: if (contents.getVerticalSpan() > 1) {
1200: startRowY = ((Integer) rowYs.get(rowIdx
1201: - contents.getVerticalSpan() + 1))
1202: .intValue();
1203: }
1204:
1205: int height = contents.getPrintHeight() - rowY
1206: + startRowY;
1207:
1208: if (height > rowStretchHeight) {
1209: rowStretchHeight = height;
1210: }
1211: }
1212: }
1213:
1214: for (int j = 0; j < headers.length; j++) {
1215: JRFillCellContents contents = headers[j];
1216:
1217: if (contents != null) {
1218: int startRowY = rowY;
1219: if (contents.getVerticalSpan() > 1) {
1220: startRowY = ((Integer) rowYs.get(rowIdx
1221: - contents.getVerticalSpan() + 1))
1222: .intValue();
1223: }
1224:
1225: contents.stretchTo(rowStretchHeight + rowY
1226: - startRowY);
1227: }
1228: }
1229:
1230: return rowStretchHeight;
1231: }
1232:
1233: private List fillColumnHeaders(
1234: JRFillCellContents[][] columnHeaderRows)
1235: throws JRException {
1236: List headerRows = new ArrayList(columnGroups.length);
1237:
1238: for (int i = 0; i < columnHeaderRows.length; ++i) {
1239: List headerRow = new ArrayList(lastColumnIndex
1240: - startColumnIndex);
1241: headerRows.add(headerRow);
1242:
1243: for (int j = 0; j < columnHeaderRows[i].length; j++) {
1244: JRFillCellContents contents = columnHeaderRows[i][j];
1245:
1246: if (contents != null) {
1247: headerRow.add(contents.fill());
1248: contents.releaseWorkingClone();
1249: }
1250: }
1251: }
1252:
1253: return headerRows;
1254: }
1255:
1256: private void releaseColumnHeaderCells(
1257: JRFillCellContents[][] columnHeaderRows)
1258: throws JRException {
1259: for (int i = 0; i < columnHeaderRows.length; ++i) {
1260: for (int j = 0; j < columnHeaderRows[i].length; j++) {
1261: JRFillCellContents contents = columnHeaderRows[i][j];
1262:
1263: if (contents != null) {
1264: contents.rewind();
1265: contents.releaseWorkingClone();
1266: }
1267: }
1268: }
1269: }
1270:
1271: protected int fillRows(int xOffset, int availableHeight)
1272: throws JRException {
1273: rowYs.clear();
1274: rowYs.add(new Integer(0));
1275:
1276: for (rowIdx = 0; rowIdx < cellData.length - startRowIndex; ++rowIdx) {
1277: initPreparedRow();
1278:
1279: prepareRow(xOffset, availableHeight);
1280:
1281: if (willOverflow) {
1282: break;
1283: }
1284:
1285: fillRow();
1286:
1287: rowYs.add(new Integer(((Integer) rowYs.get(rowIdx))
1288: .intValue()
1289: + preparedRowHeight));
1290: }
1291:
1292: if (rowIdx < cellData.length - startRowIndex)//overflow
1293: {
1294: releasePreparedRow();
1295:
1296: if (printRowHeaders) {
1297: fillContinuingRowHeaders(xOffset, availableHeight);
1298: }
1299: }
1300:
1301: yOffset += ((Integer) rowYs.get(rowIdx)).intValue();
1302:
1303: return rowIdx + startRowIndex;
1304: }
1305:
1306: private void initPreparedRow() {
1307: preparedRow.clear();
1308: preparedRowHeight = 0;
1309: }
1310:
1311: private void removeFilledRows(int rowsToRemove) {
1312: if (rowsToRemove > 0) {
1313: for (int i = 0; i < rowsToRemove; ++i) {
1314: printRows.remove(printRows.size() - 1);
1315: rowYs.remove(rowYs.size() - 1);
1316: }
1317:
1318: rowIdx -= rowsToRemove;
1319: }
1320: }
1321:
1322: private void releasePreparedRow() throws JRException {
1323: for (Iterator it = preparedRow.iterator(); it.hasNext();) {
1324: JRFillCellContents cell = (JRFillCellContents) it
1325: .next();
1326: cell.rewind();
1327: cell.releaseWorkingClone();
1328: }
1329:
1330: preparedRow.clear();
1331: }
1332:
1333: private void fillRow() throws JRException {
1334: int rowY = ((Integer) rowYs.get(rowIdx)).intValue();
1335:
1336: List rowPrints = new ArrayList(preparedRow.size());
1337: for (Iterator it = preparedRow.iterator(); it.hasNext();) {
1338: JRFillCellContents cell = (JRFillCellContents) it
1339: .next();
1340:
1341: int spanHeight = 0;
1342: if (cell.getVerticalSpan() > 1) {
1343: spanHeight = rowY
1344: - ((Integer) rowYs.get(rowIdx
1345: - cell.getVerticalSpan() + 1))
1346: .intValue();
1347: }
1348:
1349: cell.stretchTo(preparedRowHeight + spanHeight);
1350: rowPrints.add(cell.fill());
1351:
1352: cell.releaseWorkingClone();
1353: }
1354:
1355: addPrintRow(rowPrints);
1356: }
1357:
1358: private void prepareRow(int xOffset, int availableHeight)
1359: throws JRException {
1360: for (int col = startColumnIndex; col < lastColumnIndex; ++col) {
1361: CrosstabCell data = cellData[rowIdx + startRowIndex][col];
1362: boolean overflow = prepareDataCell(data, col,
1363: availableHeight, xOffset);
1364:
1365: if (overflow) {
1366: willOverflow = true;
1367: return;
1368: }
1369: }
1370:
1371: resetVariables();
1372:
1373: if (printRowHeaders) {
1374: for (int j = 0; j < rowGroups.length; j++) {
1375: HeaderCell cell = rowHeadersData[j][rowIdx
1376: + startRowIndex];
1377:
1378: boolean overflow = false;
1379: if (cell == null) {
1380: overflow = prepareClosingRowHeader(j,
1381: availableHeight);
1382: } else {
1383: if (cell.getLevelSpan() > 1) {
1384: spanHeaders[j] = cell;
1385: spanHeadersStart[j] = rowIdx
1386: + startRowIndex;
1387: continue;
1388: }
1389:
1390: overflow = prepareRowHeader(j, cell, 1,
1391: availableHeight);
1392: }
1393:
1394: if (overflow) {
1395: willOverflow = true;
1396: return;
1397: }
1398: }
1399:
1400: resetVariables();
1401: }
1402: }
1403:
1404: private boolean prepareDataCell(CrosstabCell data, int column,
1405: int availableHeight, int xOffset) throws JRException {
1406: int rowY = ((Integer) rowYs.get(rowIdx)).intValue();
1407:
1408: JRFillCrosstabCell cell = crossCells[data
1409: .getRowTotalGroupIndex()][data
1410: .getColumnTotalGroupIndex()];
1411: JRFillCellContents contents = cell == null ? null : cell
1412: .getFillContents();
1413: if (contents == null || contents.getWidth() <= 0
1414: || contents.getHeight() <= 0) {
1415: return false;
1416: }
1417:
1418: int cellAvailableStretch = availableHeight - rowY
1419: - contents.getHeight();
1420: boolean overflow = cellAvailableStretch < 0;
1421: if (!overflow) {
1422: boolean leftEmpty = startColumnIndex != 0
1423: && !isRepeatRowHeaders();
1424: boolean topEmpty = startRowIndex != 0
1425: && !isRepeatColumnHeaders();
1426:
1427: setCountVars(rowIdx + startRowIndex, column);
1428: setGroupVariables(rowGroups, data.getRowBucketValues());
1429: setGroupVariables(columnGroups, data
1430: .getColumnBucketValues());
1431: setMeasureVariables(data);
1432:
1433: boolean firstOnRow = leftEmpty
1434: && column == startColumnIndex;
1435: contents = contents
1436: .getBoxContents(
1437: firstOnRow
1438: && getRunDirection() == RUN_DIRECTION_LTR,
1439: firstOnRow
1440: && getRunDirection() == RUN_DIRECTION_RTL,
1441: topEmpty && rowIdx == 0);
1442: contents = contents.getWorkingClone();
1443:
1444: contents.evaluate(JRExpression.EVALUATION_DEFAULT);
1445: contents.prepare(cellAvailableStretch);
1446:
1447: preparedRow.add(contents);
1448:
1449: overflow = contents.willOverflow();
1450:
1451: if (!overflow) {
1452: contents.setX(columnXOffsets[column]
1453: - columnXOffsets[startColumnIndex]
1454: + xOffset);
1455: contents.setY(rowY + yOffset);
1456:
1457: int rowCellHeight = contents.getPrintHeight();
1458: if (rowCellHeight > preparedRowHeight) {
1459: preparedRowHeight = rowCellHeight;
1460: }
1461: }
1462: }
1463:
1464: return overflow;
1465: }
1466:
1467: private boolean prepareRowHeader(int rowGroup, HeaderCell cell,
1468: int vSpan, int availableHeight) throws JRException {
1469: JRFillCrosstabRowGroup group = rowGroups[rowGroup];
1470: JRFillCellContents contents = cell.isTotal() ? group
1471: .getFillTotalHeader() : group.getFillHeader();
1472:
1473: if (contents.getWidth() <= 0 || contents.getHeight() <= 0) {
1474: return false;
1475: }
1476:
1477: int spanHeight = 0;
1478: int headerY = ((Integer) rowYs.get(rowIdx - vSpan + 1))
1479: .intValue();
1480: if (vSpan > 1) {
1481: spanHeight += ((Integer) rowYs.get(rowIdx)).intValue()
1482: - headerY;
1483: }
1484: int rowHeight = spanHeight + preparedRowHeight;
1485:
1486: boolean stretchContents = group.getPosition() == JRCellContents.POSITION_Y_STRETCH;
1487: int contentsHeight = stretchContents ? rowHeight : contents
1488: .getHeight();
1489:
1490: int cellAvailableStretch = availableHeight - headerY
1491: - contentsHeight;
1492: boolean headerOverflow = cellAvailableStretch < 0
1493: || rowHeight < contents.getHeight();
1494:
1495: if (!headerOverflow) {
1496: setCountVars(rowIdx + startRowIndex - vSpan + 1, -1);
1497: setGroupVariables(rowGroups, cell.getBucketValues());
1498:
1499: if (stretchContents) {
1500: contents = contents.getTransformedContents(contents
1501: .getWidth(), rowHeight,
1502: JRCellContents.POSITION_X_LEFT,
1503: JRCellContents.POSITION_Y_STRETCH);
1504: }
1505: contents = contents
1506: .getBoxContents(
1507: false,
1508: false,
1509: rowIdx + 1 == vSpan
1510: && (!printColumnHeaders || headerCell == null));
1511: contents.getWorkingClone();
1512:
1513: contents.evaluate(JRExpression.EVALUATION_DEFAULT);
1514: contents.prepare(cellAvailableStretch);
1515:
1516: preparedRow.add(contents);
1517:
1518: headerOverflow = contents.willOverflow();
1519:
1520: if (!headerOverflow) {
1521: contents.setX(rowHeadersXOffsets[rowGroup]);
1522: contents.setY(headerY + yOffset);
1523: contents.setVerticalSpan(vSpan);
1524:
1525: int rowCellHeight = contents.getPrintHeight()
1526: - spanHeight;
1527: if (rowCellHeight > preparedRowHeight) {
1528: preparedRowHeight = rowCellHeight;
1529: }
1530: }
1531: }
1532:
1533: if (headerOverflow) {
1534: removeFilledRows(vSpan - 1);
1535: }
1536:
1537: return headerOverflow;
1538: }
1539:
1540: private boolean prepareClosingRowHeader(int rowGroup,
1541: int availableHeight) throws JRException {
1542: if (rowGroup < rowGroups.length - 1
1543: && spanHeaders[rowGroup] != null
1544: && spanHeaders[rowGroup].getLevelSpan()
1545: + spanHeadersStart[rowGroup] == rowIdx
1546: + startRowIndex + 1) {
1547: HeaderCell cell = spanHeaders[rowGroup];
1548: int vSpan = cell.getLevelSpan();
1549: if (spanHeadersStart[rowGroup] < startRowIndex)//continuing from the prev page
1550: {
1551: vSpan += spanHeadersStart[rowGroup] - startRowIndex;
1552: }
1553: spanHeaders[rowGroup] = null;
1554:
1555: return prepareRowHeader(rowGroup, cell, vSpan,
1556: availableHeight);
1557: }
1558:
1559: return false;
1560: }
1561:
1562: private void removeExceedingSpanHeaders() {
1563: for (int j = rowGroups.length - 2; j >= 0; --j) {
1564: if (spanHeaders[j] != null
1565: && spanHeadersStart[j] >= rowIdx
1566: + startRowIndex) {
1567: spanHeaders[j] = null;
1568: }
1569: }
1570: }
1571:
1572: private void setBackSpanHeaders() {
1573: for (int j = rowGroups.length - 2; j >= 0
1574: && spanHeaders[j] == null; --j) {
1575: int spanIndex = getSpanIndex(rowIdx + startRowIndex, j,
1576: rowHeadersData);
1577:
1578: if (spanIndex >= 0) {
1579: spanHeaders[j] = rowHeadersData[j][spanIndex];
1580: spanHeadersStart[j] = spanIndex;
1581: }
1582: }
1583: }
1584:
1585: private void fillContinuingRowHeaders(int xOffset,
1586: int availableHeight) throws JRException {
1587: boolean done = false;
1588: breakCrosstab: do {
1589: removeExceedingSpanHeaders();
1590:
1591: if (!rowBreakable[rowIdx + startRowIndex]) {
1592: removeFilledRows(1);
1593: setBackSpanHeaders();
1594: continue;
1595: }
1596:
1597: initPreparedRow();
1598:
1599: //fill continuing headers
1600: for (int j = 0; j < rowGroups.length - 1; ++j) {
1601: if (spanHeaders[j] != null) {
1602: boolean headerOverflow = prepareContinuingRowHeader(
1603: j, availableHeight);
1604:
1605: if (headerOverflow) {
1606: releasePreparedRow();
1607: continue breakCrosstab;
1608: }
1609: }
1610: }
1611:
1612: if (!preparedRow.isEmpty()) {
1613: int lastRowHeight = ((Integer) rowYs.get(rowIdx))
1614: .intValue()
1615: - ((Integer) rowYs.get(rowIdx - 1))
1616: .intValue();
1617:
1618: if (preparedRowHeight > lastRowHeight)//need to stretch already filled row by refilling
1619: {
1620: refillLastRow(xOffset, availableHeight);
1621: } else {
1622: fillContinuingHeaders(lastRowHeight);
1623: }
1624: }
1625:
1626: done = true;
1627: } while (!done && rowIdx > 0);
1628: }
1629:
1630: private void fillContinuingHeaders(int lastRowHeight)
1631: throws JRException {
1632: int nextToLastHeaderY = ((Integer) rowYs.get(rowIdx - 1))
1633: .intValue();
1634: List lastPrintRow = getLastPrintRow();
1635:
1636: for (int j = 0; j < preparedRow.size(); ++j) {
1637: JRFillCellContents contents = (JRFillCellContents) preparedRow
1638: .get(j);
1639:
1640: int headerY = ((Integer) rowYs.get(rowIdx
1641: - contents.getVerticalSpan())).intValue();
1642:
1643: contents.stretchTo(nextToLastHeaderY - headerY
1644: + lastRowHeight);
1645: lastPrintRow.add(contents.fill());
1646: contents.releaseWorkingClone();
1647: }
1648: }
1649:
1650: private void refillLastRow(int xOffset, int availableHeight)
1651: throws JRException {
1652: removeFilledRows(1);
1653: setBackSpanHeaders();
1654:
1655: prepareRow(xOffset, availableHeight);
1656: fillRow();
1657:
1658: rowYs.add(new Integer(((Integer) rowYs.get(rowIdx))
1659: .intValue()
1660: + preparedRowHeight));
1661: ++rowIdx;
1662: }
1663:
1664: private boolean prepareContinuingRowHeader(int rowGroup,
1665: int availableHeight) throws JRException {
1666: HeaderCell cell = spanHeaders[rowGroup];
1667: int vSpan = rowIdx + startRowIndex
1668: - spanHeadersStart[rowGroup];
1669:
1670: if (spanHeadersStart[rowGroup] < startRowIndex)//continuing from the prev page
1671: {
1672: vSpan += spanHeadersStart[rowGroup] - startRowIndex;
1673: }
1674:
1675: int headerY = ((Integer) rowYs.get(rowIdx - vSpan))
1676: .intValue();
1677: int lastHeaderY = ((Integer) rowYs.get(rowIdx)).intValue();
1678: int headerHeight = lastHeaderY - headerY;
1679: int nextToLastHeaderY = ((Integer) rowYs.get(rowIdx - 1))
1680: .intValue();
1681: int stretchHeight = nextToLastHeaderY - headerY;
1682:
1683: JRFillCrosstabRowGroup group = rowGroups[rowGroup];
1684: JRFillCellContents contents = cell.isTotal() ? group
1685: .getFillTotalHeader() : group.getFillHeader();
1686:
1687: boolean stretchContents = group.getPosition() == JRCellContents.POSITION_Y_STRETCH;
1688: int contentsHeight = stretchContents ? headerHeight
1689: : contents.getHeight();
1690:
1691: int cellAvailableStretch = availableHeight - headerY
1692: - contentsHeight;
1693: boolean headerOverflow = cellAvailableStretch < 0
1694: || headerHeight < contents.getHeight();
1695: if (!headerOverflow) {
1696: setCountVars(rowIdx + startRowIndex - vSpan, -1);
1697: setGroupVariables(rowGroups, cell.getBucketValues());
1698:
1699: if (stretchContents) {
1700: contents = contents.getTransformedContents(contents
1701: .getWidth(), headerHeight,
1702: JRCellContents.POSITION_X_LEFT,
1703: JRCellContents.POSITION_Y_STRETCH);
1704: }
1705:
1706: contents = contents
1707: .getBoxContents(
1708: false,
1709: false,
1710: rowIdx == vSpan
1711: && (!printColumnHeaders || headerCell == null));
1712: contents.getWorkingClone();
1713:
1714: contents.evaluate(JRExpression.EVALUATION_DEFAULT);
1715: contents.prepare(cellAvailableStretch);
1716:
1717: preparedRow.add(contents);
1718:
1719: headerOverflow = contents.willOverflow();
1720:
1721: if (!headerOverflow) {
1722: contents.setX(rowHeadersXOffsets[rowGroup]);
1723: contents.setY(headerY + yOffset);
1724: contents.setVerticalSpan(vSpan);
1725:
1726: int rowHeight = contents.getPrintHeight()
1727: - stretchHeight;
1728: if (rowHeight > preparedRowHeight) {
1729: preparedRowHeight = rowHeight;
1730: }
1731: }
1732: }
1733:
1734: if (headerOverflow) {
1735: removeFilledRows(vSpan);
1736: }
1737:
1738: return headerOverflow;
1739: }
1740:
1741: protected void addPrintRow(List printRow) {
1742: printRows.add(printRow);
1743: }
1744:
1745: protected List getLastPrintRow() {
1746: return (List) printRows.get(printRows.size() - 1);
1747: }
1748:
1749: protected List getPrintElements() {
1750: List prints = new ArrayList();
1751:
1752: for (Iterator it = printRows.iterator(); it.hasNext();) {
1753: List rowPrints = (List) it.next();
1754: prints.addAll(rowPrints);
1755: }
1756:
1757: return prints;
1758: }
1759:
1760: protected void setGroupVariables(JRFillCrosstabGroup[] groups,
1761: Bucket[] bucketValues) {
1762: for (int i = 0; i < groups.length; i++) {
1763: Object value = null;
1764: if (bucketValues[i] != null
1765: && !bucketValues[i].isTotal()) {
1766: value = bucketValues[i].getValue();
1767: }
1768: groups[i].getFillVariable().setValue(value);
1769: }
1770: }
1771:
1772: protected void setMeasureVariables(CrosstabCell cell) {
1773: MeasureValue[] values = cell.getMesureValues();
1774: for (int i = 0; i < measures.length; i++) {
1775: Object value = measureValue(values, i);
1776: measures[i].getFillVariable().setValue(value);
1777: }
1778:
1779: MeasureValue[][][] totals = cell.getTotals();
1780: for (int row = 0; row <= rowGroups.length; row++) {
1781: for (int col = 0; col <= columnGroups.length; col++) {
1782: MeasureValue[] vals = totals[row][col];
1783: if (retrieveTotal[row][col]) {
1784: for (int m = 0; m < measures.length; m++) {
1785: JRFillVariable totalVar = totalVariables[row][col][m];
1786: Object value = measureValue(vals, m);
1787: totalVar.setValue(value);
1788: }
1789: }
1790: }
1791: }
1792: }
1793:
1794: protected Object measureValue(MeasureValue[] values,
1795: int measureIdx) {
1796: Object value;
1797: if (measures[measureIdx].getPercentageOfType() == JRCrosstabMeasure.PERCENTAGE_TYPE_GRAND_TOTAL) {
1798: if (values[measureIdx].isInitialized()) {
1799: value = values[measureIdx].getValue();
1800: } else {
1801: value = measures[measureIdx]
1802: .getPercentageCalculator()
1803: .calculatePercentage(values[measureIdx],
1804: grandTotals[measureIdx]);
1805: }
1806: } else {
1807: value = values[measureIdx].getValue();
1808: }
1809: return value;
1810: }
1811:
1812: protected void resetVariables() {
1813: for (int i = 0; i < rowGroups.length; i++) {
1814: rowGroups[i].getFillVariable().setValue(null);
1815: }
1816:
1817: for (int i = 0; i < columnGroups.length; i++) {
1818: columnGroups[i].getFillVariable().setValue(null);
1819: }
1820:
1821: for (int i = 0; i < measures.length; i++) {
1822: measures[i].getFillVariable().setValue(null);
1823: }
1824:
1825: for (int row = 0; row <= rowGroups.length; ++row) {
1826: for (int col = 0; col <= columnGroups.length; ++col) {
1827: if (retrieveTotal[row][col]) {
1828: for (int i = 0; i < measures.length; i++) {
1829: totalVariables[row][col][i].setValue(null);
1830: }
1831: }
1832: }
1833: }
1834: }
1835: }
1836:
1837: public int getColumnBreakOffset() {
1838: return parentCrosstab.getColumnBreakOffset();
1839: }
1840:
1841: public boolean isRepeatColumnHeaders() {
1842: return parentCrosstab.isRepeatColumnHeaders();
1843: }
1844:
1845: public boolean isRepeatRowHeaders() {
1846: return parentCrosstab.isRepeatRowHeaders();
1847: }
1848:
1849: public JRCrosstabCell[][] getCells() {
1850: return crossCells;
1851: }
1852:
1853: public JRCellContents getWhenNoDataCell() {
1854: return whenNoDataCell;
1855: }
1856:
1857: public JRCrosstabParameter[] getParameters() {
1858: return parameters;
1859: }
1860:
1861: public JRExpression getParametersMapExpression() {
1862: return parentCrosstab.getParametersMapExpression();
1863: }
1864:
1865: public JRElement getElementByKey(String elementKey) {
1866: return JRBaseCrosstab.getElementByKey(this , elementKey);
1867: }
1868:
1869: public JRCloneable createClone(JRFillCloneFactory factory) {
1870: //not needed
1871: return null;
1872: }
1873:
1874: public JRCellContents getHeaderCell() {
1875: return headerCell;
1876: }
1877:
1878: public JRVariable[] getVariables() {
1879: return variables;
1880: }
1881:
1882: public byte getRunDirection() {
1883: return parentCrosstab.getRunDirection();
1884: }
1885:
1886: public void setRunDirection(byte direction) {
1887: // nothing
1888: }
1889:
1890: }
|