KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > net > sf > jasperreports > crosstabs > fill > calculation > BucketingService


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.crosstabs.fill.calculation;
29
30 import java.util.ArrayList JavaDoc;
31 import java.util.Collections JavaDoc;
32 import java.util.Iterator JavaDoc;
33 import java.util.LinkedList JavaDoc;
34 import java.util.List JavaDoc;
35 import java.util.ListIterator JavaDoc;
36 import java.util.Map JavaDoc;
37 import java.util.TreeMap JavaDoc;
38 import java.util.Map.Entry;
39
40 import net.sf.jasperreports.crosstabs.fill.calculation.BucketDefinition.Bucket;
41 import net.sf.jasperreports.crosstabs.fill.calculation.MeasureDefinition.MeasureValue;
42 import net.sf.jasperreports.engine.JRException;
43 import net.sf.jasperreports.engine.JRVariable;
44 import net.sf.jasperreports.engine.fill.JRCalculable;
45
46 /**
47  * Crosstab bucketing engine.
48  *
49  * @author Lucian Chirita (lucianc@users.sourceforge.net)
50  * @version $Id: BucketingService.java 1311 2006-06-23 12:19:26 +0300 (Fri, 23 Jun 2006) teodord $
51  */

52 public class BucketingService
53 {
54     protected static final byte DIMENSION_ROW = 0;
55
56     protected static final byte DIMENSION_COLUMN = 1;
57
58     protected static final int DIMENSIONS = 2;
59
60     protected final BucketDefinition[] allBuckets;
61     protected final BucketDefinition[][] buckets;
62
63     protected final int rowBucketCount;
64     protected final int colBucketCount;
65
66     protected final boolean[][] retrieveTotal;
67     private boolean[] rowRetrTotals;
68     private int rowRetrTotalMin;
69     private int rowRetrTotalMax;
70     private int[] rowRetrColMax;
71
72     protected final MeasureDefinition[] measures;
73     protected final int origMeasureCount;
74     protected final int[] measureIndexes;
75
76     protected final boolean sorted;
77
78     protected final BucketMap bucketValueMap;
79     protected long dataCount;
80     protected boolean processed;
81     
82     protected HeaderCell[][] colHeaders;
83     protected HeaderCell[][] rowHeaders;
84     protected CrosstabCell[][] cells;
85
86     private final MeasureValue[] zeroUserMeasureValues;
87
88
89     
90     /**
91      * Creates a crosstab bucketing engine.
92      *
93      * @param rowBuckets the row bucket definitions
94      * @param columnBuckets the column bucket definitions
95      * @param measures the measure definitions
96      * @param sorted whether the data is presorted
97      * @param retrieveTotal totals to retrieve along with the cell values
98      */

99     public BucketingService(List JavaDoc rowBuckets, List JavaDoc columnBuckets, List JavaDoc measures, boolean sorted, boolean[][] retrieveTotal)
100     {
101         this.sorted = sorted;
102
103         buckets = new BucketDefinition[DIMENSIONS][];
104         
105         rowBucketCount = rowBuckets.size();
106         buckets[DIMENSION_ROW] = new BucketDefinition[rowBucketCount];
107         rowBuckets.toArray(buckets[DIMENSION_ROW]);
108         
109         colBucketCount = columnBuckets.size();
110         buckets[DIMENSION_COLUMN] = new BucketDefinition[colBucketCount];
111         columnBuckets.toArray(buckets[DIMENSION_COLUMN]);
112
113         allBuckets = new BucketDefinition[rowBucketCount + colBucketCount];
114         System.arraycopy(buckets[DIMENSION_ROW], 0, allBuckets, 0, rowBucketCount);
115         System.arraycopy(buckets[DIMENSION_COLUMN], 0, allBuckets, rowBucketCount, colBucketCount);
116
117         origMeasureCount = measures.size();
118         List JavaDoc measuresList = new ArrayList JavaDoc(measures.size() * 2);
119         List JavaDoc measureIndexList = new ArrayList JavaDoc(measures.size() * 2);
120         for (int i = 0; i < measures.size(); ++i)
121         {
122             MeasureDefinition measure = (MeasureDefinition) measures.get(i);
123             addMeasure(measure, i, measuresList, measureIndexList);
124         }
125         this.measures = new MeasureDefinition[measuresList.size()];
126         measuresList.toArray(this.measures);
127         this.measureIndexes = new int[measureIndexList.size()];
128         for (int i = 0; i < measureIndexes.length; ++i)
129         {
130             measureIndexes[i] = ((Integer JavaDoc) measureIndexList.get(i)).intValue();
131         }
132
133         this.retrieveTotal = retrieveTotal;
134         checkTotals();
135         
136         bucketValueMap = createBucketMap(0);
137         
138         zeroUserMeasureValues = initUserMeasureValues();
139     }
140
141
142     protected void checkTotals()
143     {
144         rowRetrTotalMin = rowBucketCount + 1;
145         rowRetrTotalMax = -1;
146         rowRetrTotals = new boolean[rowBucketCount + 1];
147         rowRetrColMax = new int[rowBucketCount + 1];
148         for (int row = 0; row <= rowBucketCount; ++row)
149         {
150             rowRetrColMax[row] = -1;
151             boolean total = false;
152             for (int col = 0; col <= colBucketCount; ++col)
153             {
154                 if (retrieveTotal[row][col])
155                 {
156                     total = true;
157                     rowRetrColMax[row] = col;
158                 }
159             }
160             
161             rowRetrTotals[row] = total;
162             if (total)
163             {
164                 if (row < rowRetrTotalMin)
165                 {
166                     rowRetrTotalMin = row;
167                 }
168                 rowRetrTotalMax = row;
169                 
170                 if (row < rowBucketCount)
171                 {
172                     allBuckets[row].setComputeTotal();
173                 }
174             }
175         }
176         
177         for (int col = 0; col < colBucketCount; ++col)
178         {
179             BucketDefinition colBucket = allBuckets[rowBucketCount + col];
180             if (!colBucket.computeTotal())
181             {
182                 boolean total = false;
183                 for (int row = 0; !total && row <= rowBucketCount; ++row)
184                 {
185                     total = retrieveTotal[row][col];
186                 }
187                 
188                 if (total)
189                 {
190                     colBucket.setComputeTotal();
191                 }
192             }
193         }
194         
195         for (int d = 0; d < DIMENSIONS; ++d)
196         {
197             boolean dTotal = false;
198             
199             for (int i = 0; i < buckets[d].length; ++i)
200             {
201                 if (dTotal)
202                 {
203                     buckets[d][i].setComputeTotal();
204                 }
205                 else
206                 {
207                     dTotal = buckets[d][i].computeTotal();
208                 }
209             }
210         }
211     }
212
213
214     /**
215      * Clears all the accumulated and computed data.
216      */

217     public void clear()
218     {
219         bucketValueMap.clear();
220         processed = false;
221         dataCount = 0;
222     }
223     
224     protected BucketMap createBucketMap(int level)
225     {
226         BucketMap map;
227         if (sorted)
228         {
229             map = new BucketListMap(level, false);
230         }
231         else
232         {
233             map = new BucketTreeMap(level);
234         }
235         return map;
236     }
237     
238     protected BucketListMap createCollectBucketMap(int level)
239     {
240         return new BucketListMap(level, true);
241     }
242
243     protected void addMeasure(MeasureDefinition measure, int index, List JavaDoc measuresList, List JavaDoc measureIndexList)
244     {
245         switch (measure.getCalculation())
246         {
247             case JRVariable.CALCULATION_AVERAGE:
248             case JRVariable.CALCULATION_VARIANCE:
249             {
250                 MeasureDefinition sumMeasure = MeasureDefinition.createHelperMeasure(measure, JRVariable.CALCULATION_SUM);
251                 addMeasure(sumMeasure, index, measuresList, measureIndexList);
252                 MeasureDefinition countMeasure = MeasureDefinition.createHelperMeasure(measure, JRVariable.CALCULATION_COUNT);
253                 addMeasure(countMeasure, index, measuresList, measureIndexList);
254                 break;
255             }
256             case JRVariable.CALCULATION_STANDARD_DEVIATION:
257             {
258                 MeasureDefinition varianceMeasure = MeasureDefinition.createHelperMeasure(measure, JRVariable.CALCULATION_VARIANCE);
259                 addMeasure(varianceMeasure, index, measuresList, measureIndexList);
260                 break;
261             }
262             case JRVariable.CALCULATION_DISTINCT_COUNT:
263             {
264                 MeasureDefinition countMeasure = MeasureDefinition.createDistinctCountHelperMeasure(measure);
265                 addMeasure(countMeasure, index, measuresList, measureIndexList);
266                 break;
267             }
268         }
269
270         measuresList.add(measure);
271         measureIndexList.add(new Integer JavaDoc(index));
272     }
273
274     
275     /**
276      * Feeds data to the engine.
277      *
278      * @param bucketValues the bucket values
279      * @param measureValues the measure values
280      * @throws JRException
281      */

282     public void addData(Object JavaDoc[] bucketValues, Object JavaDoc[] measureValues) throws JRException
283     {
284         if (processed)
285         {
286             throw new JRException("Crosstab data has already been processed.");
287         }
288         
289         ++dataCount;
290         
291         Bucket[] bucketVals = getBucketValues(bucketValues);
292
293         MeasureValue[] values = bucketValueMap.insertMeasureValues(bucketVals);
294
295         for (int i = 0; i < measures.length; ++i)
296         {
297             values[i].addValue(measureValues[measureIndexes[i]]);
298         }
299     }
300
301     protected Bucket[] getBucketValues(Object JavaDoc[] bucketValues)
302     {
303         Bucket[] bucketVals = new Bucket[allBuckets.length];
304
305         for (int i = 0; i < allBuckets.length; ++i)
306         {
307             BucketDefinition bucket = allBuckets[i];
308             Object JavaDoc value = bucketValues[i];
309             bucketVals[i] = bucket.create(value);
310         }
311         
312         return bucketVals;
313     }
314
315     protected MeasureValue[] initMeasureValues()
316     {
317         MeasureValue[] values;
318         values = new MeasureValue[measures.length];
319
320         for (int i = 0; i < measures.length; ++i)
321         {
322             MeasureDefinition measure = measures[i];
323             values[i] = measure.new MeasureValue();
324
325             switch (measure.getCalculation())
326             {
327                 case JRVariable.CALCULATION_AVERAGE:
328                 case JRVariable.CALCULATION_VARIANCE:
329                 {
330                     values[i].setHelper(values[i - 2], JRCalculable.HELPER_SUM);
331                     values[i].setHelper(values[i - 1], JRCalculable.HELPER_COUNT);
332                     break;
333                 }
334                 case JRVariable.CALCULATION_STANDARD_DEVIATION:
335                 {
336                     values[i].setHelper(values[i - 1], JRCalculable.HELPER_VARIANCE);
337                 }
338                 case JRVariable.CALCULATION_DISTINCT_COUNT:
339                 {
340                     values[i].setHelper(values[i - 1], JRCalculable.HELPER_COUNT);
341                 }
342             }
343         }
344         return values;
345     }
346
347     protected MeasureValue[] initUserMeasureValues()
348     {
349         MeasureValue[] vals = new MeasureValue[origMeasureCount];
350         
351         for (int c = 0, i = 0; i < measures.length; ++i)
352         {
353             if (!measures[i].isSystemDefined())
354             {
355                 vals[c] = measures[i].new MeasureValue();
356                 ++c;
357             }
358         }
359         
360         return vals;
361     }
362
363     
364     /**
365      * Processes the data which was fed to the engine.
366      * <p>
367      * This method should be called after the data has been exhausted.
368      * The processing consists of total calculations and crosstab table creation.
369      *
370      * @throws JRException
371      */

372     public void processData() throws JRException
373     {
374         if (!processed)
375         {
376             if (dataCount > 0)
377             {
378                 if (allBuckets[rowBucketCount - 1].computeTotal() || allBuckets[allBuckets.length - 1].computeTotal())
379                 {
380                     computeTotals(bucketValueMap);
381                 }
382
383                 createCrosstab();
384             }
385             
386             processed = true;
387         }
388     }
389
390     
391     /**
392      * Checks whether there is any data accumulated by the engine.
393      *
394      * @return <code>true</code> iff the engine has any accumulated data
395      */

396     public boolean hasData()
397     {
398         return dataCount > 0;
399     }
400     
401     
402     /**
403      * Returns the crosstab column headers.
404      * <p>
405      * {@link #processData() processData()} has to be called before this.
406      *
407      * @return the crosstab column headers
408      */

409     public HeaderCell[][] getColumnHeaders()
410     {
411         return colHeaders;
412     }
413     
414     
415     /**
416      * Returns the crosstab row headers.
417      * <p>
418      * {@link #processData() processData()} has to be called before this.
419      *
420      * @return the crosstab row headers
421      */

422     public HeaderCell[][] getRowHeaders()
423     {
424         return rowHeaders;
425     }
426     
427     
428     /**
429      * Returns the crosstab data cells.
430      * <p>
431      * {@link #processData() processData()} has to be called before this.
432      *
433      * @return the crosstab data cells
434      */

435     public CrosstabCell[][] getCrosstabCells()
436     {
437         return cells;
438     }
439     
440     
441     /**
442      * Returns the measure values for a set of bucket values.
443      *
444      * @param bucketValues the bucket values
445      * @return the measure values corresponding to the bucket values
446      */

447     public MeasureValue[] getMeasureValues(Bucket[] bucketValues)
448     {
449         BucketMap map = bucketValueMap;
450         
451         for (int i = 0; map != null && i < allBuckets.length - 1; ++i)
452         {
453             map = (BucketMap) map.get(bucketValues[i]);
454         }
455         
456         return map == null ? null : (MeasureValue[]) map.get(bucketValues[allBuckets.length - 1]);
457     }
458
459     protected MeasureValue[] getUserMeasureValues(MeasureValue[] values)
460     {
461         MeasureValue[] vals = new MeasureValue[origMeasureCount];
462         
463         for (int c = 0, i = 0; i < measures.length; ++i)
464         {
465             if (!measures[i].isSystemDefined())
466             {
467                 vals[c] = values[i];
468                 ++c;
469             }
470         }
471         
472         return vals;
473     }
474
475     
476     /**
477      * Returns the grand total measure values.
478      *
479      * @return the grand total measure values
480      */

481     public MeasureValue[] getGrandTotals()
482     {
483         BucketMap map = bucketValueMap;
484         
485         for (int i = 0; map != null && i < allBuckets.length - 1; ++i)
486         {
487             map = (BucketMap) map.getTotalEntry().getValue();
488         }
489         
490         return map == null ? null : (MeasureValue[]) map.getTotalEntry().getValue();
491     }
492
493     
494     protected void computeTotals(BucketMap bucketMap) throws JRException
495     {
496         byte dimension = bucketMap.level < rowBucketCount ? DIMENSION_ROW : DIMENSION_COLUMN;
497         
498         if (dimension == DIMENSION_COLUMN && !allBuckets[allBuckets.length - 1].computeTotal())
499         {
500             return;
501         }
502         
503         if (!bucketMap.last)
504         {
505             for (Iterator JavaDoc it = bucketMap.entryIterator(); it.hasNext();)
506             {
507                 Map.Entry JavaDoc entry = (Map.Entry JavaDoc) it.next();
508
509                 computeTotals((BucketMap) entry.getValue());
510             }
511         }
512         
513         if (allBuckets[bucketMap.level].computeTotal())
514         {
515             if (dimension == DIMENSION_COLUMN)
516             {
517                 computeColumnTotal(bucketMap);
518             }
519             else
520             {
521                 computeRowTotals(bucketMap);
522             }
523         }
524     }
525
526
527     protected void sumVals(MeasureValue[] totals, MeasureValue[] vals) throws JRException
528     {
529         for (int i = 0; i < measures.length; i++)
530         {
531             totals[i].addValue(vals[i]);
532         }
533     }
534     
535     protected void computeColumnTotal(BucketMap bucketMap) throws JRException
536     {
537         MeasureValue[] totals = initMeasureValues();
538         
539         for (Iterator JavaDoc it = bucketMap.entryIterator(); it.hasNext();)
540         {
541             Map.Entry JavaDoc entry = (Map.Entry JavaDoc) it.next();
542             
543             for (int i = bucketMap.level + 1; i < allBuckets.length; ++i)
544             {
545                 entry = ((BucketMap) entry.getValue()).getTotalEntry();
546             }
547             
548             sumVals(totals, (MeasureValue[]) entry.getValue());
549         }
550                 
551         for (int i = bucketMap.level + 1; i < allBuckets.length; ++i)
552         {
553             bucketMap = bucketMap.addTotalNextMap();
554         }
555         
556         bucketMap.addTotalEntry(totals);
557     }
558
559
560     protected void computeRowTotals(BucketMap bucketMap) throws JRException
561     {
562         BucketListMap totals = createCollectBucketMap(rowBucketCount);
563         
564         for (Iterator JavaDoc it = bucketMap.entryIterator(); it.hasNext();)
565         {
566             Map.Entry JavaDoc entry = (Map.Entry JavaDoc) it.next();
567             
568             for (int i = bucketMap.level + 1; i < rowBucketCount; ++i)
569             {
570                 entry = ((BucketMap) entry.getValue()).getTotalEntry();
571             }
572             
573             totals.collectVals((BucketMap) entry.getValue(), true);
574         }
575         
576         BucketMap totalBucketMap = bucketMap;
577         for (int i = bucketMap.level + 1; i < rowBucketCount; ++i)
578         {
579             totalBucketMap = totalBucketMap.addTotalNextMap();
580         }
581         
582         totalBucketMap.addTotalEntry(totals);
583     }
584
585     
586     static protected class MapEntry implements Map.Entry JavaDoc, Comparable JavaDoc
587     {
588         final Bucket key;
589
590         final Object JavaDoc value;
591         
592         MapEntry(Bucket key, Object JavaDoc value)
593         {
594             this.key = key;
595             this.value = value;
596         }
597
598         public Object JavaDoc getKey()
599         {
600             return key;
601         }
602
603         public Object JavaDoc getValue()
604         {
605             return value;
606         }
607
608         public Object JavaDoc setValue(Object JavaDoc value)
609         {
610             throw new UnsupportedOperationException JavaDoc();
611         }
612
613         public int compareTo(Object JavaDoc o)
614         {
615             return key.compareTo(((MapEntry) o).key);
616         }
617         
618         public String JavaDoc toString()
619         {
620             return key + ":" + value;
621         }
622     }
623     
624     protected abstract class BucketMap
625     {
626         final int level;
627         final boolean last;
628         final Bucket totalKey;
629
630         BucketMap(int level)
631         {
632             this.level = level;
633             this.last = level == allBuckets.length - 1;
634             totalKey = allBuckets[level].VALUE_TOTAL;
635         }
636
637         BucketMap addTotalNextMap()
638         {
639             BucketMap nextMap = createBucketMap(level + 1);
640             addTotalEntry(nextMap);
641             return nextMap;
642         }
643         
644         abstract void set(Bucket key, Object JavaDoc value);
645
646         abstract void clear();
647
648         abstract Iterator JavaDoc entryIterator();
649
650         abstract Object JavaDoc get(Bucket key);
651
652         abstract MeasureValue[] insertMeasureValues(Bucket[] bucketValues);
653
654 /* abstract void fillKeys(Collection collectedKeys);*/
655
656         abstract void addTotalEntry(Object JavaDoc val);
657
658         abstract int size();
659         
660         abstract Map.Entry JavaDoc getTotalEntry();
661     }
662
663     protected class BucketTreeMap extends BucketMap
664     {
665         TreeMap JavaDoc map;
666
667         BucketTreeMap(int level)
668         {
669             super(level);
670
671             map = new TreeMap JavaDoc();
672         }
673         
674         void clear()
675         {
676             map.clear();
677         }
678
679         Iterator JavaDoc entryIterator()
680         {
681             return map.entrySet().iterator();
682         }
683
684         Object JavaDoc get(Bucket key)
685         {
686             return map.get(key);
687         }
688
689         MeasureValue[] insertMeasureValues(Bucket[] bucketValues)
690         {
691             BucketTreeMap levelMap = (BucketTreeMap) bucketValueMap;
692             for (int i = 0; i < bucketValues.length - 1; i++)
693             {
694                 BucketTreeMap nextMap = (BucketTreeMap) levelMap.get(bucketValues[i]);
695                 if (nextMap == null)
696                 {
697                     nextMap = new BucketTreeMap(i + 1);
698                     levelMap.map.put(bucketValues[i], nextMap);
699                 }
700
701                 levelMap = nextMap;
702             }
703
704             MeasureValue[] values = (MeasureValue[]) levelMap.get(bucketValues[bucketValues.length - 1]);
705             if (values == null)
706             {
707                 values = initMeasureValues();
708                 levelMap.map.put(bucketValues[bucketValues.length - 1], values);
709             }
710
711             return values;
712         }
713
714         int size()
715         {
716             return map.size();
717         }
718
719         void addTotalEntry(Object JavaDoc value)
720         {
721             map.put(totalKey, value);
722         }
723         
724         Map.Entry JavaDoc getTotalEntry()
725         {
726             Object JavaDoc value = get(totalKey);
727             return value == null ? null : new MapEntry(totalKey, value);
728         }
729         
730         
731         public String JavaDoc toString()
732         {
733             return map.toString();
734         }
735
736         void set(Bucket key, Object JavaDoc value)
737         {
738             map.put(key, value);
739         }
740     }
741
742     protected class BucketListMap extends BucketMap
743     {
744         List JavaDoc entries;
745
746         BucketListMap(int level, boolean linked)
747         {
748             super(level);
749
750             if (linked)
751             {
752                 entries = new LinkedList JavaDoc();
753             }
754             else
755             {
756                 entries = new ArrayList JavaDoc();
757             }
758         }
759
760         void clear()
761         {
762             entries.clear();
763         }
764         
765         Iterator JavaDoc entryIterator()
766         {
767             return entries.iterator();
768         }
769
770         private void add(Bucket key, Object JavaDoc value)
771         {
772             entries.add(new MapEntry(key, value));
773         }
774
775         Object JavaDoc get(Bucket key)
776         {
777             int idx = Collections.binarySearch(entries, new MapEntry(key, null));
778             return idx >= 0 ? ((MapEntry) entries.get(idx)).value : null;
779         }
780
781         MeasureValue[] insertMeasureValues(Bucket[] bucketValues)
782         {
783             int i = 0;
784             Object JavaDoc levelObj = this;
785             BucketListMap map = null;
786             while (i < allBuckets.length)
787             {
788                 map = (BucketListMap) levelObj;
789                 int size = map.entries.size();
790                 if (size == 0)
791                 {
792                     break;
793                 }
794
795                 MapEntry lastEntry = (MapEntry) map.entries.get(size - 1);
796                 if (!lastEntry.key.equals(bucketValues[i]))
797                 {
798                     break;
799                 }
800                 
801                 ++i;
802                 levelObj = lastEntry.value;
803             }
804
805             if (i == allBuckets.length)
806             {
807                 return (MeasureValue[]) levelObj;
808             }
809
810             while (i < allBuckets.length - 1)
811             {
812                 BucketListMap nextMap = new BucketListMap(i + 1, false);
813                 map.add(bucketValues[i], nextMap);
814                 map = nextMap;
815                 ++i;
816             }
817
818             MeasureValue[] values = initMeasureValues();
819             map.add(bucketValues[i], values);
820
821             return values;
822         }
823
824         int size()
825         {
826             return entries.size();
827         }
828
829         void addTotalEntry(Object JavaDoc value)
830         {
831             add(totalKey, value);
832         }
833
834         Map.Entry JavaDoc getTotalEntry()
835         {
836             MapEntry lastEntry = (MapEntry) entries.get(entries.size() - 1);
837             if (lastEntry.key.isTotal())
838             {
839                 return lastEntry;
840             }
841             
842             return null;
843         }
844
845         void set(Bucket key, Object JavaDoc value)
846         {
847             MapEntry mapEntry = new MapEntry(key, value);
848             int idx = Collections.binarySearch(entries, mapEntry);
849             int ins = -idx - 1;
850             entries.add(ins, mapEntry);
851         }
852
853         
854         void collectVals(BucketMap map, boolean sum) throws JRException
855         {
856             ListIterator JavaDoc totalIt = entries.listIterator();
857             MapEntry totalItEntry = totalIt.hasNext() ? (MapEntry) totalIt.next() : null;
858             
859             Iterator JavaDoc it = map.entryIterator();
860             Map.Entry JavaDoc entry = it.hasNext() ? (Map.Entry JavaDoc) it.next() : null;
861             while(entry != null)
862             {
863                 Bucket key = (Bucket) entry.getKey();
864                 
865                 int compare = totalItEntry == null ? -1 : key.compareTo(totalItEntry.key);
866                 if (compare <= 0)
867                 {
868                     Object JavaDoc addVal = null;
869                     
870                     if (last)
871                     {
872                         if (sum)
873                         {
874                             MeasureValue[] totalVals = compare == 0 ? (MeasureValue[]) totalItEntry.value : null;
875
876                             if (totalVals == null)
877                             {
878                                 totalVals = initMeasureValues();
879                                 addVal = totalVals;
880                             }
881
882                             sumVals(totalVals, (MeasureValue[]) entry.getValue());
883                         }
884                     }
885                     else
886                     {
887                         BucketListMap nextTotals = compare == 0 ? (BucketListMap) totalItEntry.value : null;
888                         
889                         if (nextTotals == null)
890                         {
891                             nextTotals = createCollectBucketMap(level + 1);
892                             addVal = nextTotals;
893                         }
894                         
895                         nextTotals.collectVals((BucketMap) entry.getValue(), sum);
896                     }
897                     
898                     if (compare < 0)
899                     {
900                         if (totalItEntry != null)
901                         {
902                             totalIt.previous();
903                         }
904                         totalIt.add(new MapEntry(key, addVal));
905                         if (totalItEntry != null)
906                         {
907                             totalIt.next();
908                         }
909                     }
910                     
911                     entry = it.hasNext() ? (Map.Entry JavaDoc) it.next() : null;
912                 }
913                 
914                 if (compare >= 0)
915                 {
916                     totalItEntry = totalIt.hasNext() ? (MapEntry) totalIt.next() : null;
917                 }
918             }
919         }
920     }
921
922     
923     
924     protected void createCrosstab() throws JRException
925     {
926         CollectedList[] collectedHeaders = new CollectedList[BucketingService.DIMENSIONS];
927         collectedHeaders[DIMENSION_ROW] = createHeadersList(DIMENSION_ROW, bucketValueMap, 0, false);
928         
929         BucketListMap collectedCols;
930         if (allBuckets[0].computeTotal())
931         {
932             BucketMap map = bucketValueMap;
933             for (int i = 0; i < rowBucketCount; ++i)
934             {
935                 map = (BucketMap) map.getTotalEntry().getValue();
936             }
937             collectedCols = (BucketListMap) map;
938         }
939         else
940         {
941             collectedCols = createCollectBucketMap(rowBucketCount);
942             collectCols(collectedCols, bucketValueMap);
943         }
944         collectedHeaders[DIMENSION_COLUMN] = createHeadersList(DIMENSION_COLUMN, collectedCols, 0, false);
945
946         colHeaders = createHeaders(BucketingService.DIMENSION_COLUMN, collectedHeaders);
947         rowHeaders = createHeaders(BucketingService.DIMENSION_ROW, collectedHeaders);
948         
949         cells = new CrosstabCell[collectedHeaders[BucketingService.DIMENSION_ROW].span][collectedHeaders[BucketingService.DIMENSION_COLUMN].span];
950         fillCells(collectedHeaders, bucketValueMap, 0, new int[]{0, 0}, new ArrayList JavaDoc(), new ArrayList JavaDoc());
951     }
952
953
954     protected void collectCols(BucketListMap collectedCols, BucketMap bucketMap) throws JRException
955     {
956         if (allBuckets[bucketMap.level].computeTotal())
957         {
958             BucketMap map = bucketMap;
959             for (int i = bucketMap.level; i < rowBucketCount; ++i)
960             {
961                 map = (BucketMap) map.getTotalEntry().getValue();
962             }
963             collectedCols.collectVals(map, false);
964             
965             return;
966         }
967         
968         for (Iterator JavaDoc it = bucketMap.entryIterator(); it.hasNext();)
969         {
970             Map.Entry JavaDoc entry = (Map.Entry JavaDoc) it.next();
971             BucketMap nextMap = (BucketMap) entry.getValue();
972             if (bucketMap.level == rowBucketCount - 1)
973             {
974                 collectedCols.collectVals(nextMap, false);
975             }
976             else
977             {
978                 collectCols(collectedCols, nextMap);
979             }
980         }
981     }
982     
983     
984     protected CollectedList createHeadersList(byte dimension, BucketMap bucketMap, int level, boolean total)
985     {
986         CollectedList headers = new CollectedList();
987
988         for (Iterator JavaDoc it = bucketMap.entryIterator(); it.hasNext();)
989         {
990             Map.Entry JavaDoc entry = (Map.Entry JavaDoc) it.next();
991             Bucket bucketValue = (Bucket) entry.getKey();
992
993             boolean totalBucket = bucketValue.isTotal();
994             byte totalPosition = allBuckets[bucketMap.level].getTotalPosition();
995             boolean createHeader = !totalBucket || total || totalPosition != BucketDefinition.TOTAL_POSITION_NONE;
996
997             if (createHeader)
998             {
999                 CollectedList nextHeaders;
1000                if (level + 1 < buckets[dimension].length)
1001                {
1002                    BucketMap nextMap = (BucketMap) entry.getValue();
1003                    nextHeaders = createHeadersList(dimension, nextMap, level + 1, total || totalBucket);
1004                }
1005                else
1006                {
1007                    nextHeaders = new CollectedList();
1008                    nextHeaders.span = 1;
1009                }
1010                nextHeaders.key = bucketValue;
1011
1012                if (totalBucket)
1013                {
1014                    if (totalPosition == BucketDefinition.TOTAL_POSITION_START)
1015                    {
1016                        headers.addFirst(nextHeaders);
1017                    }
1018                    else
1019                    {
1020                        headers.add(nextHeaders);
1021                    }
1022                }
1023                else
1024                {
1025                    headers.add(nextHeaders);
1026                }
1027            }
1028        }
1029
1030        if (headers.span == 0)
1031        {
1032            headers.span = 1;
1033        }
1034
1035        return headers;
1036    }
1037    
1038    protected HeaderCell[][] createHeaders(byte dimension, CollectedList[] headersLists)
1039    {
1040        HeaderCell[][] headers = new HeaderCell[buckets[dimension].length][headersLists[dimension].span];
1041        
1042        List JavaDoc vals = new ArrayList JavaDoc();
1043        fillHeaders(dimension, headers, 0, 0, headersLists[dimension], vals);
1044        
1045        return headers;
1046    }
1047
1048    
1049    protected void fillHeaders(byte dimension, HeaderCell[][] headers, int level, int col, CollectedList list, List JavaDoc vals)
1050    {
1051        if (level == buckets[dimension].length)
1052        {
1053            return;
1054        }
1055        
1056        for (Iterator JavaDoc it = list.iterator(); it.hasNext();)
1057        {
1058            CollectedList subList = (CollectedList) it.next();
1059            
1060            vals.add(subList.key);
1061            
1062            int depthSpan = subList.key.isTotal() ? buckets[dimension].length - level : 1;
1063            Bucket[] values = new Bucket[buckets[dimension].length];
1064            vals.toArray(values);
1065            
1066            headers[level][col] = new HeaderCell(values, subList.span, depthSpan);
1067            
1068            if (!subList.key.isTotal())
1069            {
1070                fillHeaders(dimension, headers, level + 1, col, subList, vals);
1071            }
1072            
1073            col += subList.span;
1074            vals.remove(vals.size() - 1);
1075        }
1076    }
1077
1078
1079    protected void fillCells(CollectedList[] collectedHeaders, BucketMap bucketMap, int level, int[] pos, List JavaDoc vals, List JavaDoc bucketMaps)
1080    {
1081        bucketMaps.add(bucketMap);
1082        
1083        byte dimension = level < rowBucketCount ? DIMENSION_ROW : DIMENSION_COLUMN;
1084        boolean last = level == allBuckets.length - 1;
1085
1086        CollectedList[] nextCollected = null;
1087        if (!last)
1088        {
1089            nextCollected = new CollectedList[DIMENSIONS];
1090            for (int d = 0; d < DIMENSIONS; ++d)
1091            {
1092                if (d != dimension)
1093                {
1094                    nextCollected[d] = collectedHeaders[d];
1095                }
1096            }
1097        }
1098        
1099        boolean incrementRow = level == buckets[BucketingService.DIMENSION_ROW].length - 1;
1100                
1101        CollectedList collectedList = collectedHeaders[dimension];
1102        
1103        Iterator JavaDoc bucketIt = bucketMap == null ? null : bucketMap.entryIterator();
1104        Map.Entry JavaDoc bucketItEntry = bucketIt != null && bucketIt.hasNext() ? (Map.Entry JavaDoc) bucketIt.next() : null;
1105        for (Iterator JavaDoc it = collectedList.iterator(); it.hasNext();)
1106        {
1107            CollectedList list = (CollectedList) it.next();
1108            
1109            Map.Entry JavaDoc bucketEntry = null;
1110            if (list.key.isTotal())
1111            {
1112                if (bucketMap != null)
1113                {
1114                    bucketEntry = bucketMap.getTotalEntry();
1115                }
1116            }
1117            else
1118            {
1119                if (bucketItEntry != null && bucketItEntry.getKey().equals(list.key))
1120                {
1121                    bucketEntry = bucketItEntry;
1122                    bucketItEntry = bucketIt.hasNext() ? (Map.Entry JavaDoc) bucketIt.next() : null;
1123                }
1124            }
1125            
1126            vals.add(list.key);
1127            if (last)
1128            {
1129                fillCell(pos, vals, bucketMaps, bucketEntry);
1130            }
1131            else
1132            {
1133                nextCollected[dimension] = list;
1134                BucketMap nextMap = bucketEntry == null ? null : (BucketMap) bucketEntry.getValue();
1135                
1136                fillCells(nextCollected, nextMap, level + 1, pos, vals, bucketMaps);
1137            }
1138            vals.remove(vals.size() - 1);
1139                
1140            if (incrementRow)
1141            {
1142                ++pos[0];
1143                pos[1] = 0;
1144            }
1145        }
1146        
1147        bucketMaps.remove(bucketMaps.size() - 1);
1148    }
1149
1150
1151    protected void fillCell(int[] pos, List JavaDoc vals, List JavaDoc bucketMaps, Map.Entry JavaDoc bucketEntry)
1152    {
1153        Iterator JavaDoc valsIt = vals.iterator();
1154        Bucket[] rowValues = new Bucket[buckets[BucketingService.DIMENSION_ROW].length];
1155        for (int i = 0; i < rowValues.length; i++)
1156        {
1157            rowValues[i] = (Bucket) valsIt.next();
1158        }
1159        
1160        Bucket[] columnValues = new Bucket[buckets[BucketingService.DIMENSION_COLUMN].length];
1161        for (int i = 0; i < columnValues.length; i++)
1162        {
1163            columnValues[i] = (Bucket) valsIt.next();
1164        }
1165        
1166        MeasureValue[] measureVals = bucketEntry == null ? zeroUserMeasureValues : getUserMeasureValues((MeasureValue[]) bucketEntry.getValue());
1167        MeasureValue[][][] totals = retrieveTotals(vals, bucketMaps);
1168        cells[pos[0]][pos[1]] = new CrosstabCell(rowValues, columnValues, measureVals, totals);
1169        ++pos[1];
1170    }
1171    
1172    
1173    protected MeasureValue[][][] retrieveTotals(List JavaDoc vals, List JavaDoc bucketMaps)
1174    {
1175        MeasureValue[][][] totals = new MeasureValue[rowBucketCount + 1][colBucketCount + 1][];
1176        
1177        for (int row = rowRetrTotalMax; row >= rowRetrTotalMin; --row)
1178        {
1179            if (!rowRetrTotals[row])
1180            {
1181                continue;
1182            }
1183            
1184            BucketMap rowMap = (BucketMap) bucketMaps.get(row);
1185            for (int i = row; rowMap != null && i < rowBucketCount; ++i)
1186            {
1187                Entry totalEntry = rowMap.getTotalEntry();
1188                rowMap = totalEntry == null ? null : (BucketMap) totalEntry.getValue();
1189            }
1190
1191            for (int col = 0; col <= rowRetrColMax[row]; ++col)
1192            {
1193                BucketMap colMap = rowMap;
1194                
1195                if (col < colBucketCount - 1)
1196                {
1197                    if (row == rowBucketCount)
1198                    {
1199                        rowMap = (BucketMap) bucketMaps.get(rowBucketCount + col + 1);
1200                    }
1201                    else if (rowMap != null)
1202                    {
1203                        rowMap = (BucketMap) rowMap.get((Bucket) vals.get(rowBucketCount + col));
1204                    }
1205                }
1206                
1207                if (!retrieveTotal[row][col])
1208                {
1209                    continue;
1210                }
1211                
1212                for (int i = col + 1; colMap != null && i < colBucketCount; ++i)
1213                {
1214                    colMap = (BucketMap) colMap.getTotalEntry().getValue();
1215                }
1216                
1217                if (colMap != null)
1218                {
1219                    if (col == colBucketCount)
1220                    {
1221                        MeasureValue[] measureValues = (MeasureValue[]) colMap.get((Bucket) vals.get(rowBucketCount + colBucketCount - 1));
1222                        totals[row][col] = getUserMeasureValues(measureValues);
1223                    }
1224                    else
1225                    {
1226                        Map.Entry JavaDoc totalEntry = colMap.getTotalEntry();
1227                        if (totalEntry != null)
1228                        {
1229                            MeasureValue[] totalValues = (MeasureValue[]) totalEntry.getValue();
1230                            totals[row][col] = getUserMeasureValues(totalValues);
1231                        }
1232                    }
1233                }
1234                
1235                if (totals[row][col] == null)
1236                {
1237                    totals[row][col] = zeroUserMeasureValues;
1238                }
1239            }
1240        }
1241
1242        return totals;
1243    }
1244    
1245    protected static class CollectedList extends LinkedList JavaDoc
1246    {
1247        int span;
1248        Bucket key;
1249        
1250        CollectedList()
1251        {
1252            super();
1253            
1254            span = 0;
1255        }
1256
1257        public boolean add(Object JavaDoc o)
1258        {
1259            boolean added = super.add(o);
1260            
1261            incrementSpan(o);
1262            
1263            return added;
1264        }
1265
1266        public void addFirst(Object JavaDoc o)
1267        {
1268            super.addFirst(o);
1269            
1270            incrementSpan(o);
1271        }
1272
1273        public void addLast(Object JavaDoc o)
1274        {
1275            super.add(o);
1276
1277            incrementSpan(o);
1278        }
1279
1280        private void incrementSpan(Object JavaDoc o)
1281        {
1282            if (o != null && o instanceof CollectedList)
1283            {
1284                span += ((CollectedList) o).span;
1285            }
1286            else
1287            {
1288                span += 1;
1289            }
1290        }
1291        
1292        public String JavaDoc toString()
1293        {
1294            return key + "/" + span + ": " + super.toString();
1295        }
1296    }
1297}
1298
Popular Tags