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.crosstabs.design;
0029:
0030: import java.beans.PropertyChangeEvent;
0031: import java.beans.PropertyChangeListener;
0032: import java.io.IOException;
0033: import java.io.Serializable;
0034: import java.net.URLStreamHandlerFactory;
0035: import java.util.ArrayList;
0036: import java.util.HashMap;
0037: import java.util.Iterator;
0038: import java.util.List;
0039: import java.util.ListIterator;
0040: import java.util.Locale;
0041: import java.util.Map;
0042: import java.util.ResourceBundle;
0043: import java.util.TimeZone;
0044:
0045: import net.sf.jasperreports.crosstabs.JRCellContents;
0046: import net.sf.jasperreports.crosstabs.JRCrosstab;
0047: import net.sf.jasperreports.crosstabs.JRCrosstabBucket;
0048: import net.sf.jasperreports.crosstabs.JRCrosstabCell;
0049: import net.sf.jasperreports.crosstabs.JRCrosstabColumnGroup;
0050: import net.sf.jasperreports.crosstabs.JRCrosstabDataset;
0051: import net.sf.jasperreports.crosstabs.JRCrosstabGroup;
0052: import net.sf.jasperreports.crosstabs.JRCrosstabMeasure;
0053: import net.sf.jasperreports.crosstabs.JRCrosstabParameter;
0054: import net.sf.jasperreports.crosstabs.JRCrosstabRowGroup;
0055: import net.sf.jasperreports.crosstabs.base.JRBaseCrosstab;
0056: import net.sf.jasperreports.engine.JRAbstractObjectFactory;
0057: import net.sf.jasperreports.engine.JRChild;
0058: import net.sf.jasperreports.engine.JRConstants;
0059: import net.sf.jasperreports.engine.JRDefaultStyleProvider;
0060: import net.sf.jasperreports.engine.JRElement;
0061: import net.sf.jasperreports.engine.JRException;
0062: import net.sf.jasperreports.engine.JRExpression;
0063: import net.sf.jasperreports.engine.JRExpressionCollector;
0064: import net.sf.jasperreports.engine.JRParameter;
0065: import net.sf.jasperreports.engine.JRVariable;
0066: import net.sf.jasperreports.engine.design.JRDesignElement;
0067: import net.sf.jasperreports.engine.design.JRDesignVariable;
0068: import net.sf.jasperreports.engine.util.FormatFactory;
0069: import net.sf.jasperreports.engine.util.JRStyleResolver;
0070: import net.sf.jasperreports.engine.xml.JRXmlWriter;
0071:
0072: import org.apache.commons.collections.SequencedHashMap;
0073:
0074: /**
0075: * Design-time {@link net.sf.jasperreports.crosstabs.JRCrosstab crosstab} implementation.
0076: *
0077: * @author Lucian Chirita (lucianc@users.sourceforge.net)
0078: * @version $Id: JRDesignCrosstab.java 1741 2007-06-08 10:53:33Z lucianc $
0079: */
0080: public class JRDesignCrosstab extends JRDesignElement implements
0081: JRCrosstab {
0082: private static final long serialVersionUID = JRConstants.SERIAL_VERSION_UID;
0083:
0084: protected List parametersList;
0085: protected Map parametersMap;
0086: protected Map variablesList;
0087: protected JRExpression parametersMapExpression;
0088: protected JRDesignCrosstabDataset dataset;
0089: protected List rowGroups;
0090: protected List columnGroups;
0091: protected List measures;
0092: protected Map rowGroupsMap;
0093: protected Map columnGroupsMap;
0094: protected Map measuresMap;
0095: protected int columnBreakOffset = DEFAULT_COLUMN_BREAK_OFFSET;
0096: protected boolean repeatColumnHeaders = true;
0097: protected boolean repeatRowHeaders = true;
0098: protected byte runDirection;
0099: protected List cellsList;
0100: protected Map cellsMap;
0101: protected JRDesignCrosstabCell[][] crossCells;
0102: protected JRDesignCellContents whenNoDataCell;
0103: protected JRDesignCellContents headerCell;
0104:
0105: private class MeasureClassChangeListener implements
0106: PropertyChangeListener, Serializable {
0107: private static final long serialVersionUID = JRConstants.SERIAL_VERSION_UID;
0108:
0109: public void propertyChange(PropertyChangeEvent evt) {
0110: measureClassChanged((JRDesignCrosstabMeasure) evt
0111: .getSource(), (String) evt.getNewValue());
0112: }
0113: }
0114:
0115: private PropertyChangeListener measureClassChangeListener = new MeasureClassChangeListener();
0116:
0117: private static final Object[] BUILT_IN_PARAMETERS = new Object[] {
0118: JRParameter.REPORT_PARAMETERS_MAP, java.util.Map.class,
0119: JRParameter.REPORT_LOCALE, Locale.class,
0120: JRParameter.REPORT_RESOURCE_BUNDLE, ResourceBundle.class,
0121: JRParameter.REPORT_TIME_ZONE, TimeZone.class,
0122: JRParameter.REPORT_FORMAT_FACTORY, FormatFactory.class,
0123: JRParameter.REPORT_CLASS_LOADER, ClassLoader.class,
0124: JRParameter.REPORT_URL_HANDLER_FACTORY,
0125: URLStreamHandlerFactory.class };
0126:
0127: private static final Object[] BUILT_IN_VARIABLES = new Object[] {
0128: JRCrosstab.VARIABLE_ROW_COUNT, Integer.class,
0129: JRCrosstab.VARIABLE_COLUMN_COUNT, Integer.class };
0130:
0131: /**
0132: * Creates a new crosstab.
0133: *
0134: * @param defaultStyleProvider default style provider
0135: */
0136: public JRDesignCrosstab(JRDefaultStyleProvider defaultStyleProvider) {
0137: super (defaultStyleProvider);
0138:
0139: parametersList = new ArrayList();
0140: parametersMap = new HashMap();
0141:
0142: rowGroupsMap = new HashMap();
0143: rowGroups = new ArrayList();
0144: columnGroupsMap = new HashMap();
0145: columnGroups = new ArrayList();
0146: measuresMap = new HashMap();
0147: measures = new ArrayList();
0148:
0149: cellsMap = new HashMap();
0150: cellsList = new ArrayList();
0151:
0152: addBuiltinParameters();
0153:
0154: variablesList = new SequencedHashMap();
0155: addBuiltinVariables();
0156:
0157: dataset = new JRDesignCrosstabDataset();
0158: }
0159:
0160: private void addBuiltinParameters() {
0161: for (int i = 0; i < BUILT_IN_PARAMETERS.length; i++) {
0162: JRDesignCrosstabParameter parameter = new JRDesignCrosstabParameter();
0163: parameter.setName((String) BUILT_IN_PARAMETERS[i++]);
0164: parameter.setValueClass((Class) BUILT_IN_PARAMETERS[i]);
0165: parameter.setSystemDefined(true);
0166: try {
0167: addParameter(parameter);
0168: } catch (JRException e) {
0169: // never reached
0170: }
0171: }
0172: }
0173:
0174: private void addBuiltinVariables() {
0175: for (int i = 0; i < BUILT_IN_VARIABLES.length; ++i) {
0176: JRDesignVariable variable = new JRDesignVariable();
0177: variable.setName((String) BUILT_IN_VARIABLES[i]);
0178: variable.setValueClass((Class) BUILT_IN_VARIABLES[++i]);
0179: variable.setCalculation(JRVariable.CALCULATION_SYSTEM);
0180: variable.setSystemDefined(true);
0181: addVariable(variable);
0182: }
0183: }
0184:
0185: /**
0186: * Creates a new crosstab.
0187: */
0188: public JRDesignCrosstab() {
0189: this (null);
0190: }
0191:
0192: /**
0193: * The ID of the crosstab is only generated at compile time.
0194: */
0195: public int getId() {
0196: return 0;
0197: }
0198:
0199: public JRCrosstabDataset getDataset() {
0200: return dataset;
0201: }
0202:
0203: /**
0204: * Returns the crosstab dataset object to be used for report designing.
0205: *
0206: * @return the crosstab dataset design object
0207: */
0208: public JRDesignCrosstabDataset getDesignDataset() {
0209: return dataset;
0210: }
0211:
0212: public JRCrosstabRowGroup[] getRowGroups() {
0213: JRCrosstabRowGroup[] groups = new JRCrosstabRowGroup[rowGroups
0214: .size()];
0215: rowGroups.toArray(groups);
0216: return groups;
0217: }
0218:
0219: public JRCrosstabColumnGroup[] getColumnGroups() {
0220: JRCrosstabColumnGroup[] groups = new JRCrosstabColumnGroup[columnGroups
0221: .size()];
0222: columnGroups.toArray(groups);
0223: return groups;
0224: }
0225:
0226: public JRCrosstabMeasure[] getMeasures() {
0227: JRCrosstabMeasure[] measureArray = new JRCrosstabMeasure[measures
0228: .size()];
0229: measures.toArray(measureArray);
0230: return measureArray;
0231: }
0232:
0233: public void collectExpressions(JRExpressionCollector collector) {
0234: collector.collect(this );
0235: }
0236:
0237: public JRChild getCopy(JRAbstractObjectFactory factory) {
0238: return factory.getCrosstab(this );
0239: }
0240:
0241: public void writeXml(JRXmlWriter writer) throws IOException {
0242: writer.writeCrosstab(this );
0243: }
0244:
0245: /**
0246: * Sets the crosstab input dataset.
0247: *
0248: * @param dataset the dataset
0249: * @see JRCrosstab#getDataset()
0250: */
0251: public void setDataset(JRDesignCrosstabDataset dataset) {
0252: this .dataset = dataset;
0253: }
0254:
0255: /**
0256: * Adds a row group.
0257: * <p>
0258: * This group will be a sub group of the last row group, if any.
0259: *
0260: * @param group the group
0261: * @throws JRException
0262: * @see JRCrosstab#getRowGroups()
0263: */
0264: public void addRowGroup(JRDesignCrosstabRowGroup group)
0265: throws JRException {
0266: String groupName = group.getName();
0267: if (rowGroupsMap.containsKey(groupName)
0268: || columnGroupsMap.containsKey(groupName)
0269: || measuresMap.containsKey(groupName)) {
0270: throw new JRException(
0271: "A group or measure having the same name already exists in the crosstab.");
0272: }
0273:
0274: rowGroupsMap.put(groupName, new Integer(rowGroups.size()));
0275: rowGroups.add(group);
0276:
0277: addRowGroupVars(group);
0278: }
0279:
0280: protected void addRowGroupVars(JRDesignCrosstabRowGroup rowGroup) {
0281: addVariable(rowGroup.getVariable());
0282:
0283: for (Iterator measureIt = measures.iterator(); measureIt
0284: .hasNext();) {
0285: JRCrosstabMeasure measure = (JRCrosstabMeasure) measureIt
0286: .next();
0287: addTotalVar(measure, rowGroup, null);
0288:
0289: for (Iterator colIt = columnGroups.iterator(); colIt
0290: .hasNext();) {
0291: JRCrosstabColumnGroup colGroup = (JRCrosstabColumnGroup) colIt
0292: .next();
0293: addTotalVar(measure, rowGroup, colGroup);
0294: }
0295: }
0296: }
0297:
0298: /**
0299: * Adds a column group.
0300: * <p>
0301: * This group will be a sub group of the last column group, if any.
0302: *
0303: * @param group the group
0304: * @throws JRException
0305: * @see JRCrosstab#getColumnGroups()
0306: */
0307: public void addColumnGroup(JRDesignCrosstabColumnGroup group)
0308: throws JRException {
0309: String groupName = group.getName();
0310: if (rowGroupsMap.containsKey(groupName)
0311: || columnGroupsMap.containsKey(groupName)
0312: || measuresMap.containsKey(groupName)) {
0313: throw new JRException(
0314: "A group or measure having the same name already exists in the crosstab.");
0315: }
0316:
0317: columnGroupsMap
0318: .put(groupName, new Integer(columnGroups.size()));
0319: columnGroups.add(group);
0320:
0321: addColGroupVars(group);
0322: }
0323:
0324: protected void addColGroupVars(JRDesignCrosstabColumnGroup colGroup) {
0325: addVariable(colGroup.getVariable());
0326:
0327: for (Iterator measureIt = measures.iterator(); measureIt
0328: .hasNext();) {
0329: JRCrosstabMeasure measure = (JRCrosstabMeasure) measureIt
0330: .next();
0331: addTotalVar(measure, null, colGroup);
0332:
0333: for (Iterator rowIt = rowGroups.iterator(); rowIt.hasNext();) {
0334: JRCrosstabRowGroup rowGroup = (JRCrosstabRowGroup) rowIt
0335: .next();
0336: addTotalVar(measure, rowGroup, colGroup);
0337: }
0338: }
0339: }
0340:
0341: /**
0342: * Adds a measure to the crosstab.
0343: *
0344: * @param measure the measure
0345: * @throws JRException
0346: * @see JRCrosstab#getMeasures()
0347: */
0348: public void addMeasure(JRDesignCrosstabMeasure measure)
0349: throws JRException {
0350: String measureName = measure.getName();
0351: if (rowGroupsMap.containsKey(measureName)
0352: || columnGroupsMap.containsKey(measureName)
0353: || measuresMap.containsKey(measureName)) {
0354: throw new JRException(
0355: "A group or measure having the same name already exists in the crosstab.");
0356: }
0357:
0358: measure.addPropertyChangeListener(
0359: JRDesignCrosstabMeasure.PROPERTY_VALUE_CLASS,
0360: measureClassChangeListener);
0361:
0362: measuresMap.put(measureName, new Integer(measures.size()));
0363: measures.add(measure);
0364:
0365: addMeasureVars(measure);
0366: }
0367:
0368: protected void addMeasureVars(JRDesignCrosstabMeasure measure) {
0369: addVariable(measure.getVariable());
0370:
0371: for (Iterator colIt = columnGroups.iterator(); colIt.hasNext();) {
0372: JRCrosstabColumnGroup colGroup = (JRCrosstabColumnGroup) colIt
0373: .next();
0374: addTotalVar(measure, null, colGroup);
0375: }
0376:
0377: for (Iterator rowIt = rowGroups.iterator(); rowIt.hasNext();) {
0378: JRCrosstabRowGroup rowGroup = (JRCrosstabRowGroup) rowIt
0379: .next();
0380: addTotalVar(measure, rowGroup, null);
0381:
0382: for (Iterator colIt = columnGroups.iterator(); colIt
0383: .hasNext();) {
0384: JRCrosstabColumnGroup colGroup = (JRCrosstabColumnGroup) colIt
0385: .next();
0386: addTotalVar(measure, rowGroup, colGroup);
0387: }
0388: }
0389: }
0390:
0391: protected void addTotalVar(JRCrosstabMeasure measure,
0392: JRCrosstabRowGroup rowGroup, JRCrosstabColumnGroup colGroup) {
0393: JRDesignVariable var = new JRDesignVariable();
0394: var.setCalculation(JRVariable.CALCULATION_SYSTEM);
0395: var.setSystemDefined(true);
0396: var.setName(getTotalVariableName(measure, rowGroup, colGroup));
0397: var.setValueClassName(measure.getValueClassName());
0398: addVariable(var);
0399: }
0400:
0401: protected void removeTotalVar(JRCrosstabMeasure measure,
0402: JRCrosstabRowGroup rowGroup, JRCrosstabColumnGroup colGroup) {
0403: String varName = getTotalVariableName(measure, rowGroup,
0404: colGroup);
0405: removeVariable(varName);
0406: }
0407:
0408: public static String getTotalVariableName(
0409: JRCrosstabMeasure measure, JRCrosstabRowGroup rowGroup,
0410: JRCrosstabColumnGroup colGroup) {
0411: StringBuffer name = new StringBuffer();
0412: name.append(measure.getName());
0413: if (rowGroup != null) {
0414: name.append('_');
0415: name.append(rowGroup.getName());
0416: }
0417: if (colGroup != null) {
0418: name.append('_');
0419: name.append(colGroup.getName());
0420: }
0421: name.append("_ALL");
0422: return name.toString();
0423: }
0424:
0425: /**
0426: * Removes a row group.
0427: *
0428: * @param groupName the group name
0429: * @return the removed group
0430: */
0431: public JRCrosstabRowGroup removeRowGroup(String groupName) {
0432: JRCrosstabRowGroup removed = null;
0433:
0434: Integer idx = (Integer) rowGroupsMap.remove(groupName);
0435: if (idx != null) {
0436: removed = (JRCrosstabRowGroup) rowGroups.remove(idx
0437: .intValue());
0438:
0439: for (ListIterator it = rowGroups.listIterator(idx
0440: .intValue()); it.hasNext();) {
0441: JRCrosstabRowGroup group = (JRCrosstabRowGroup) it
0442: .next();
0443: rowGroupsMap.put(group.getName(), new Integer(it
0444: .previousIndex()));
0445: }
0446:
0447: for (Iterator it = cellsList.iterator(); it.hasNext();) {
0448: JRCrosstabCell cell = (JRCrosstabCell) it.next();
0449: String rowTotalGroup = cell.getRowTotalGroup();
0450: if (rowTotalGroup != null
0451: && rowTotalGroup.equals(groupName)) {
0452: it.remove();
0453: cellsMap.remove(new Pair(rowTotalGroup, cell
0454: .getColumnTotalGroup()));
0455: }
0456: }
0457:
0458: removeRowGroupVars(removed);
0459: }
0460:
0461: return removed;
0462: }
0463:
0464: protected void removeRowGroupVars(JRCrosstabRowGroup rowGroup) {
0465: removeVariable(rowGroup.getVariable());
0466:
0467: for (Iterator measureIt = measures.iterator(); measureIt
0468: .hasNext();) {
0469: JRCrosstabMeasure measure = (JRCrosstabMeasure) measureIt
0470: .next();
0471: removeTotalVar(measure, rowGroup, null);
0472:
0473: for (Iterator colIt = columnGroups.iterator(); colIt
0474: .hasNext();) {
0475: JRCrosstabColumnGroup colGroup = (JRCrosstabColumnGroup) colIt
0476: .next();
0477: removeTotalVar(measure, rowGroup, colGroup);
0478: }
0479: }
0480: }
0481:
0482: /**
0483: * Removes a row group.
0484: *
0485: * @param group the group to be removed
0486: * @return the removed group
0487: */
0488: public JRCrosstabRowGroup removeRowGroup(JRCrosstabRowGroup group) {
0489: return removeRowGroup(group.getName());
0490: }
0491:
0492: /**
0493: * Removes a column group.
0494: *
0495: * @param groupName the group name
0496: * @return the removed group
0497: */
0498: public JRCrosstabColumnGroup removeColumnGroup(String groupName) {
0499: JRCrosstabColumnGroup removed = null;
0500:
0501: Integer idx = (Integer) columnGroupsMap.remove(groupName);
0502: if (idx != null) {
0503: removed = (JRCrosstabColumnGroup) columnGroups.remove(idx
0504: .intValue());
0505:
0506: for (ListIterator it = columnGroups.listIterator(idx
0507: .intValue()); it.hasNext();) {
0508: JRCrosstabColumnGroup group = (JRCrosstabColumnGroup) it
0509: .next();
0510: columnGroupsMap.put(group.getName(), new Integer(it
0511: .previousIndex()));
0512: }
0513:
0514: for (Iterator it = cellsList.iterator(); it.hasNext();) {
0515: JRCrosstabCell cell = (JRCrosstabCell) it.next();
0516: String columnTotalGroup = cell.getColumnTotalGroup();
0517: if (columnTotalGroup != null
0518: && columnTotalGroup.equals(groupName)) {
0519: it.remove();
0520: cellsMap.remove(new Pair(cell.getRowTotalGroup(),
0521: columnTotalGroup));
0522: }
0523: }
0524:
0525: removeColGroupVars(removed);
0526: }
0527:
0528: return removed;
0529: }
0530:
0531: protected void removeColGroupVars(JRCrosstabColumnGroup colGroup) {
0532: removeVariable(colGroup.getVariable());
0533:
0534: for (Iterator measureIt = measures.iterator(); measureIt
0535: .hasNext();) {
0536: JRCrosstabMeasure measure = (JRCrosstabMeasure) measureIt
0537: .next();
0538: removeTotalVar(measure, null, colGroup);
0539:
0540: for (Iterator rowIt = rowGroups.iterator(); rowIt.hasNext();) {
0541: JRCrosstabRowGroup rowGroup = (JRCrosstabRowGroup) rowIt
0542: .next();
0543: removeTotalVar(measure, rowGroup, colGroup);
0544: }
0545: }
0546: }
0547:
0548: /**
0549: * Removes a column group.
0550: *
0551: * @param group the group
0552: * @return the removed group
0553: */
0554: public JRCrosstabColumnGroup removeColumnGroup(
0555: JRCrosstabColumnGroup group) {
0556: return removeColumnGroup(group.getName());
0557: }
0558:
0559: /**
0560: * Removes a measure.
0561: *
0562: * @param measureName the measure name
0563: * @return the removed measure
0564: */
0565: public JRCrosstabMeasure removeMeasure(String measureName) {
0566: JRDesignCrosstabMeasure removed = null;
0567:
0568: Integer idx = (Integer) measuresMap.remove(measureName);
0569: if (idx != null) {
0570: removed = (JRDesignCrosstabMeasure) measures.remove(idx
0571: .intValue());
0572:
0573: for (ListIterator it = measures
0574: .listIterator(idx.intValue()); it.hasNext();) {
0575: JRCrosstabMeasure group = (JRCrosstabMeasure) it.next();
0576: measuresMap.put(group.getName(), new Integer(it
0577: .previousIndex()));
0578: }
0579:
0580: removeMeasureVars(removed);
0581:
0582: removed.removePropertyChangeListener(
0583: JRDesignCrosstabMeasure.PROPERTY_VALUE_CLASS,
0584: measureClassChangeListener);
0585: }
0586:
0587: return removed;
0588: }
0589:
0590: protected void removeMeasureVars(JRDesignCrosstabMeasure measure) {
0591: removeVariable(measure.getVariable());
0592:
0593: for (Iterator colIt = columnGroups.iterator(); colIt.hasNext();) {
0594: JRCrosstabColumnGroup colGroup = (JRCrosstabColumnGroup) colIt
0595: .next();
0596: removeTotalVar(measure, null, colGroup);
0597: }
0598:
0599: for (Iterator rowIt = rowGroups.iterator(); rowIt.hasNext();) {
0600: JRCrosstabRowGroup rowGroup = (JRCrosstabRowGroup) rowIt
0601: .next();
0602: removeTotalVar(measure, rowGroup, null);
0603:
0604: for (Iterator colIt = columnGroups.iterator(); colIt
0605: .hasNext();) {
0606: JRCrosstabColumnGroup colGroup = (JRCrosstabColumnGroup) colIt
0607: .next();
0608: removeTotalVar(measure, rowGroup, colGroup);
0609: }
0610: }
0611: }
0612:
0613: /**
0614: * Removes a measure.
0615: *
0616: * @param measure the measure
0617: * @return the removed measure
0618: */
0619: public JRCrosstabMeasure removeMeasure(JRCrosstabMeasure measure) {
0620: return removeMeasure(measure.getName());
0621: }
0622:
0623: public boolean isRepeatColumnHeaders() {
0624: return repeatColumnHeaders;
0625: }
0626:
0627: /**
0628: * Sets the repeat column headers flag.
0629: *
0630: * @param repeatColumnHeaders whether to repeat the column headers on row breaks
0631: * @see JRCrosstab#isRepeatColumnHeaders()
0632: */
0633: public void setRepeatColumnHeaders(boolean repeatColumnHeaders) {
0634: this .repeatColumnHeaders = repeatColumnHeaders;
0635: }
0636:
0637: public boolean isRepeatRowHeaders() {
0638: return repeatRowHeaders;
0639: }
0640:
0641: /**
0642: * Sets the repeat row headers flag.
0643: *
0644: * @param repeatRowHeaders whether to repeat the row headers on column breaks
0645: * @see JRCrosstab#isRepeatRowHeaders()
0646: */
0647: public void setRepeatRowHeaders(boolean repeatRowHeaders) {
0648: this .repeatRowHeaders = repeatRowHeaders;
0649: }
0650:
0651: public JRCrosstabCell[][] getCells() {
0652: return crossCells;
0653: }
0654:
0655: /**
0656: * Returns the data cells list.
0657: *
0658: * @return the data cells list
0659: * @see #addCell(JRDesignCrosstabCell)
0660: */
0661: public List getCellsList() {
0662: return cellsList;
0663: }
0664:
0665: /**
0666: * Adds a data cell to the crosstab.
0667: *
0668: * @param cell the cell
0669: * @throws JRException
0670: * @see JRCrosstab#getCells()
0671: */
0672: public void addCell(JRDesignCrosstabCell cell) throws JRException {
0673: String rowTotalGroup = cell.getRowTotalGroup();
0674: if (rowTotalGroup != null
0675: && !rowGroupsMap.containsKey(rowTotalGroup)) {
0676: throw new JRException("Row group " + rowTotalGroup
0677: + " does not exist.");
0678: }
0679:
0680: String columnTotalGroup = cell.getColumnTotalGroup();
0681: if (columnTotalGroup != null
0682: && !columnGroupsMap.containsKey(columnTotalGroup)) {
0683: throw new JRException("Row group " + columnTotalGroup
0684: + " does not exist.");
0685: }
0686:
0687: Object cellKey = new Pair(rowTotalGroup, columnTotalGroup);
0688: if (cellsMap.containsKey(cellKey)) {
0689: throw new JRException("Duplicate cell in crosstab.");
0690: }
0691:
0692: cellsMap.put(cellKey, cell);
0693: cellsList.add(cell);
0694: }
0695:
0696: /**
0697: * Removes a data cell.
0698: *
0699: * @param rowTotalGroup the cell's total row group
0700: * @param columnTotalGroup the cell's total column group
0701: * @return the removed cell
0702: */
0703: public JRCrosstabCell removeCell(String rowTotalGroup,
0704: String columnTotalGroup) {
0705: Object cellKey = new Pair(rowTotalGroup, columnTotalGroup);
0706:
0707: JRCrosstabCell cell = (JRCrosstabCell) cellsMap.remove(cellKey);
0708: if (cell != null) {
0709: cellsList.remove(cell);
0710: }
0711:
0712: return cell;
0713: }
0714:
0715: /**
0716: * Removes a data cell.
0717: *
0718: * @param cell the cell to be removed
0719: * @return the removed cell
0720: */
0721: public JRCrosstabCell removeCell(JRCrosstabCell cell) {
0722: Object cellKey = new Pair(cell.getRowTotalGroup(), cell
0723: .getColumnTotalGroup());
0724: JRCrosstabCell removedCell = (JRCrosstabCell) cellsMap
0725: .remove(cellKey);
0726: if (removedCell != null) {
0727: cellsList.remove(removedCell);
0728: }
0729:
0730: return removedCell;
0731: }
0732:
0733: private static class Pair implements Serializable {
0734: private static final long serialVersionUID = 1L;
0735:
0736: final Object o1;
0737: final Object o2;
0738:
0739: Pair(Object o1, Object o2) {
0740: this .o1 = o1;
0741: this .o2 = o2;
0742: }
0743:
0744: public boolean equals(Object o) {
0745: if (o == this ) {
0746: return true;
0747: }
0748:
0749: if (o == null || !(o instanceof Pair)) {
0750: return false;
0751: }
0752:
0753: Pair p = (Pair) o;
0754:
0755: return (p.o1 == null ? o1 == null : (o1 != null && p.o1
0756: .equals(o1)))
0757: && (p.o2 == null ? o2 == null : (o2 != null && p.o2
0758: .equals(o2)));
0759: }
0760:
0761: public int hashCode() {
0762: int hash = o1 == null ? 0 : o1.hashCode();
0763:
0764: hash *= 31;
0765:
0766: hash += o2 == null ? 0 : o2.hashCode();
0767:
0768: return hash;
0769: }
0770: }
0771:
0772: public JRCrosstabParameter[] getParameters() {
0773: JRCrosstabParameter[] parameters = new JRCrosstabParameter[parametersList
0774: .size()];
0775: parametersList.toArray(parameters);
0776: return parameters;
0777: }
0778:
0779: /**
0780: * Returns the paremeters list.
0781: *
0782: * @return the paremeters list
0783: */
0784: public List getParametersList() {
0785: return parametersList;
0786: }
0787:
0788: /**
0789: * Returns the parameters indexed by names.
0790: *
0791: * @return the parameters indexed by names
0792: */
0793: public Map getParametersMap() {
0794: return parametersMap;
0795: }
0796:
0797: public JRExpression getParametersMapExpression() {
0798: return parametersMapExpression;
0799: }
0800:
0801: /**
0802: * Adds a parameter to the crosstab.
0803: *
0804: * @param parameter the parameter
0805: * @throws JRException
0806: * @see JRCrosstab#getMeasures()
0807: */
0808: public void addParameter(JRCrosstabParameter parameter)
0809: throws JRException {
0810: if (parametersMap.containsKey(parameter.getName())) {
0811: if (parametersMap.containsKey(parameter.getName())) {
0812: throw new JRException(
0813: "Duplicate declaration of parameter : "
0814: + parameter.getName());
0815: }
0816: }
0817:
0818: parametersMap.put(parameter.getName(), parameter);
0819: parametersList.add(parameter);
0820: }
0821:
0822: /**
0823: * Removes a parameter.
0824: *
0825: * @param parameterName the name of the parameter to be removed
0826: * @return the removed parameter
0827: */
0828: public JRCrosstabParameter removeParameter(String parameterName) {
0829: JRCrosstabParameter param = (JRCrosstabParameter) parametersMap
0830: .remove(parameterName);
0831:
0832: if (param != null) {
0833: parametersList.remove(param);
0834: }
0835:
0836: return param;
0837: }
0838:
0839: /**
0840: * Removes a parameter.
0841: *
0842: * @param parameter the parameter to be removed
0843: * @return the removed parameter
0844: */
0845: public JRCrosstabParameter removeParameter(
0846: JRCrosstabParameter parameter) {
0847: return removeParameter(parameter.getName());
0848: }
0849:
0850: /**
0851: * Sets the parameters map expression.
0852: *
0853: * @param expression the parameters map expression
0854: * @see JRCrosstab#getParametersMapExpression()
0855: */
0856: public void setParametersMapExpression(JRExpression expression) {
0857: this .parametersMapExpression = expression;
0858: }
0859:
0860: /**
0861: * Returns the variables of this crosstab indexed by name.
0862: *
0863: * @return the variables of this crosstab indexed by name
0864: */
0865: public Map getVariablesMap() {
0866: JRVariable[] variables = getVariables();
0867: Map variablesMap = new HashMap();
0868:
0869: for (int i = 0; i < variables.length; i++) {
0870: variablesMap.put(variables[i].getName(), variables[i]);
0871: }
0872:
0873: return variablesMap;
0874: }
0875:
0876: /**
0877: * Returns the list of variables created for this crosstab.
0878: *
0879: * @return the list of variables created for this crosstab
0880: * @see JRCrosstabGroup#getVariable()
0881: * @see JRCrosstabMeasure#getVariable()
0882: * @see JRCrosstab#VARIABLE_ROW_COUNT
0883: * @see JRCrosstab#VARIABLE_COLUMN_COUNT
0884: */
0885: public JRVariable[] getVariables() {
0886: JRVariable[] variables = new JRVariable[variablesList.size()];
0887: variablesList.values().toArray(variables);
0888: return variables;
0889: }
0890:
0891: public int getColumnBreakOffset() {
0892: return columnBreakOffset;
0893: }
0894:
0895: /**
0896: * Sets the column break offset.
0897: *
0898: * @param columnBreakOffset the offset
0899: * @see JRCrosstab#getColumnBreakOffset()
0900: */
0901: public void setColumnBreakOffset(int columnBreakOffset) {
0902: this .columnBreakOffset = columnBreakOffset;
0903: }
0904:
0905: /**
0906: * Performs all the calculations required for report compilation.
0907: */
0908: public void preprocess() {
0909: setGroupVariablesClass(rowGroups);
0910: setGroupVariablesClass(columnGroups);
0911:
0912: calculateSizes();
0913: }
0914:
0915: protected void setGroupVariablesClass(List groups) {
0916: for (Iterator it = groups.iterator(); it.hasNext();) {
0917: JRDesignCrosstabGroup group = (JRDesignCrosstabGroup) it
0918: .next();
0919: JRCrosstabBucket bucket = group.getBucket();
0920: if (bucket != null) {
0921: JRExpression expression = bucket.getExpression();
0922: if (expression != null) {
0923: group.designVariable.setValueClassName(expression
0924: .getValueClassName());
0925: }
0926: }
0927: }
0928: }
0929:
0930: protected void calculateSizes() {
0931: setWhenNoDataCellSize();
0932:
0933: createCellMatrix();
0934:
0935: int rowHeadersWidth = calculateRowHeadersSizes();
0936: int colHeadersHeight = calculateColumnHeadersSizes();
0937:
0938: if (headerCell != null) {
0939: headerCell.setWidth(rowHeadersWidth);
0940: headerCell.setHeight(colHeadersHeight);
0941: }
0942: }
0943:
0944: protected void setWhenNoDataCellSize() {
0945: if (whenNoDataCell != null) {
0946: whenNoDataCell.setWidth(getWidth());
0947: whenNoDataCell.setHeight(getHeight());
0948: }
0949: }
0950:
0951: protected void createCellMatrix() {
0952: crossCells = new JRDesignCrosstabCell[rowGroups.size() + 1][columnGroups
0953: .size() + 1];
0954: for (Iterator it = cellsList.iterator(); it.hasNext();) {
0955: JRDesignCrosstabCell crosstabCell = (JRDesignCrosstabCell) it
0956: .next();
0957: JRDesignCellContents contents = (JRDesignCellContents) crosstabCell
0958: .getContents();
0959:
0960: String rowTotalGroup = crosstabCell.getRowTotalGroup();
0961: int rowGroupIndex = rowTotalGroup == null ? rowGroupIndex = rowGroups
0962: .size()
0963: : ((Integer) rowGroupsMap.get(rowTotalGroup))
0964: .intValue();
0965:
0966: Integer cellWidth = crosstabCell.getWidth();
0967: if (cellWidth != null) {
0968: contents.setWidth(cellWidth.intValue());
0969: }
0970:
0971: String columnTotalGroup = crosstabCell
0972: .getColumnTotalGroup();
0973: int columnGroupIndex = columnTotalGroup == null ? columnGroupIndex = columnGroups
0974: .size()
0975: : ((Integer) columnGroupsMap.get(columnTotalGroup))
0976: .intValue();
0977: Integer cellHeight = crosstabCell.getHeight();
0978: if (cellHeight != null) {
0979: contents.setHeight(cellHeight.intValue());
0980: }
0981:
0982: crossCells[rowGroupIndex][columnGroupIndex] = crosstabCell;
0983: }
0984:
0985: inheritCells();
0986: }
0987:
0988: protected JRDesignCrosstabRowGroup getRowGroup(int rowGroupIndex) {
0989: return (JRDesignCrosstabRowGroup) rowGroups.get(rowGroupIndex);
0990: }
0991:
0992: protected JRDesignCrosstabColumnGroup getColumnGroup(
0993: int columnGroupIndex) {
0994: return (JRDesignCrosstabColumnGroup) columnGroups
0995: .get(columnGroupIndex);
0996: }
0997:
0998: protected void inheritCells() {
0999: for (int i = rowGroups.size(); i >= 0; --i) {
1000: for (int j = columnGroups.size(); j >= 0; --j) {
1001: boolean used = (i == rowGroups.size() || getRowGroup(i)
1002: .hasTotal())
1003: && (j == columnGroups.size() || getColumnGroup(
1004: j).hasTotal());
1005:
1006: if (used) {
1007: if (crossCells[i][j] == null) {
1008: inheritCell(i, j);
1009:
1010: if (crossCells[i][j] == null) {
1011: crossCells[i][j] = emptyCell(i, j);
1012: inheritCellSize(i, j);
1013: }
1014: } else {
1015: inheritCellSize(i, j);
1016: }
1017: } else {
1018: crossCells[i][j] = null;
1019: }
1020: }
1021: }
1022: }
1023:
1024: private JRDesignCrosstabCell emptyCell(int i, int j) {
1025: JRDesignCrosstabCell emptyCell = new JRDesignCrosstabCell();
1026: if (i < rowGroups.size()) {
1027: emptyCell.setRowTotalGroup(((JRCrosstabRowGroup) rowGroups
1028: .get(i)).getName());
1029: }
1030: if (j < columnGroups.size()) {
1031: emptyCell
1032: .setColumnTotalGroup(((JRCrosstabColumnGroup) columnGroups
1033: .get(j)).getName());
1034: }
1035: return emptyCell;
1036: }
1037:
1038: protected void inheritCellSize(int i, int j) {
1039: JRDesignCrosstabCell cell = crossCells[i][j];
1040:
1041: JRDesignCellContents contents = (JRDesignCellContents) cell
1042: .getContents();
1043:
1044: if (contents.getWidth() == JRCellContents.NOT_CALCULATED) {
1045: if (i < rowGroups.size()) {
1046: JRDesignCrosstabCell rowCell = crossCells[rowGroups
1047: .size()][j];
1048: if (rowCell != null) {
1049: contents.setWidth(rowCell.getContents().getWidth());
1050: }
1051: } else {
1052: for (int k = j + 1; k <= columnGroups.size(); ++k) {
1053: if (crossCells[i][k] != null) {
1054: contents.setWidth(crossCells[i][k]
1055: .getContents().getWidth());
1056: break;
1057: }
1058: }
1059: }
1060: }
1061:
1062: if (contents.getHeight() == JRCellContents.NOT_CALCULATED) {
1063: if (j < columnGroups.size()) {
1064: JRDesignCrosstabCell colCell = crossCells[i][columnGroups
1065: .size()];
1066: if (colCell != null) {
1067: contents.setHeight(colCell.getContents()
1068: .getHeight());
1069: }
1070: } else {
1071: for (int k = i + 1; k <= rowGroups.size(); ++k) {
1072: if (crossCells[k][j] != null) {
1073: contents.setHeight(crossCells[k][j]
1074: .getContents().getHeight());
1075: }
1076: }
1077: }
1078: }
1079: }
1080:
1081: protected void inheritCell(int i, int j) {
1082: JRDesignCrosstabCell inheritedCell = null;
1083:
1084: if (j < columnGroups.size()) {
1085: JRDesignCrosstabCell colCell = crossCells[rowGroups.size()][j];
1086: JRDesignCellContents colContents = colCell == null ? null
1087: : (JRDesignCellContents) colCell.getContents();
1088: for (int k = j + 1; inheritedCell == null
1089: && k <= columnGroups.size(); ++k) {
1090: JRDesignCrosstabCell cell = crossCells[i][k];
1091: if (cell != null) {
1092: JRDesignCellContents contents = (JRDesignCellContents) cell
1093: .getContents();
1094: if (colContents == null
1095: || contents.getWidth() == colContents
1096: .getWidth()) {
1097: inheritedCell = cell;
1098: }
1099: }
1100: }
1101: }
1102:
1103: if (inheritedCell == null && i < rowGroups.size()) {
1104: JRDesignCrosstabCell rowCell = crossCells[i][columnGroups
1105: .size()];
1106: JRDesignCellContents rowContents = rowCell == null ? null
1107: : (JRDesignCellContents) rowCell.getContents();
1108: for (int k = i + 1; inheritedCell == null
1109: && k <= rowGroups.size(); ++k) {
1110: JRDesignCrosstabCell cell = crossCells[k][j];
1111: if (cell != null) {
1112: JRDesignCellContents contents = (JRDesignCellContents) cell
1113: .getContents();
1114: if (rowContents == null
1115: || contents.getHeight() == rowContents
1116: .getHeight()) {
1117: inheritedCell = cell;
1118: }
1119: }
1120: }
1121: }
1122:
1123: crossCells[i][j] = inheritedCell;
1124: }
1125:
1126: protected int calculateRowHeadersSizes() {
1127: int widthSum = 0;
1128: for (int i = rowGroups.size() - 1, heightSum = 0; i >= 0; --i) {
1129: JRDesignCrosstabRowGroup group = (JRDesignCrosstabRowGroup) rowGroups
1130: .get(i);
1131:
1132: widthSum += group.getWidth();
1133:
1134: JRDesignCrosstabCell cell = crossCells[i + 1][columnGroups
1135: .size()];
1136: if (cell != null) {
1137: heightSum += cell.getContents().getHeight();
1138: }
1139:
1140: JRDesignCellContents header = (JRDesignCellContents) group
1141: .getHeader();
1142: header.setHeight(heightSum);
1143: header.setWidth(group.getWidth());
1144:
1145: if (group.hasTotal()) {
1146: JRDesignCellContents totalHeader = (JRDesignCellContents) group
1147: .getTotalHeader();
1148: totalHeader.setWidth(widthSum);
1149: JRDesignCrosstabCell totalCell = crossCells[i][columnGroups
1150: .size()];
1151: if (totalCell != null) {
1152: totalHeader.setHeight(totalCell.getContents()
1153: .getHeight());
1154: }
1155: }
1156: }
1157: return widthSum;
1158: }
1159:
1160: protected int calculateColumnHeadersSizes() {
1161: int heightSum = 0;
1162: for (int i = columnGroups.size() - 1, widthSum = 0; i >= 0; --i) {
1163: JRDesignCrosstabColumnGroup group = (JRDesignCrosstabColumnGroup) columnGroups
1164: .get(i);
1165:
1166: heightSum += group.getHeight();
1167: JRDesignCrosstabCell cell = crossCells[rowGroups.size()][i + 1];
1168: if (cell != null) {
1169: widthSum += cell.getContents().getWidth();
1170: }
1171:
1172: JRDesignCellContents header = (JRDesignCellContents) group
1173: .getHeader();
1174: header.setHeight(group.getHeight());
1175: header.setWidth(widthSum);
1176:
1177: if (group.hasTotal()) {
1178: JRDesignCellContents totalHeader = (JRDesignCellContents) group
1179: .getTotalHeader();
1180: totalHeader.setHeight(heightSum);
1181: JRDesignCrosstabCell totalCell = crossCells[rowGroups
1182: .size()][i];
1183: if (totalCell != null) {
1184: totalHeader.setWidth(totalCell.getContents()
1185: .getWidth());
1186: }
1187: }
1188: }
1189: return heightSum;
1190: }
1191:
1192: public JRCellContents getWhenNoDataCell() {
1193: return whenNoDataCell;
1194: }
1195:
1196: /**
1197: * Sets the "No data" cell.
1198: *
1199: * @param whenNoDataCell the cell
1200: * @see JRCrosstab#getWhenNoDataCell()
1201: */
1202: public void setWhenNoDataCell(JRDesignCellContents whenNoDataCell) {
1203: this .whenNoDataCell = whenNoDataCell;
1204: }
1205:
1206: public JRElement getElementByKey(String elementKey) {
1207: return JRBaseCrosstab.getElementByKey(this , elementKey);
1208: }
1209:
1210: public byte getMode() {
1211: return JRStyleResolver.getMode(this , MODE_TRANSPARENT);
1212: }
1213:
1214: public JRCellContents getHeaderCell() {
1215: return headerCell;
1216: }
1217:
1218: /**
1219: * Sets the crosstab header cell (this cell will be rendered at the upper-left corder of the crosstab).
1220: *
1221: * @param headerCell the cell
1222: * @see JRCrosstab#getHeaderCell()
1223: */
1224: public void setHeaderCell(JRDesignCellContents headerCell) {
1225: this .headerCell = headerCell;
1226: }
1227:
1228: protected void measureClassChanged(JRDesignCrosstabMeasure measure,
1229: String valueClassName) {
1230: for (Iterator colIt = columnGroups.iterator(); colIt.hasNext();) {
1231: JRCrosstabColumnGroup colGroup = (JRCrosstabColumnGroup) colIt
1232: .next();
1233: setTotalVarClass(measure, null, colGroup, valueClassName);
1234: }
1235:
1236: for (Iterator rowIt = rowGroups.iterator(); rowIt.hasNext();) {
1237: JRCrosstabRowGroup rowGroup = (JRCrosstabRowGroup) rowIt
1238: .next();
1239: setTotalVarClass(measure, rowGroup, null, valueClassName);
1240:
1241: for (Iterator colIt = columnGroups.iterator(); colIt
1242: .hasNext();) {
1243: JRCrosstabColumnGroup colGroup = (JRCrosstabColumnGroup) colIt
1244: .next();
1245: setTotalVarClass(measure, rowGroup, colGroup,
1246: valueClassName);
1247: }
1248: }
1249: }
1250:
1251: protected void setTotalVarClass(JRCrosstabMeasure measure,
1252: JRCrosstabRowGroup rowGroup,
1253: JRCrosstabColumnGroup colGroup, String valueClassName) {
1254: JRDesignVariable variable = getVariable(getTotalVariableName(
1255: measure, rowGroup, colGroup));
1256: variable.setValueClassName(valueClassName);
1257: }
1258:
1259: private void addVariable(JRVariable variable) {
1260: variablesList.put(variable.getName(), variable);
1261: }
1262:
1263: private void removeVariable(JRVariable variable) {
1264: removeVariable(variable.getName());
1265: }
1266:
1267: private void removeVariable(String varName) {
1268: variablesList.remove(varName);
1269: }
1270:
1271: private JRDesignVariable getVariable(String varName) {
1272: return (JRDesignVariable) variablesList.get(varName);
1273: }
1274:
1275: public byte getRunDirection() {
1276: return runDirection;
1277: }
1278:
1279: public void setRunDirection(byte runDirection) {
1280: this.runDirection = runDirection;
1281: }
1282: }
|