KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > net > sf > jasperreports > engine > fill > JRFillCrosstab


1 /*
2  * ============================================================================
3  * GNU Lesser General Public License
4  * ============================================================================
5  *
6  * JasperReports - Free Java report-generating library.
7  * Copyright (C) 2001-2006 JasperSoft Corporation http://www.jaspersoft.com
8  *
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public
11  * License as published by the Free Software Foundation; either
12  * version 2.1 of the License, or (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with this library; if not, write to the Free Software
21  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
22  *
23  * JasperSoft Corporation
24  * 303 Second Street, Suite 450 North
25  * San Francisco, CA 94107
26  * http://www.jaspersoft.com
27  */

28 package net.sf.jasperreports.engine.fill;
29
30 import java.io.IOException JavaDoc;
31 import java.util.ArrayList JavaDoc;
32 import java.util.Comparator JavaDoc;
33 import java.util.HashMap JavaDoc;
34 import java.util.Iterator JavaDoc;
35 import java.util.List JavaDoc;
36 import java.util.Map JavaDoc;
37 import java.util.ResourceBundle JavaDoc;
38
39 import net.sf.jasperreports.crosstabs.JRCellContents;
40 import net.sf.jasperreports.crosstabs.JRCrosstab;
41 import net.sf.jasperreports.crosstabs.JRCrosstabBucket;
42 import net.sf.jasperreports.crosstabs.JRCrosstabCell;
43 import net.sf.jasperreports.crosstabs.JRCrosstabColumnGroup;
44 import net.sf.jasperreports.crosstabs.JRCrosstabDataset;
45 import net.sf.jasperreports.crosstabs.JRCrosstabGroup;
46 import net.sf.jasperreports.crosstabs.JRCrosstabMeasure;
47 import net.sf.jasperreports.crosstabs.JRCrosstabParameter;
48 import net.sf.jasperreports.crosstabs.JRCrosstabRowGroup;
49 import net.sf.jasperreports.crosstabs.base.JRBaseCrosstab;
50 import net.sf.jasperreports.crosstabs.design.JRDesignCrosstab;
51 import net.sf.jasperreports.crosstabs.fill.JRCrosstabExpressionEvaluator;
52 import net.sf.jasperreports.crosstabs.fill.JRFillCrosstabCell;
53 import net.sf.jasperreports.crosstabs.fill.JRFillCrosstabColumnGroup;
54 import net.sf.jasperreports.crosstabs.fill.JRFillCrosstabGroup;
55 import net.sf.jasperreports.crosstabs.fill.JRFillCrosstabMeasure;
56 import net.sf.jasperreports.crosstabs.fill.JRFillCrosstabParameter;
57 import net.sf.jasperreports.crosstabs.fill.JRFillCrosstabRowGroup;
58 import net.sf.jasperreports.crosstabs.fill.calculation.BucketDefinition;
59 import net.sf.jasperreports.crosstabs.fill.calculation.BucketingService;
60 import net.sf.jasperreports.crosstabs.fill.calculation.CrosstabCell;
61 import net.sf.jasperreports.crosstabs.fill.calculation.HeaderCell;
62 import net.sf.jasperreports.crosstabs.fill.calculation.MeasureDefinition;
63 import net.sf.jasperreports.crosstabs.fill.calculation.BucketDefinition.Bucket;
64 import net.sf.jasperreports.crosstabs.fill.calculation.MeasureDefinition.MeasureValue;
65 import net.sf.jasperreports.engine.JRAbstractObjectFactory;
66 import net.sf.jasperreports.engine.JRChild;
67 import net.sf.jasperreports.engine.JRElement;
68 import net.sf.jasperreports.engine.JRException;
69 import net.sf.jasperreports.engine.JRExpression;
70 import net.sf.jasperreports.engine.JRExpressionChunk;
71 import net.sf.jasperreports.engine.JRExpressionCollector;
72 import net.sf.jasperreports.engine.JRGraphicElement;
73 import net.sf.jasperreports.engine.JRParameter;
74 import net.sf.jasperreports.engine.JRPrintElement;
75 import net.sf.jasperreports.engine.JRPrintFrame;
76 import net.sf.jasperreports.engine.JRPrintRectangle;
77 import net.sf.jasperreports.engine.JRRuntimeException;
78 import net.sf.jasperreports.engine.JRStyle;
79 import net.sf.jasperreports.engine.JRVariable;
80 import net.sf.jasperreports.engine.JasperReport;
81 import net.sf.jasperreports.engine.design.JRDefaultCompiler;
82 import net.sf.jasperreports.engine.design.JRDesignRectangle;
83 import net.sf.jasperreports.engine.util.JRStyleResolver;
84 import net.sf.jasperreports.engine.xml.JRXmlWriter;
85
86 import org.jfree.data.general.Dataset;
87
88 /**
89  * Fill-time implementation of a {@link net.sf.jasperreports.crosstabs.JRCrosstab crosstab}.
90  *
91  * @author Lucian Chirita (lucianc@users.sourceforge.net)
92  * @version $Id: JRFillCrosstab.java 1485 2006-11-14 20:23:17 +0200 (Tue, 14 Nov 2006) teodord $
93  */

94 public class JRFillCrosstab extends JRFillElement implements JRCrosstab
95 {
96     final protected JRCrosstab parentCrosstab;
97
98     protected JRFillCrosstabDataset dataset;
99
100     protected JRFillCrosstabRowGroup[] rowGroups;
101
102     protected Map JavaDoc rowGroupsMap;
103
104     protected JRFillCrosstabColumnGroup[] columnGroups;
105
106     protected Map JavaDoc columnGroupsMap;
107
108     protected JRFillCrosstabMeasure[] measures;
109
110     protected BucketingService bucketingService;
111
112     protected JRFillVariable[] variables;
113
114     protected Map JavaDoc variablesMap;
115     
116     protected JRFillVariable[][][] totalVariables;
117     protected boolean[][] retrieveTotal;
118
119     protected JRFillCrosstabParameter[] parameters;
120
121     protected Map JavaDoc parametersMap;
122
123     protected JRCrosstabExpressionEvaluator crosstabEvaluator;
124
125     protected JRFillCrosstabCell[][] crossCells;
126     protected JRFillCellContents headerCell;
127     protected JRFillCellContents whenNoDataCell;
128
129     protected boolean hasData;
130     protected HeaderCell[][] columnHeadersData;
131     protected HeaderCell[][] rowHeadersData;
132     protected CrosstabCell[][] cellData;
133     protected MeasureValue[] grandTotals;
134
135     private boolean percentage;
136
137     private CrosstabFiller crosstabFiller;
138     
139     public JRFillCrosstab(JRBaseFiller filler, JRCrosstab crosstab, JRFillObjectFactory factory)
140     {
141         super(filler, crosstab, factory);
142
143         parentCrosstab = crosstab;
144
145         loadEvaluator(filler.getJasperReport());
146
147         JRFillObjectFactory crosstabFactory = new JRFillObjectFactory(filler, crosstabEvaluator);
148         
149         headerCell = crosstabFactory.getCell(crosstab.getHeaderCell());
150
151         copyRowGroups(crosstab, crosstabFactory);
152         copyColumnGroups(crosstab, crosstabFactory);
153         
154         copyMeasures(crosstab, crosstabFactory);
155         copyCells(crosstab, crosstabFactory);
156         whenNoDataCell = crosstabFactory.getCell(crosstab.getWhenNoDataCell());
157         
158         dataset = factory.getCrosstabDataset(crosstab.getDataset(), this);
159
160         copyParameters(crosstab, factory);
161         copyVariables(crosstab, crosstabFactory);
162
163         crosstabFiller = new CrosstabFiller();
164     }
165
166     /**
167      *
168      */

169     public byte getMode()
170     {
171         return JRStyleResolver.getMode(this, MODE_TRANSPARENT);
172     }
173
174     private void copyRowGroups(JRCrosstab crosstab, JRFillObjectFactory factory)
175     {
176         JRCrosstabRowGroup[] groups = crosstab.getRowGroups();
177         rowGroups = new JRFillCrosstabRowGroup[groups.length];
178         rowGroupsMap = new HashMap JavaDoc();
179         for (int i = 0; i < groups.length; ++i)
180         {
181             JRFillCrosstabRowGroup group = factory.getCrosstabRowGroup(groups[i]);
182             group.getFillHeader().setVerticalPositionType(groups[i].getPosition());
183
184             rowGroups[i] = group;
185             rowGroupsMap.put(group.getName(), new Integer JavaDoc(i));
186         }
187     }
188
189     private void copyColumnGroups(JRCrosstab crosstab, JRFillObjectFactory factory)
190     {
191         JRCrosstabColumnGroup[] groups = crosstab.getColumnGroups();
192         columnGroups = new JRFillCrosstabColumnGroup[groups.length];
193         columnGroupsMap = new HashMap JavaDoc();
194         for (int i = 0; i < groups.length; ++i)
195         {
196             JRFillCrosstabColumnGroup group = factory.getCrosstabColumnGroup(groups[i]);
197             columnGroups[i] = group;
198             columnGroupsMap.put(group.getName(), new Integer JavaDoc(i));
199         }
200     }
201
202     private void copyMeasures(JRCrosstab crosstab, JRFillObjectFactory factory)
203     {
204         JRCrosstabMeasure[] crossMeasures = crosstab.getMeasures();
205         measures = new JRFillCrosstabMeasure[crossMeasures.length];
206         for (int i = 0; i < crossMeasures.length; i++)
207         {
208             measures[i] = factory.getCrosstabMeasure(crossMeasures[i]);
209         }
210     }
211
212     private void copyParameters(JRCrosstab crosstab, JRFillObjectFactory factory)
213     {
214         JRCrosstabParameter[] crossParams = crosstab.getParameters();
215         parameters = new JRFillCrosstabParameter[crossParams.length];
216         parametersMap = new HashMap JavaDoc();
217         for (int i = 0; i < crossParams.length; i++)
218         {
219             parameters[i] = factory.getCrosstabParameter(crossParams[i]);
220             parametersMap.put(parameters[i].getName(), parameters[i]);
221         }
222     }
223
224     private void copyCells(JRCrosstab crosstab, JRFillObjectFactory factory)
225     {
226         JRCrosstabCell[][] crosstabCells = crosstab.getCells();
227         crossCells = new JRFillCrosstabCell[rowGroups.length + 1][columnGroups.length + 1];
228         for (int i = 0; i <= rowGroups.length; ++i)
229         {
230             for (int j = 0; j <= columnGroups.length; ++j)
231             {
232                 if (crosstabCells[i][j] != null)
233                 {
234                     crossCells[i][j] = factory.getCrosstabCell(crosstabCells[i][j]);
235                 }
236             }
237         }
238     }
239
240     private void copyVariables(JRCrosstab crosstab, JRFillObjectFactory factory)
241     {
242         JRVariable[] vars = crosstab.getVariables();
243         variables = new JRFillVariable[vars.length];
244         variablesMap = new HashMap JavaDoc();
245         for (int i = 0; i < variables.length; i++)
246         {
247             variables[i] = factory.getVariable(vars[i]);
248             variablesMap.put(variables[i].getName(), variables[i]);
249         }
250         
251         Map JavaDoc totalVarPos = new HashMap JavaDoc();
252         totalVariables = new JRFillVariable[rowGroups.length + 1][columnGroups.length + 1][measures.length];
253         for (int row = 0; row <= rowGroups.length; ++row)
254         {
255             JRCrosstabRowGroup rowGroup = row == rowGroups.length ? null : rowGroups[row];
256             for (int col = 0; col <= columnGroups.length; ++col)
257             {
258                 JRCrosstabColumnGroup colGroup = col == columnGroups.length ? null : columnGroups[col];
259                 
260                 if (row < rowGroups.length || col < columnGroups.length)
261                 {
262                     for (int m = 0; m < measures.length; m++)
263                     {
264                         String JavaDoc totalVariableName = JRDesignCrosstab.getTotalVariableName(measures[m], rowGroup, colGroup);
265                         totalVariables[row][col][m] = (JRFillVariable) variablesMap.get(totalVariableName);
266                         totalVarPos.put(totalVariableName, new int[]{row, col});
267                     }
268                 }
269             }
270         }
271
272         retrieveTotal = new boolean[rowGroups.length + 1][columnGroups.length + 1];
273         
274         //FIXME avoid this
275
List JavaDoc expressions = JRExpressionCollector.collectExpressions(filler.getJasperReport(), crosstab);
276         for (Iterator JavaDoc iter = expressions.iterator(); iter.hasNext();)
277         {
278             JRExpression expression = (JRExpression) iter.next();
279             JRExpressionChunk[] chunks = expression.getChunks();
280             if (chunks != null)
281             {
282                 for (int i = 0; i < chunks.length; i++)
283                 {
284                     JRExpressionChunk chunk = chunks[i];
285                     if (chunk.getType() == JRExpressionChunk.TYPE_VARIABLE)
286                     {
287                         String JavaDoc varName = chunk.getText();
288                         int[] pos = (int[]) totalVarPos.get(varName);
289                         if (pos != null)
290                         {
291                             retrieveTotal[pos[0]][pos[1]] = true;
292                         }
293                     }
294                 }
295             }
296         }
297     }
298
299     protected void loadEvaluator(JasperReport jasperReport)
300     {
301         try
302         {
303             JREvaluator evaluator = JRDefaultCompiler.getInstance().loadEvaluator(jasperReport, parentCrosstab);
304             crosstabEvaluator = new JRCrosstabExpressionEvaluator(evaluator);
305         }
306         catch (JRException e)
307         {
308             throw new JRRuntimeException("Could not load evaluator for crosstab.", e);
309         }
310     }
311
312     private BucketingService createService(byte evaluation) throws JRException
313     {
314         List JavaDoc rowBuckets = new ArrayList JavaDoc(rowGroups.length);
315         for (int i = 0; i < rowGroups.length; ++i)
316         {
317             rowBuckets.add(createServiceBucket(rowGroups[i], evaluation));
318         }
319
320         List JavaDoc colBuckets = new ArrayList JavaDoc(columnGroups.length);
321         for (int i = 0; i < columnGroups.length; ++i)
322         {
323             colBuckets.add(createServiceBucket(columnGroups[i], evaluation));
324         }
325
326         percentage = false;
327         List JavaDoc measureList = new ArrayList JavaDoc(measures.length);
328         for (int i = 0; i < measures.length; ++i)
329         {
330             measureList.add(createServiceMeasure(measures[i]));
331             percentage |= measures[i].getPercentageOfType() == JRCrosstabMeasure.PERCENTAGE_TYPE_GRAND_TOTAL;
332         }
333
334         if (percentage)
335         {
336             ((BucketDefinition) rowBuckets.get(0)).setComputeTotal();
337             ((BucketDefinition) colBuckets.get(0)).setComputeTotal();
338         }
339         
340         return new BucketingService(rowBuckets, colBuckets, measureList, dataset.isDataPreSorted(), retrieveTotal);
341     }
342
343     private BucketDefinition createServiceBucket(JRCrosstabGroup group, byte evaluation) throws JRException
344     {
345         JRCrosstabBucket bucket = group.getBucket();
346
347         Comparator JavaDoc comparator = null;
348         JRExpression comparatorExpression = bucket.getComparatorExpression();
349         if (comparatorExpression != null)
350         {
351             comparator = (Comparator JavaDoc) evaluateExpression(comparatorExpression, evaluation);
352         }
353
354         byte totalPosition = group.getTotalPosition();
355         return new BucketDefinition(bucket.getExpression().getValueClass(), comparator, bucket.getOrder(), totalPosition);
356     }
357
358     private MeasureDefinition createServiceMeasure(JRFillCrosstabMeasure measure)
359     {
360         return new MeasureDefinition(
361                 measure.getValueClass(),
362                 measure.getCalculation(),
363                 measure.getIncrementerFactory());
364     }
365
366     protected void reset()
367     {
368         super.reset();
369
370         for (int i = 0; i < variables.length; i++)
371         {
372             variables[i].setValue(null);
373             variables[i].setInitialized(true);
374         }
375     }
376
377     protected void evaluate(byte evaluation) throws JRException
378     {
379         reset();
380
381         evaluatePrintWhenExpression(evaluation);
382
383         if (isPrintWhenExpressionNull() || isPrintWhenTrue())
384         {
385             dataset.evaluateDatasetRun(evaluation);
386
387             initEvaluator(evaluation);
388
389             bucketingService.processData();
390
391             hasData = bucketingService.hasData();
392             
393             if (hasData)
394             {
395                 columnHeadersData = bucketingService.getColumnHeaders();
396                 rowHeadersData = bucketingService.getRowHeaders();
397                 cellData = bucketingService.getCrosstabCells();
398                 if (percentage)
399                 {
400                     grandTotals = bucketingService.getGrandTotals();
401                 }
402
403                 crosstabFiller.initCrosstab();
404             }
405         }
406     }
407
408     protected void initEvaluator(byte evaluation) throws JRException
409     {
410         Map JavaDoc parameterValues =
411             JRFillSubreport.getParameterValues(
412                 filler,
413                 getParametersMapExpression(),
414                 getParameters(),
415                 evaluation,
416                 true,
417                 false,//hasResourceBundle
418
false//hasFormatFactory
419
);
420         
421         ResourceBundle JavaDoc resBdl = (ResourceBundle JavaDoc) parameterValues.get(JRParameter.REPORT_RESOURCE_BUNDLE);
422         if (resBdl == null)
423         {
424             JRFillParameter resourceBundleParam = (JRFillParameter) filler.getParametersMap().get(JRParameter.REPORT_RESOURCE_BUNDLE);
425             parameterValues.put(JRParameter.REPORT_RESOURCE_BUNDLE, resourceBundleParam.getValue());
426         }
427         
428         parameterValues.put(JRParameter.REPORT_PARAMETERS_MAP, parameterValues);
429
430         for (int i = 0; i < parameters.length; i++)
431         {
432             Object JavaDoc value = parameterValues.get(parameters[i].getName());
433             parameters[i].setValue(value);
434         }
435
436         crosstabEvaluator.init(parametersMap, variablesMap, filler.getWhenResourceMissingType());
437     }
438
439     protected void initBucketingService()
440     {
441         if (bucketingService == null)
442         {
443             try
444             {
445                 bucketingService = createService(JRExpression.EVALUATION_TIME_NOW);
446             }
447             catch (JRException e)
448             {
449                 throw new JRRuntimeException("Could not create bucketing service", e);
450             }
451         }
452         else
453         {
454             bucketingService.clear();
455         }
456     }
457
458     protected boolean prepare(int availableStretchHeight, boolean isOverflow) throws JRException
459     {
460         super.prepare(availableStretchHeight, isOverflow);
461
462         if (!isToPrint())
463         {
464             return false;
465         }
466
467         if (availableStretchHeight < getRelativeY() - getY() - getBandBottomY())
468         {
469             setToPrint(false);
470             return true;
471         }
472
473         if (isOverflow && crosstabFiller.ended() && isAlreadyPrinted())
474         {
475             if (isPrintWhenDetailOverflows())
476             {
477                 rewind();
478                 setReprinted(true);
479             }
480             else
481             {
482                 setStretchHeight(getHeight());
483                 setToPrint(false);
484
485                 return false;
486             }
487         }
488
489         if (isOverflow && isPrintWhenDetailOverflows())
490         {
491             setReprinted(true);
492         }
493
494         int availableHeight = getHeight() + availableStretchHeight - getRelativeY() + getY() + getBandBottomY();
495         crosstabFiller.fill(availableHeight);
496         
497         boolean willOverflow = crosstabFiller.willOverflow();
498         setStretchHeight(willOverflow ? availableHeight : crosstabFiller.getUsedHeight());
499         
500         return willOverflow;
501     }
502
503     protected JRPrintElement fill()
504     {
505         JRPrintRectangle printRectangle = null;
506
507         printRectangle = new JRTemplatePrintRectangle(getJRTemplateRectangle());
508         printRectangle.setX(getX());
509         printRectangle.setY(getRelativeY());
510         printRectangle.setWidth(getWidth());
511         printRectangle.setHeight(getStretchHeight());
512
513         return printRectangle;
514     }
515
516     protected JRTemplateRectangle getJRTemplateRectangle()
517     {
518         JRStyle style = getStyle();
519         JRTemplateRectangle template = (JRTemplateRectangle) getTemplate(style);
520         if (template == null)
521         {
522             JRDesignRectangle rectangle = new JRDesignRectangle();
523
524             rectangle.setKey(getKey());
525             rectangle.setPositionType(getPositionType());
526             // rectangle.setPrintRepeatedValues(isPrintRepeatedValues());
527
rectangle.setMode(getMode());
528             rectangle.setX(getX());
529             rectangle.setY(getY());
530             rectangle.setWidth(getWidth());
531             rectangle.setHeight(getHeight());
532             rectangle.setRemoveLineWhenBlank(isRemoveLineWhenBlank());
533             rectangle.setPrintInFirstWholeBand(isPrintInFirstWholeBand());
534             rectangle.setPrintWhenDetailOverflows(isPrintWhenDetailOverflows());
535             rectangle.setPrintWhenGroupChanges(getPrintWhenGroupChanges());
536             rectangle.setForecolor(getForecolor());
537             rectangle.setBackcolor(getBackcolor());
538             rectangle.setPen(JRGraphicElement.PEN_NONE);
539
540             template = new JRTemplateRectangle(filler.getJasperPrint().getDefaultStyleProvider(), rectangle);
541             
542             registerTemplate(style, template);
543         }
544
545         return template;
546     }
547
548     protected void rewind()
549     {
550         crosstabFiller.initCrosstab();
551     }
552
553     protected List JavaDoc getPrintElements()
554     {
555         return crosstabFiller.getPrintElements();
556     }
557
558     protected void resolveElement(JRPrintElement element, byte evaluation)
559     {
560         // nothing
561
}
562
563     public void collectExpressions(JRExpressionCollector collector)
564     {
565         collector.collect(this);
566     }
567
568     public JRChild getCopy(JRAbstractObjectFactory factory)
569     {
570         return factory.getCrosstab(this);
571     }
572
573     public void writeXml(JRXmlWriter writer) throws IOException JavaDoc
574     {
575         writer.writeCrosstab(this);
576     }
577
578     public int getId()
579     {
580         return parentCrosstab.getId();
581     }
582
583     public JRCrosstabDataset getDataset()
584     {
585         return dataset;
586     }
587
588     public JRCrosstabRowGroup[] getRowGroups()
589     {
590         return rowGroups;
591     }
592
593     public JRCrosstabColumnGroup[] getColumnGroups()
594     {
595         return columnGroups;
596     }
597
598     public JRCrosstabMeasure[] getMeasures()
599     {
600         return measures;
601     }
602
603     
604     /**
605      * Fill-time crosstab input dataset implementation.
606      *
607      * @author Lucian Chirita (lucianc@users.sourceforge.net)
608      */

609     public class JRFillCrosstabDataset extends JRFillElementDataset implements JRCrosstabDataset
610     {
611         private Object JavaDoc[] bucketValues;
612
613         private Object JavaDoc[] measureValues;
614
615         public JRFillCrosstabDataset(JRCrosstabDataset dataset, JRFillObjectFactory factory)
616         {
617             super(dataset, factory);
618
619             this.bucketValues = new Object JavaDoc[rowGroups.length + columnGroups.length];
620             this.measureValues = new Object JavaDoc[measures.length];
621         }
622
623         protected void customInitialize()
624         {
625             initBucketingService();
626         }
627
628         protected void customEvaluate(JRCalculator calculator) throws JRExpressionEvalException
629         {
630             for (int i = 0; i < rowGroups.length; i++)
631             {
632                 bucketValues[i] = calculator.evaluate(rowGroups[i].getBucket().getExpression());
633             }
634
635             for (int i = 0; i < columnGroups.length; ++i)
636             {
637                 bucketValues[i + rowGroups.length] = calculator.evaluate(columnGroups[i].getBucket().getExpression());
638             }
639
640             for (int i = 0; i < measures.length; i++)
641             {
642                 measureValues[i] = calculator.evaluate(measures[i].getValueExpression());
643             }
644         }
645
646         protected void customIncrement()
647         {
648             try
649             {
650                 bucketingService.addData(bucketValues, measureValues);
651             }
652             catch (JRException e)
653             {
654                 throw new JRRuntimeException("Error incrementing crosstab dataset", e);
655             }
656         }
657
658         protected Dataset getCustomDataset()
659         {
660             return null;
661         }
662
663         public void collectExpressions(JRExpressionCollector collector)
664         {
665         }
666
667         public boolean isDataPreSorted()
668         {
669             return ((JRCrosstabDataset) parent).isDataPreSorted();
670         }
671     }
672     
673     /**
674      * Crosstab filler class.
675      *
676      * @author Lucian Chirita (lucianc@users.sourceforge.net)
677      */

678     protected class CrosstabFiller
679     {
680         private int yOffset;
681         private boolean willOverflow;
682
683         private int[] rowHeadersXOffsets;
684         
685         private boolean[] columnBreakable;
686         private boolean[] rowBreakable;
687         private int[] columnCount;
688         private int[] rowCount;
689         private int[] columnXOffsets;
690         
691         private boolean noDataCellPrinted;
692         
693         private int startRowIndex;
694         private int startColumnIndex;
695         private int lastColumnIndex;
696         private List JavaDoc columnHeaders;
697
698         private List JavaDoc printRows;
699
700         private HeaderCell[] spanHeaders;
701         private int[] spanHeadersStart;
702
703         private List JavaDoc rowYs = new ArrayList JavaDoc();
704         private int rowIdx;
705         
706         private List JavaDoc preparedRow = new ArrayList JavaDoc();
707         private int preparedRowHeight;
708         
709         private boolean printRowHeaders;
710         private boolean printColumnHeaders;
711         
712         private JRFillVariable rowCountVar;
713         private JRFillVariable colCountVar;
714
715         protected CrosstabFiller()
716         {
717             setRowHeadersXOffsets();
718
719             printRows = new ArrayList JavaDoc();
720             
721             rowCountVar = (JRFillVariable) variablesMap.get(JRCrosstab.VARIABLE_ROW_COUNT);
722             colCountVar = (JRFillVariable) variablesMap.get(JRCrosstab.VARIABLE_COLUMN_COUNT);
723         }
724         
725         protected void initCrosstab()
726         {
727             columnXOffsets = computeOffsets(columnHeadersData, columnGroups, true);
728             columnBreakable = computeBreakableHeaders(columnHeadersData, columnGroups, columnXOffsets, true, true);
729             columnCount = computeCounts(columnHeadersData);
730             
731             int[] rowYOffsets = computeOffsets(rowHeadersData, rowGroups, false);
732             rowBreakable = computeBreakableHeaders(rowHeadersData, rowGroups, rowYOffsets, false, false);
733             rowCount = computeCounts(rowHeadersData);
734             
735             spanHeaders = new HeaderCell[rowGroups.length - 1];
736             spanHeadersStart = new int[rowGroups.length - 1];
737             
738             startRowIndex = 0;
739             startColumnIndex = 0;
740             lastColumnIndex = 0;
741             noDataCellPrinted = false;
742         }
743
744         protected void setRowHeadersXOffsets()
745         {
746             rowHeadersXOffsets = new int[rowGroups.length + 1];
747             rowHeadersXOffsets[0] = 0;
748             for (int i = 0; i < rowGroups.length; i++)
749             {
750                 rowHeadersXOffsets[i + 1] = rowHeadersXOffsets[i] + rowGroups[i].getWidth();
751             }
752         }
753
754         protected int[] computeOffsets(HeaderCell[][] headersData, JRFillCrosstabGroup[] groups, boolean width)
755         {
756             int[] offsets = new int[headersData[0].length + 1];
757             offsets[0] = 0;
758             for (int i = 0; i < headersData[0].length; i++)
759             {
760                 int size = 0;
761                 for (int j = groups.length - 1; j >= 0; --j)
762                 {
763                     if (headersData[j][i] != null)
764                     {
765                         JRFillCellContents cell = headersData[j][i].isTotal() ? groups[j].getFillTotalHeader() : groups[j].getFillHeader();
766                         size = cell == null ? 0 : (width ? cell.getWidth() : cell.getHeight());
767                         break;
768                     }
769                 }
770
771                 offsets[i + 1] = offsets[i] + size;
772             }
773
774             return offsets;
775         }
776
777         protected boolean[] computeBreakableHeaders(HeaderCell[][] headersData, JRFillCrosstabGroup[] groups, int[] offsets, boolean width, boolean startHeaders)
778         {
779             boolean[] breakable = new boolean[headersData[0].length];
780             for (int i = 0; i < breakable.length; i++)
781             {
782                 breakable[i] = true;
783             }
784
785             for (int j = 0; j < groups.length; ++j)
786             {
787                 JRFillCellContents fillHeader = groups[j].getFillHeader();
788                 
789                 if (fillHeader != null)
790                 {
791                     int size = width ? fillHeader.getWidth() : fillHeader.getHeight();
792                     
793                     for (int i = 0; i < headersData[0].length; i++)
794                     {
795                         HeaderCell header = headersData[j][i];
796                         if (header != null && !header.isTotal() && header.getLevelSpan() > 1)
797                         {
798                             int span = header.getLevelSpan();
799                             
800                             if (startHeaders)
801                             {
802                                 for (int k = i + 1; k < i + span && offsets[k] - offsets[i] < size; ++k)
803                                 {
804                                     breakable[k] = false;
805                                 }
806                             }
807                             
808                             for (int k = i + span - 1; k > i && offsets[i + span] - offsets[k] < size; --k)
809                             {
810                                 breakable[k] = false;
811                             }
812                         }
813                     }
814                 }
815             }
816
817             return breakable;
818         }
819
820         private int[] computeCounts(HeaderCell[][] headersData)
821         {
822             int[] counts = new int[headersData[0].length];
823             
824             HeaderCell[] lastHeaders = headersData[headersData.length - 1];
825             for (int i = 0, c = 0; i < counts.length; ++i)
826             {
827                 HeaderCell lastHeader = lastHeaders[i];
828                 if (lastHeader != null && !lastHeader.isTotal())
829                 {
830                     ++c;
831                 }
832                 
833                 counts[i] = c;
834             }
835             
836             return counts;
837         }
838
839         protected void fill(int availableHeight) throws JRException
840         {
841             printRows.clear();
842
843             yOffset = 0;
844             willOverflow = false;
845             
846             fillVerticalCrosstab(availableHeight);
847         }
848         
849         protected boolean willOverflow()
850         {
851             return willOverflow;
852         }
853         
854         protected int getUsedHeight()
855         {
856             return yOffset;
857         }
858         
859         protected boolean ended()
860         {
861             return hasData ? (startRowIndex >= rowHeadersData[0].length && startColumnIndex >= columnHeadersData[0].length) : noDataCellPrinted;
862         }
863         
864         protected void fillVerticalCrosstab(int availableHeight) throws JRException
865         {
866             if (!hasData)
867             {
868                 fillNoDataCell(availableHeight);
869                 return;
870             }
871             
872             printRowHeaders = startColumnIndex == 0 || isRepeatRowHeaders();
873             int rowHeadersXOffset = printRowHeaders ? rowHeadersXOffsets[rowGroups.length] : 0;
874
875             if (startColumnIndex == lastColumnIndex)
876             {
877                 int availableWidth = getWidth();
878
879                 columnHeaders = getGroupHeaders(availableWidth - rowHeadersXOffset, columnXOffsets, columnBreakable, startColumnIndex, columnHeadersData, columnGroups);
880                 lastColumnIndex = startColumnIndex + columnHeaders.size();
881
882                 if (startColumnIndex == lastColumnIndex)
883                 {
884                     throw new JRRuntimeException("Not enough space to render the crosstab.");
885                 }
886             }
887             
888             printColumnHeaders = startRowIndex == 0 || isRepeatColumnHeaders();
889             List JavaDoc columnHeaderRows = null;
890             if (printColumnHeaders)
891             {
892                 columnHeaderRows = fillColumnHeaders(rowHeadersXOffset, availableHeight - yOffset);
893                 if (willOverflow)
894                 {
895                     return;
896                 }
897             }
898
899             int lastRowIndex = fillRows(rowHeadersXOffset, availableHeight - yOffset);
900
901             if (lastRowIndex == startRowIndex)
902             {
903                 willOverflow = true;
904                 return;
905             }
906
907             if (columnHeaderRows != null)
908             {
909                 printRows.addAll(columnHeaderRows);
910             }
911             
912             if (lastRowIndex >= rowHeadersData[0].length)
913             {
914                 startColumnIndex = lastColumnIndex;
915
916                 if (startColumnIndex < columnHeadersData[0].length)
917                 {
918                     startRowIndex = lastRowIndex = 0;
919
920                     yOffset += getColumnBreakOffset();
921                     fillVerticalCrosstab(availableHeight);
922                     return;
923                 }
924             }
925
926             boolean fillEnded = lastRowIndex >= rowHeadersData[0].length && lastColumnIndex >= columnHeadersData[0].length;
927             if (fillEnded)
928             {
929                 setStretchHeight(yOffset);
930             }
931             else
932             {
933                 setStretchHeight(availableHeight);
934             }
935
936             startRowIndex = lastRowIndex;
937
938             willOverflow = !fillEnded;
939         }
940
941         
942         protected List JavaDoc getGroupHeaders(int available, int[] offsets, boolean[] breakable, int firstIndex, HeaderCell[][] headersData, JRFillCrosstabGroup[] groups)
943         {
944             List JavaDoc headers = new ArrayList JavaDoc();
945
946             int maxOffset = available + offsets[firstIndex];
947             int lastIndex;
948             for (lastIndex = firstIndex; lastIndex < headersData[0].length && offsets[lastIndex + 1] <= maxOffset; ++lastIndex)
949             {
950                 HeaderCell[] groupHeaders = new HeaderCell[groups.length];
951
952                 for (int j = 0; j < groups.length; ++j)
953                 {
954                     groupHeaders[j] = headersData[j][lastIndex];
955                 }
956
957                 headers.add(groupHeaders);
958             }
959
960             
961             if (lastIndex < headersData[0].length)
962             {
963                 while(lastIndex > firstIndex && !breakable[lastIndex])
964                 {
965                     --lastIndex;
966                     headers.remove(headers.size() - 1);
967                 }
968             }
969             
970             if (lastIndex > firstIndex)
971             {
972                 if (firstIndex > 0)
973                 {
974                     HeaderCell[] firstHeaders = (HeaderCell[]) headers.get(0);
975
976                     for (int j = 0; j < groups.length; ++j)
977                     {
978                         HeaderCell header = headersData[j][firstIndex];
979
980                         if (header == null)
981                         {
982                             int spanIndex = getSpanIndex(firstIndex, j, headersData);
983                             if (spanIndex >= 0)
984                             {
985                                 HeaderCell spanCell = headersData[j][spanIndex];
986                                 int headerEndIdx = spanCell.getLevelSpan() + spanIndex;
987                                 if (headerEndIdx > lastIndex)
988                                 {
989                                     headerEndIdx = lastIndex;
990                                 }
991                                 firstHeaders[j] = HeaderCell.createLevelSpanCopy(spanCell, headerEndIdx - firstIndex);
992                             }
993                         }
994                     }
995                 }
996
997                 if (lastIndex < headersData[0].length)
998                 {
999                     for (int j = 0; j < groups.length; ++j)
1000                    {
1001                        HeaderCell header = headersData[j][lastIndex];
1002
1003                        if (header == null)
1004                        {
1005                            int spanIndex = getSpanIndex(lastIndex, j, headersData);
1006                            if (spanIndex >= firstIndex)
1007                            {
1008                                HeaderCell spanCell = headersData[j][spanIndex];
1009                                HeaderCell[] headerCells = (HeaderCell[]) headers.get(spanIndex - firstIndex);
1010                                headerCells[j] = HeaderCell.createLevelSpanCopy(spanCell, lastIndex - spanIndex);
1011                            }
1012                        }
1013                    }
1014                }
1015            }
1016
1017            return headers;
1018        }
1019
1020        
1021        protected int getSpanIndex(int i, int j, HeaderCell[][] headersData)
1022        {
1023            int spanIndex = i - 1;
1024            while (spanIndex >= 0 && headersData[j][spanIndex] == null)
1025            {
1026                --spanIndex;
1027            }
1028
1029            if (spanIndex >= 0)
1030            {
1031                HeaderCell spanCell = headersData[j][spanIndex];
1032                int span = spanCell.getLevelSpan();
1033
1034                if (span > i - spanIndex)
1035                {
1036                    return spanIndex;
1037                }
1038            }
1039
1040            return -1;
1041        }
1042        
1043        
1044        protected void fillNoDataCell(int availableHeight) throws JRException
1045        {
1046            if (whenNoDataCell == null)
1047            {
1048                noDataCellPrinted = true;
1049            }
1050            else
1051            {
1052                if (availableHeight < whenNoDataCell.getHeight())
1053                {
1054                    willOverflow = true;
1055                }
1056                else
1057                {
1058                    whenNoDataCell.evaluate(JRExpression.EVALUATION_DEFAULT);
1059                    whenNoDataCell.prepare(availableHeight - whenNoDataCell.getHeight());
1060                    
1061                    willOverflow = whenNoDataCell.willOverflow();
1062                    
1063                    if (!willOverflow)
1064                    {
1065                        whenNoDataCell.setX(0);
1066                        whenNoDataCell.setY(0);
1067                        
1068                        JRPrintFrame printCell = whenNoDataCell.fill();
1069                        List JavaDoc noDataRow = new ArrayList JavaDoc(1);
1070                        noDataRow.add(printCell);
1071                        addPrintRow(noDataRow);
1072                        
1073                        yOffset += whenNoDataCell.getPrintHeight();
1074                        noDataCellPrinted = true;
1075                    }
1076                }
1077            }
1078        }
1079        
1080
1081        protected List JavaDoc fillColumnHeaders(int rowHeadersXOffset, int availableHeight) throws JRException
1082        {
1083            JRFillCellContents[][] columnHeaderRows = new JRFillCellContents[columnGroups.length][lastColumnIndex - startColumnIndex + 1];
1084            
1085            rowYs.clear();
1086            rowYs.add(new Integer JavaDoc(0));
1087            
1088            if (printRowHeaders && headerCell != null)
1089            {
1090                JRFillCellContents contents = fillHeader(availableHeight);
1091
1092                if (willOverflow)
1093                {
1094                    return null;
1095                }
1096
1097                columnHeaderRows[columnGroups.length - 1][0] = contents;
1098            }
1099            
1100            rows:
1101            for (rowIdx = 0; rowIdx < columnGroups.length; rowIdx++)
1102            {
1103                for (int columnIdx = startColumnIndex; columnIdx < lastColumnIndex; ++columnIdx)
1104                {
1105                    HeaderCell[] headers = (HeaderCell[]) columnHeaders.get(columnIdx - startColumnIndex);
1106                    HeaderCell cell = headers[rowIdx];
1107                    
1108                    if (cell != null)
1109                    {
1110                        JRFillCellContents contents = prepareColumnHeader(cell, columnIdx, rowHeadersXOffset, availableHeight);
1111                        columnHeaderRows[rowIdx + cell.getDepthSpan() - 1][columnIdx - startColumnIndex + 1] = contents;
1112                        
1113                        if (willOverflow)
1114                        {
1115                            break rows;
1116                        }
1117                    }
1118                }
1119
1120                int rowStretchHeight = stretchColumnHeadersRow(columnHeaderRows[rowIdx]);
1121                rowYs.add(new Integer JavaDoc(((Integer JavaDoc) rowYs.get(rowIdx)).intValue() + rowStretchHeight));
1122            }
1123            
1124            List JavaDoc headerRows;
1125            if (willOverflow)
1126            {
1127                headerRows = null;
1128                releaseColumnHeaderCells(columnHeaderRows);
1129            }
1130            else
1131            {
1132                headerRows = fillColumnHeaders(columnHeaderRows);
1133                yOffset += ((Integer JavaDoc) rowYs.get(columnGroups.length)).intValue();
1134            }
1135
1136            resetVariables();
1137            
1138            return headerRows;
1139        }
1140
1141        
1142        private void setCountVars(int rowIdx, int colIdx)
1143        {
1144            if (rowIdx == -1)
1145            {
1146                rowCountVar.setValue(null);
1147            }
1148            else
1149            {
1150                rowCountVar.setValue(new Integer JavaDoc(rowCount[rowIdx]));
1151            }
1152            
1153            if (colIdx == -1)
1154            {
1155                colCountVar.setValue(null);
1156            }
1157            else
1158            {
1159                colCountVar.setValue(new Integer JavaDoc(columnCount[colIdx]));
1160            }
1161        }
1162        
1163        private JRFillCellContents fillHeader(int availableHeight) throws JRException
1164        {
1165            setCountVars(-1, -1);
1166            
1167            JRFillCellContents contents = headerCell.getWorkingClone();
1168            contents.evaluate(JRExpression.EVALUATION_DEFAULT);
1169            contents.prepare(availableHeight - headerCell.getHeight());
1170            
1171            willOverflow = contents.willOverflow();
1172            
1173            if (!willOverflow)
1174            {
1175                contents.setX(0);
1176                contents.setY(yOffset);
1177                contents.setVerticalSpan(columnGroups.length);
1178            }
1179            return contents;
1180        }
1181        
1182        private JRFillCellContents prepareColumnHeader(HeaderCell cell, int columnIdx, int xOffset, int availableHeight) throws JRException
1183        {
1184            JRFillCrosstabColumnGroup group = columnGroups[rowIdx];
1185            JRFillCellContents contents = cell.isTotal() ? group.getFillTotalHeader() : group.getFillHeader();
1186
1187            int width = columnXOffsets[columnIdx + cell.getLevelSpan()] - columnXOffsets[columnIdx];
1188            int height = contents.getHeight();
1189            
1190            if (width <= 0 || height <= 0)
1191            {
1192                return null;
1193            }
1194            
1195            JRFillCellContents preparedContents = null;
1196            
1197            int rowY = ((Integer JavaDoc) rowYs.get(rowIdx)).intValue();
1198            int cellAvailableStretch = availableHeight - rowY - height;
1199            
1200            if (cellAvailableStretch >= 0)
1201            {
1202                setCountVars(-1, columnIdx);
1203                setGroupVariables(columnGroups, cell.getBucketValues());
1204                
1205                contents = contents.getTransformedContents(width, height, group.getPosition(), JRCellContents.POSITION_Y_TOP);
1206                contents = contents.getBoxContents(columnIdx == startColumnIndex && (!printRowHeaders || headerCell == null), false);
1207                contents = contents.getWorkingClone();
1208
1209                contents.evaluate(JRExpression.EVALUATION_DEFAULT);
1210                contents.prepare(cellAvailableStretch);
1211
1212                if (contents.willOverflow())
1213                {
1214                    willOverflow = true;
1215                }
1216                else
1217                {
1218                    contents.setX(columnXOffsets[columnIdx] - columnXOffsets[startColumnIndex] + xOffset);
1219                    contents.setY(rowY + yOffset);
1220                    contents.setVerticalSpan(cell.getDepthSpan());
1221                    
1222                    preparedContents = contents;
1223                }
1224            }
1225            else
1226            {
1227                willOverflow = true;
1228            }
1229            
1230            return preparedContents;
1231        }
1232
1233        
1234        private int stretchColumnHeadersRow(JRFillCellContents[] headers)
1235        {
1236            int rowY = ((Integer JavaDoc) rowYs.get(rowIdx)).intValue();
1237            
1238            int rowStretchHeight = 0;
1239            for (int j = 0; j < headers.length; j++)
1240            {
1241                JRFillCellContents contents = headers[j];
1242                
1243                if (contents != null)
1244                {
1245                    int startRowY = rowY;
1246                    if (contents.getVerticalSpan() > 1)
1247                    {
1248                        startRowY = ((Integer JavaDoc) rowYs.get(rowIdx - contents.getVerticalSpan() + 1)).intValue();
1249                    }
1250                    
1251                    int height = contents.getPrintHeight() - rowY + startRowY;
1252                    
1253                    if (height > rowStretchHeight)
1254                    {
1255                        rowStretchHeight = height;
1256                    }
1257                }
1258            }
1259            
1260            for (int j = 0; j < headers.length; j++)
1261            {
1262                JRFillCellContents contents = headers[j];
1263                
1264                if (contents != null)
1265                {
1266                    int startRowY = rowY;
1267                    if (contents.getVerticalSpan() > 1)
1268                    {
1269                        startRowY = ((Integer JavaDoc) rowYs.get(rowIdx - contents.getVerticalSpan() + 1)).intValue();
1270                    }
1271                    
1272                    contents.stretchTo(rowStretchHeight + rowY - startRowY);
1273                }
1274            }
1275            
1276            return rowStretchHeight;
1277        }
1278
1279        
1280        private List JavaDoc fillColumnHeaders(JRFillCellContents[][] columnHeaderRows) throws JRException
1281        {
1282            List JavaDoc headerRows = new ArrayList JavaDoc(columnGroups.length);
1283            
1284            for (int i = 0; i < columnHeaderRows.length; ++i)
1285            {
1286                List JavaDoc headerRow = new ArrayList JavaDoc(lastColumnIndex - startColumnIndex);
1287                headerRows.add(headerRow);
1288                
1289                for (int j = 0; j < columnHeaderRows[i].length; j++)
1290                {
1291                    JRFillCellContents contents = columnHeaderRows[i][j];
1292                    
1293                    if (contents != null)
1294                    {
1295                        headerRow.add(contents.fill());
1296                        contents.releaseWorkingClone();
1297                    }
1298                }
1299            }
1300            
1301            return headerRows;
1302        }
1303
1304        private void releaseColumnHeaderCells(JRFillCellContents[][] columnHeaderRows) throws JRException
1305        {
1306            for (int i = 0; i < columnHeaderRows.length; ++i)
1307            {
1308                for (int j = 0; j < columnHeaderRows[i].length; j++)
1309                {
1310                    JRFillCellContents contents = columnHeaderRows[i][j];
1311                    
1312                    if (contents != null)
1313                    {
1314                        contents.rewind();
1315                        contents.releaseWorkingClone();
1316                    }
1317                }
1318            }
1319        }
1320        
1321        protected int fillRows(int xOffset, int availableHeight) throws JRException
1322        {
1323            rowYs.clear();
1324            rowYs.add(new Integer JavaDoc(0));
1325
1326            for (rowIdx = 0; rowIdx < cellData.length - startRowIndex; ++rowIdx)
1327            {
1328                initPreparedRow();
1329                
1330                prepareRow(xOffset, availableHeight);
1331
1332                if (willOverflow)
1333                {
1334                    break;
1335                }
1336                
1337                fillRow();
1338                
1339                rowYs.add(new Integer JavaDoc(((Integer JavaDoc) rowYs.get(rowIdx)).intValue() + preparedRowHeight));
1340            }
1341            
1342            if (rowIdx < cellData.length - startRowIndex)//overflow
1343
{
1344                releasePreparedRow();
1345                
1346                if (printRowHeaders)
1347                {
1348                    fillContinuingRowHeaders(xOffset, availableHeight);
1349                }
1350            }
1351            
1352            yOffset += ((Integer JavaDoc) rowYs.get(rowIdx)).intValue();
1353
1354            return rowIdx + startRowIndex;
1355        }
1356
1357        private void initPreparedRow()
1358        {
1359            preparedRow.clear();
1360            preparedRowHeight = 0;
1361        }
1362
1363        private void removeFilledRows(int rowsToRemove)
1364        {
1365            if (rowsToRemove > 0)
1366            {
1367                for (int i = 0; i < rowsToRemove; ++i)
1368                {
1369                    printRows.remove(printRows.size() - 1);
1370                    rowYs.remove(rowYs.size() - 1);
1371                }
1372                
1373                rowIdx -= rowsToRemove;
1374            }
1375        }
1376
1377        private void releasePreparedRow() throws JRException
1378        {
1379            for (Iterator JavaDoc it = preparedRow.iterator(); it.hasNext();)
1380            {
1381                JRFillCellContents cell = (JRFillCellContents) it.next();
1382                cell.rewind();
1383                cell.releaseWorkingClone();
1384            }
1385            
1386            preparedRow.clear();
1387        }
1388
1389        private void fillRow() throws JRException
1390        {
1391            int rowY = ((Integer JavaDoc) rowYs.get(rowIdx)).intValue();
1392            
1393            List JavaDoc rowPrints = new ArrayList JavaDoc(preparedRow.size());
1394            for (Iterator JavaDoc it = preparedRow.iterator(); it.hasNext();)
1395            {
1396                JRFillCellContents cell = (JRFillCellContents) it.next();
1397                
1398                int spanHeight = 0;
1399                if (cell.getVerticalSpan() > 1)
1400                {
1401                    spanHeight = rowY - ((Integer JavaDoc) rowYs.get(rowIdx - cell.getVerticalSpan() + 1)).intValue();
1402                }
1403                
1404                cell.stretchTo(preparedRowHeight + spanHeight);
1405                rowPrints.add(cell.fill());
1406                
1407                cell.releaseWorkingClone();
1408            }
1409            
1410            addPrintRow(rowPrints);
1411        }
1412        
1413        private void prepareRow(int xOffset, int availableHeight) throws JRException
1414        {
1415            for (int col = startColumnIndex; col < lastColumnIndex; ++col)
1416            {
1417                CrosstabCell data = cellData[rowIdx + startRowIndex][col];
1418                boolean overflow = prepareDataCell(data, col, availableHeight, xOffset);
1419                
1420                if (overflow)
1421                {
1422                    willOverflow = true;
1423                    return;
1424                }
1425            }
1426            
1427            resetVariables();
1428            
1429            if (printRowHeaders)
1430            {
1431                for (int j = 0; j < rowGroups.length; j++)
1432                {
1433                    HeaderCell cell = rowHeadersData[j][rowIdx + startRowIndex];
1434                    
1435                    boolean overflow = false;
1436                    if (cell == null)
1437                    {
1438                        overflow = prepareClosingRowHeader(j, availableHeight);
1439                    }
1440                    else
1441                    {
1442                        if (cell.getLevelSpan() > 1)
1443                        {
1444                            spanHeaders[j] = cell;
1445                            spanHeadersStart[j] = rowIdx + startRowIndex;
1446                            continue;
1447                        }
1448
1449                        overflow = prepareRowHeader(j, cell, 1, availableHeight);
1450                    }
1451                    
1452                    if (overflow)
1453                    {
1454                        willOverflow = true;
1455                        return;
1456                    }
1457                }
1458                
1459                resetVariables();
1460            }
1461        }
1462        
1463        private boolean prepareDataCell(CrosstabCell data, int column, int availableHeight, int xOffset) throws JRException
1464        {
1465            int rowY = ((Integer JavaDoc) rowYs.get(rowIdx)).intValue();
1466            
1467            JRFillCrosstabCell cell = crossCells[data.getRowTotalGroupIndex()][data.getColumnTotalGroupIndex()];
1468            JRFillCellContents contents = cell == null ? null : cell.getFillContents();
1469            if (contents == null || contents.getWidth() <= 0 || contents.getHeight() <= 0)
1470            {
1471                return false;
1472            }
1473            
1474            int cellAvailableStretch = availableHeight - rowY - contents.getHeight();
1475            boolean overflow = cellAvailableStretch < 0;
1476            if (!overflow)
1477            {
1478                boolean leftEmpty = startColumnIndex != 0 && !isRepeatRowHeaders();
1479                boolean topEmpty = startRowIndex != 0 && !isRepeatColumnHeaders();
1480                
1481                setCountVars(rowIdx + startRowIndex, column);
1482                setGroupVariables(rowGroups, data.getRowBucketValues());
1483                setGroupVariables(columnGroups, data.getColumnBucketValues());
1484                setMeasureVariables(data);
1485                
1486                contents = contents.getBoxContents(leftEmpty && column == startColumnIndex, topEmpty && rowIdx == 0);
1487                contents = contents.getWorkingClone();
1488                
1489                contents.evaluate(JRExpression.EVALUATION_DEFAULT);
1490                contents.prepare(cellAvailableStretch);
1491                                
1492                preparedRow.add(contents);
1493                
1494                overflow = contents.willOverflow();
1495                
1496                if (!overflow)
1497                {
1498                    contents.setX(columnXOffsets[column] - columnXOffsets[startColumnIndex] + xOffset);
1499                    contents.setY(rowY + yOffset);
1500
1501                    int rowCellHeight = contents.getPrintHeight();
1502                    if (rowCellHeight > preparedRowHeight)
1503                    {
1504                        preparedRowHeight = rowCellHeight;
1505                    }
1506                }
1507            }
1508            
1509            return overflow;
1510        }
1511
1512        private boolean prepareRowHeader(int rowGroup, HeaderCell cell, int vSpan, int availableHeight) throws JRException
1513        {
1514            JRFillCrosstabRowGroup group = rowGroups[rowGroup];
1515            JRFillCellContents contents = cell.isTotal() ? group.getFillTotalHeader() : group.getFillHeader();
1516
1517            if (contents.getWidth() <= 0 || contents.getHeight() <= 0)
1518            {
1519                return false;
1520            }
1521            
1522            int spanHeight = 0;
1523            int headerY = ((Integer JavaDoc) rowYs.get(rowIdx - vSpan + 1)).intValue();
1524            if (vSpan > 1)
1525            {
1526                spanHeight += ((Integer JavaDoc) rowYs.get(rowIdx)).intValue() - headerY;
1527            }
1528            int rowHeight = spanHeight + preparedRowHeight;
1529            
1530            boolean stretchContents = group.getPosition() == JRCellContents.POSITION_Y_STRETCH;
1531            int contentsHeight = stretchContents ? rowHeight : contents.getHeight();
1532            
1533            int cellAvailableStretch = availableHeight - headerY - contentsHeight;
1534            boolean headerOverflow = cellAvailableStretch < 0 || rowHeight < contents.getHeight();
1535            
1536            if (!headerOverflow)
1537            {
1538                setCountVars(rowIdx + startRowIndex - vSpan + 1, -1);
1539                setGroupVariables(rowGroups, cell.getBucketValues());
1540
1541                if (stretchContents)
1542                {
1543                    contents = contents.getTransformedContents(contents.getWidth(), rowHeight, JRCellContents.POSITION_X_LEFT, JRCellContents.POSITION_Y_STRETCH);
1544                }
1545                contents = contents.getBoxContents(false, rowIdx + 1 == vSpan && (!printColumnHeaders || headerCell == null));
1546                contents.getWorkingClone();
1547
1548                contents.evaluate(JRExpression.EVALUATION_DEFAULT);
1549                contents.prepare(cellAvailableStretch);
1550                
1551                preparedRow.add(contents);
1552
1553                headerOverflow = contents.willOverflow();
1554                
1555                if (!headerOverflow)
1556                {
1557                    contents.setX(rowHeadersXOffsets[rowGroup]);
1558                    contents.setY(headerY + yOffset);
1559                    contents.setVerticalSpan(vSpan);
1560                    
1561                    int rowCellHeight = contents.getPrintHeight() - spanHeight;
1562                    if (rowCellHeight > preparedRowHeight)
1563                    {
1564                        preparedRowHeight = rowCellHeight;
1565                    }
1566                }
1567            }
1568            
1569            if (headerOverflow)
1570            {
1571                removeFilledRows(vSpan - 1);
1572            }
1573            
1574            return headerOverflow;
1575        }
1576
1577        private boolean prepareClosingRowHeader(int rowGroup, int availableHeight) throws JRException
1578        {
1579            if (rowGroup < rowGroups.length - 1 &&
1580                    spanHeaders[rowGroup] != null &&
1581                    spanHeaders[rowGroup].getLevelSpan() + spanHeadersStart[rowGroup] == rowIdx + startRowIndex + 1)
1582            {
1583                HeaderCell cell = spanHeaders[rowGroup];
1584                int vSpan = cell.getLevelSpan();
1585                if (spanHeadersStart[rowGroup] < startRowIndex)//continuing from the prev page
1586
{
1587                    vSpan += spanHeadersStart[rowGroup] - startRowIndex;
1588                }
1589                spanHeaders[rowGroup] = null;
1590                
1591                return prepareRowHeader(rowGroup, cell, vSpan, availableHeight);
1592            }
1593            
1594            return false;
1595        }
1596
1597        private void removeExceedingSpanHeaders()
1598        {
1599            for (int j = rowGroups.length - 2; j >= 0; --j)
1600            {
1601                if (spanHeaders[j] != null && spanHeadersStart[j] >= rowIdx + startRowIndex)
1602                {
1603                    spanHeaders[j] = null;
1604                }
1605            }
1606        }
1607
1608        private void setBackSpanHeaders()
1609        {
1610            for (int j = rowGroups.length - 2; j >= 0 && spanHeaders[j] == null; --j)
1611            {
1612                int spanIndex = getSpanIndex(rowIdx + startRowIndex, j, rowHeadersData);
1613                
1614                if (spanIndex >= 0)
1615                {
1616                    spanHeaders[j] = rowHeadersData[j][spanIndex];
1617                    spanHeadersStart[j] = spanIndex;
1618                }
1619            }
1620        }
1621
1622        private void fillContinuingRowHeaders(int xOffset, int availableHeight) throws JRException
1623        {
1624            boolean done = false;
1625            breakCrosstab:
1626            do
1627            {
1628                removeExceedingSpanHeaders();
1629                
1630                if (!rowBreakable[rowIdx + startRowIndex])
1631                {
1632                    removeFilledRows(1);
1633                    setBackSpanHeaders();
1634                    continue;
1635                }
1636
1637                initPreparedRow();
1638                
1639                //fill continuing headers
1640
for (int j = 0; j < rowGroups.length - 1; ++j)
1641                {
1642                    if (spanHeaders[j] != null)
1643                    {
1644                        boolean headerOverflow = prepareContinuingRowHeader(j, availableHeight);
1645                        
1646                        if (headerOverflow)
1647                        {
1648                            releasePreparedRow();
1649                            continue breakCrosstab;
1650                        }
1651                    }
1652                }
1653
1654                if (!preparedRow.isEmpty())
1655                {
1656                    int lastRowHeight = ((Integer JavaDoc) rowYs.get(rowIdx)).intValue() - ((Integer JavaDoc) rowYs.get(rowIdx - 1)).intValue();
1657                    
1658                    if (preparedRowHeight > lastRowHeight)//need to stretch already filled row by refilling
1659
{
1660                        refillLastRow(xOffset, availableHeight);
1661                    }
1662                    else
1663                    {
1664                        fillContinuingHeaders(lastRowHeight);
1665                    }
1666                }
1667                
1668                done = true;
1669            }
1670            while (!done && rowIdx > 0);
1671        }
1672
1673        private void fillContinuingHeaders(int lastRowHeight) throws JRException
1674        {
1675            int nextToLastHeaderY = ((Integer JavaDoc) rowYs.get(rowIdx - 1)).intValue();
1676            List JavaDoc lastPrintRow = getLastPrintRow();
1677            
1678            for (int j = 0; j < preparedRow.size(); ++j)
1679            {
1680                JRFillCellContents contents = (JRFillCellContents) preparedRow.get(j);
1681                
1682                int headerY = ((Integer JavaDoc) rowYs.get(rowIdx - contents.getVerticalSpan())).intValue();
1683                
1684                contents.stretchTo(nextToLastHeaderY - headerY + lastRowHeight);
1685                lastPrintRow.add(contents.fill());
1686                contents.releaseWorkingClone();
1687            }
1688        }
1689
1690        private void refillLastRow(int xOffset, int availableHeight) throws JRException
1691        {
1692            removeFilledRows(1);
1693            setBackSpanHeaders();
1694            
1695            prepareRow(xOffset, availableHeight);
1696            fillRow();
1697            
1698            rowYs.add(new Integer JavaDoc(((Integer JavaDoc) rowYs.get(rowIdx)).intValue() + preparedRowHeight));
1699            ++rowIdx;
1700        }
1701
1702        private boolean prepareContinuingRowHeader(int rowGroup, int availableHeight) throws JRException
1703        {
1704            HeaderCell cell = spanHeaders[rowGroup];
1705            int vSpan = rowIdx + startRowIndex - spanHeadersStart[rowGroup];
1706
1707            if (spanHeadersStart[rowGroup] < startRowIndex)//continuing from the prev page
1708
{
1709                vSpan += spanHeadersStart[rowGroup] - startRowIndex;
1710            }
1711
1712            int headerY = ((Integer JavaDoc) rowYs.get(rowIdx - vSpan)).intValue();
1713            int lastHeaderY = ((Integer JavaDoc) rowYs.get(rowIdx)).intValue();
1714            int headerHeight = lastHeaderY - headerY;
1715            int nextToLastHeaderY = ((Integer JavaDoc) rowYs.get(rowIdx - 1)).intValue();
1716            int stretchHeight = nextToLastHeaderY - headerY;
1717            
1718            JRFillCrosstabRowGroup group = rowGroups[rowGroup];
1719            JRFillCellContents contents = cell.isTotal() ? group.getFillTotalHeader() : group.getFillHeader();
1720            
1721            boolean stretchContents = group.getPosition() == JRCellContents.POSITION_Y_STRETCH;
1722            int contentsHeight = stretchContents ? headerHeight : contents.getHeight();
1723            
1724            int cellAvailableStretch = availableHeight - headerY - contentsHeight;
1725            boolean headerOverflow = cellAvailableStretch < 0 || headerHeight < contents.getHeight();
1726            if (!headerOverflow)
1727            {
1728                setCountVars(rowIdx + startRowIndex - vSpan, -1);
1729                setGroupVariables(rowGroups, cell.getBucketValues());
1730
1731                if (stretchContents)
1732                {
1733                    contents = contents.getTransformedContents(contents.getWidth(), headerHeight, JRCellContents.POSITION_X_LEFT, JRCellContents.POSITION_Y_STRETCH);
1734                }
1735                
1736                contents = contents.getBoxContents(false, rowIdx == vSpan && (!printColumnHeaders || headerCell == null));
1737                contents.getWorkingClone();
1738
1739                contents.evaluate(JRExpression.EVALUATION_DEFAULT);
1740                contents.prepare(cellAvailableStretch);
1741                
1742                preparedRow.add(contents);
1743
1744                headerOverflow = contents.willOverflow();
1745
1746                if (!headerOverflow)
1747                {
1748                    contents.setX(rowHeadersXOffsets[rowGroup]);
1749                    contents.setY(headerY + yOffset);
1750                    contents.setVerticalSpan(vSpan);
1751                    
1752                    int rowHeight = contents.getPrintHeight() - stretchHeight;
1753                    if (rowHeight > preparedRowHeight)
1754                    {
1755                        preparedRowHeight = rowHeight;
1756                    }
1757                }
1758            }
1759
1760            if (headerOverflow)
1761            {
1762                removeFilledRows(vSpan);
1763            }
1764            
1765            return headerOverflow;
1766        }
1767        
1768        protected void addPrintRow(List JavaDoc printRow)
1769        {
1770            printRows.add(printRow);
1771        }
1772        
1773        protected List JavaDoc getLastPrintRow()
1774        {
1775            return (List JavaDoc) printRows.get(printRows.size() - 1);
1776        }
1777
1778        protected List JavaDoc getPrintElements()
1779        {
1780            List JavaDoc prints = new ArrayList JavaDoc();
1781            
1782            for (Iterator JavaDoc it = printRows.iterator(); it.hasNext();)
1783            {
1784                List JavaDoc rowPrints = (List JavaDoc) it.next();
1785                prints.addAll(rowPrints);
1786            }
1787            
1788            return prints;
1789        }
1790
1791        protected void setGroupVariables(JRFillCrosstabGroup[] groups, Bucket[] bucketValues)
1792        {
1793            for (int i = 0; i < groups.length; i++)
1794            {
1795                Object JavaDoc value = null;
1796                if (bucketValues[i] != null && !bucketValues[i].isTotal())
1797                {
1798                    value = bucketValues[i].getValue();
1799                }
1800                groups[i].getFillVariable().setValue(value);
1801            }
1802        }
1803
1804        protected void setMeasureVariables(CrosstabCell cell)
1805        {
1806            MeasureValue[] values = cell.getMesureValues();
1807            for (int i = 0; i < measures.length; i++)
1808            {
1809                Object JavaDoc value = measureValue(values, i);
1810                measures[i].getFillVariable().setValue(value);
1811            }
1812            
1813            MeasureValue[][][] totals = cell.getTotals();
1814            for (int row = 0; row <= rowGroups.length; row++)
1815            {
1816                for (int col = 0; col <= columnGroups.length; col++)
1817                {
1818                    MeasureValue[] vals = totals[row][col];
1819                    if (retrieveTotal[row][col])
1820                    {
1821                        for (int m = 0; m < measures.length; m++)
1822                        {
1823                            JRFillVariable totalVar = totalVariables[row][col][m];
1824                            Object JavaDoc value = measureValue(vals, m);
1825                            totalVar.setValue(value);
1826                        }
1827                    }
1828                }
1829            }
1830        }
1831
1832        
1833        protected Object JavaDoc measureValue(MeasureValue[] values, int measureIdx)
1834        {
1835            Object JavaDoc value;
1836            if (measures[measureIdx].getPercentageOfType() == JRCrosstabMeasure.PERCENTAGE_TYPE_GRAND_TOTAL)
1837            {
1838                if (values[measureIdx].isInitialized())
1839                {
1840                    value = values[measureIdx].getValue();
1841                }
1842                else
1843                {
1844                    value = measures[measureIdx].getPercentageCalculator().calculatePercentage(values[measureIdx], grandTotals[measureIdx]);
1845                }
1846            }
1847            else
1848            {
1849                value = values[measureIdx].getValue();
1850            }
1851            return value;
1852        }
1853
1854        
1855        protected void resetVariables()
1856        {
1857            for (int i = 0; i < rowGroups.length; i++)
1858            {
1859                rowGroups[i].getFillVariable().setValue(null);
1860            }
1861            
1862            for (int i = 0; i < columnGroups.length; i++)
1863            {
1864                columnGroups[i].getFillVariable().setValue(null);
1865            }
1866            
1867            for (int i = 0; i < measures.length; i++)
1868            {
1869                measures[i].getFillVariable().setValue(null);
1870            }
1871            
1872            for (int row = 0; row <= rowGroups.length; ++row)
1873            {
1874                for (int col = 0; col <= columnGroups.length; ++col)
1875                {
1876                    if (retrieveTotal[row][col])
1877                    {
1878                        for (int i = 0; i < measures.length; i++)
1879                        {
1880                            totalVariables[row][col][i].setValue(null);
1881                        }
1882                    }
1883                }
1884            }
1885        }
1886    }
1887
1888    public int getColumnBreakOffset()
1889    {
1890        return parentCrosstab.getColumnBreakOffset();
1891    }
1892
1893    public boolean isRepeatColumnHeaders()
1894    {
1895        return parentCrosstab.isRepeatColumnHeaders();
1896    }
1897
1898    public boolean isRepeatRowHeaders()
1899    {
1900        return parentCrosstab.isRepeatRowHeaders();
1901    }
1902
1903    public JRCrosstabCell[][] getCells()
1904    {
1905        return crossCells;
1906    }
1907
1908    public JRCellContents getWhenNoDataCell()
1909    {
1910        return whenNoDataCell;
1911    }
1912
1913    public JRCrosstabParameter[] getParameters()
1914    {
1915        return parameters;
1916    }
1917
1918    public JRExpression getParametersMapExpression()
1919    {
1920        return parentCrosstab.getParametersMapExpression();
1921    }
1922
1923    
1924    public JRElement getElementByKey(String JavaDoc elementKey)
1925    {
1926        return JRBaseCrosstab.getElementByKey(this, elementKey);
1927    }
1928
1929    public JRCloneable createClone(JRFillCloneFactory factory)
1930    {
1931        //not needed
1932
return null;
1933    }
1934
1935    public JRCellContents getHeaderCell()
1936    {
1937        return headerCell;
1938    }
1939
1940    public JRVariable[] getVariables()
1941    {
1942        return variables;
1943    }
1944}
1945
Popular Tags