package net.sf.jasperreports.crosstabs.fill.calculation; import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.ListIterator; import java.util.Map; import java.util.TreeMap; import net.sf.jasperreports.engine.JRException; import net.sf.jasperreports.engine.JRRuntimeException; import net.sf.jasperreports.engine.util.JRProperties; public class BucketingService { public static final String PROPERTY_BUCKET_MEASURE_LIMIT = "net.sf.jasperreports.crosstab.bucket.measure.limit"; protected static final byte DIMENSION_ROW = 0; protected static final byte DIMENSION_COLUMN = 1; protected static final int DIMENSIONS = 2; protected final BucketDefinition[] allBuckets; protected final BucketDefinition[][] buckets; protected final int rowBucketCount; protected final int colBucketCount; protected final boolean[][] retrieveTotal; private boolean[] rowRetrTotals; private int rowRetrTotalMin; private int rowRetrTotalMax; private int[] rowRetrColMax; protected final MeasureDefinition[] measures; protected final int origMeasureCount; protected final int[] measureIndexes; protected final boolean sorted; protected final BucketMap bucketValueMap; protected long dataCount; protected boolean processed; protected HeaderCell[][] colHeaders; protected HeaderCell[][] rowHeaders; protected CrosstabCell[][] cells; private final MeasureDefinition.MeasureValue[] zeroUserMeasureValues; private final int bucketMeasureLimit; private int runningBucketMeasureCount = 0; public BucketingService(List rowBuckets, List columnBuckets, List measures, boolean sorted, boolean[][] retrieveTotal) { this.sorted = sorted; this.buckets = new BucketDefinition[2][]; this.rowBucketCount = rowBuckets.size(); this.buckets[0] = new BucketDefinition[this.rowBucketCount]; rowBuckets.toArray((Object[])this.buckets[0]); this.colBucketCount = columnBuckets.size(); this.buckets[1] = new BucketDefinition[this.colBucketCount]; columnBuckets.toArray((Object[])this.buckets[1]); this.allBuckets = new BucketDefinition[this.rowBucketCount + this.colBucketCount]; System.arraycopy(this.buckets[0], 0, this.allBuckets, 0, this.rowBucketCount); System.arraycopy(this.buckets[1], 0, this.allBuckets, this.rowBucketCount, this.colBucketCount); this.origMeasureCount = measures.size(); List measuresList = new ArrayList(measures.size() * 2); List measureIndexList = new ArrayList(measures.size() * 2); int i; for (i = 0; i < measures.size(); i++) { MeasureDefinition measure = measures.get(i); addMeasure(measure, i, measuresList, measureIndexList); } this.measures = new MeasureDefinition[measuresList.size()]; measuresList.toArray((Object[])this.measures); this.measureIndexes = new int[measureIndexList.size()]; for (i = 0; i < this.measureIndexes.length; i++) this.measureIndexes[i] = ((Integer)measureIndexList.get(i)).intValue(); this.retrieveTotal = retrieveTotal; checkTotals(); this.bucketValueMap = createBucketMap(0); this.zeroUserMeasureValues = initUserMeasureValues(); this.bucketMeasureLimit = JRProperties.getIntegerProperty("net.sf.jasperreports.crosstab.bucket.measure.limit", 0); } protected void checkTotals() { this.rowRetrTotalMin = this.rowBucketCount + 1; this.rowRetrTotalMax = -1; this.rowRetrTotals = new boolean[this.rowBucketCount + 1]; this.rowRetrColMax = new int[this.rowBucketCount + 1]; for (int row = 0; row <= this.rowBucketCount; row++) { this.rowRetrColMax[row] = -1; boolean total = false; for (int i = 0; i <= this.colBucketCount; i++) { if (this.retrieveTotal[row][i]) { total = true; this.rowRetrColMax[row] = i; } } this.rowRetrTotals[row] = total; if (total) { if (row < this.rowRetrTotalMin) this.rowRetrTotalMin = row; this.rowRetrTotalMax = row; if (row < this.rowBucketCount) this.allBuckets[row].setComputeTotal(); } } for (int col = 0; col < this.colBucketCount; col++) { BucketDefinition colBucket = this.allBuckets[this.rowBucketCount + col]; if (!colBucket.computeTotal()) { boolean total = false; for (int i = 0; !total && i <= this.rowBucketCount; i++) total = this.retrieveTotal[i][col]; if (total) colBucket.setComputeTotal(); } } for (int d = 0; d < 2; d++) { boolean dTotal = false; for (int i = 0; i < (this.buckets[d]).length; i++) { if (dTotal) { this.buckets[d][i].setComputeTotal(); } else { dTotal = this.buckets[d][i].computeTotal(); } } } } public void clear() { this.bucketValueMap.clear(); this.processed = false; this.dataCount = 0L; this.runningBucketMeasureCount = 0; } protected BucketMap createBucketMap(int level) { BucketMap map; if (this.sorted) { map = new BucketListMap(level, false); } else { map = new BucketTreeMap(level); } return map; } protected BucketListMap createCollectBucketMap(int level) { return new BucketListMap(level, true); } protected void addMeasure(MeasureDefinition measure, int index, List measuresList, List measureIndexList) { MeasureDefinition sumMeasure, varianceMeasure, countMeasure, measureDefinition1; switch (measure.getCalculation()) { case 3: case 7: sumMeasure = MeasureDefinition.createHelperMeasure(measure, (byte)2); addMeasure(sumMeasure, index, measuresList, measureIndexList); measureDefinition1 = MeasureDefinition.createHelperMeasure(measure, (byte)1); addMeasure(measureDefinition1, index, measuresList, measureIndexList); break; case 6: varianceMeasure = MeasureDefinition.createHelperMeasure(measure, (byte)7); addMeasure(varianceMeasure, index, measuresList, measureIndexList); break; case 10: countMeasure = MeasureDefinition.createDistinctCountHelperMeasure(measure); addMeasure(countMeasure, index, measuresList, measureIndexList); break; } measuresList.add(measure); measureIndexList.add(new Integer(index)); } public void addData(Object[] bucketValues, Object[] measureValues) throws JRException { if (this.processed) throw new JRException("Crosstab data has already been processed."); this.dataCount++; BucketDefinition.Bucket[] bucketVals = getBucketValues(bucketValues); MeasureDefinition.MeasureValue[] values = this.bucketValueMap.insertMeasureValues(bucketVals); for (int i = 0; i < this.measures.length; i++) values[i].addValue(measureValues[this.measureIndexes[i]]); } protected void bucketMeasuresCreated() { this.runningBucketMeasureCount += this.origMeasureCount; checkBucketMeasureCount(this.runningBucketMeasureCount); } protected BucketDefinition.Bucket[] getBucketValues(Object[] bucketValues) { BucketDefinition.Bucket[] bucketVals = new BucketDefinition.Bucket[this.allBuckets.length]; for (int i = 0; i < this.allBuckets.length; i++) { BucketDefinition bucket = this.allBuckets[i]; Object value = bucketValues[i]; bucketVals[i] = bucket.create(value); } return bucketVals; } protected MeasureDefinition.MeasureValue[] initMeasureValues() { MeasureDefinition.MeasureValue[] values = new MeasureDefinition.MeasureValue[this.measures.length]; for (int i = 0; i < this.measures.length; i++) { MeasureDefinition measure = this.measures[i]; measure.getClass(); values[i] = new MeasureDefinition.MeasureValue(measure); switch (measure.getCalculation()) { case 3: case 7: values[i].setHelper(values[i - 2], (byte)1); values[i].setHelper(values[i - 1], (byte)0); break; case 6: values[i].setHelper(values[i - 1], (byte)2); case 10: values[i].setHelper(values[i - 1], (byte)0); break; } } return values; } protected MeasureDefinition.MeasureValue[] initUserMeasureValues() { MeasureDefinition.MeasureValue[] vals = new MeasureDefinition.MeasureValue[this.origMeasureCount]; for (int c = 0, i = 0; i < this.measures.length; i++) { if (!this.measures[i].isSystemDefined()) { this.measures[i].getClass(); vals[c] = new MeasureDefinition.MeasureValue(this.measures[i]); c++; } } return vals; } public void processData() throws JRException { if (!this.processed) { if (this.dataCount > 0L) { if (this.allBuckets[this.rowBucketCount - 1].computeTotal() || this.allBuckets[this.allBuckets.length - 1].computeTotal()) computeTotals(this.bucketValueMap); createCrosstab(); } this.processed = true; } } public boolean hasData() { return (this.dataCount > 0L); } public HeaderCell[][] getColumnHeaders() { return this.colHeaders; } public HeaderCell[][] getRowHeaders() { return this.rowHeaders; } public CrosstabCell[][] getCrosstabCells() { return this.cells; } public MeasureDefinition.MeasureValue[] getMeasureValues(BucketDefinition.Bucket[] bucketValues) { BucketMap map = this.bucketValueMap; for (int i = 0; map != null && i < this.allBuckets.length - 1; i++) map = (BucketMap)map.get(bucketValues[i]); return (map == null) ? null : (MeasureDefinition.MeasureValue[])map.get(bucketValues[this.allBuckets.length - 1]); } protected MeasureDefinition.MeasureValue[] getUserMeasureValues(MeasureDefinition.MeasureValue[] values) { MeasureDefinition.MeasureValue[] vals = new MeasureDefinition.MeasureValue[this.origMeasureCount]; for (int c = 0, i = 0; i < this.measures.length; i++) { if (!this.measures[i].isSystemDefined()) { vals[c] = values[i]; c++; } } return vals; } public MeasureDefinition.MeasureValue[] getGrandTotals() { BucketMap map = this.bucketValueMap; for (int i = 0; map != null && i < this.allBuckets.length - 1; i++) map = (BucketMap)map.getTotalEntry().getValue(); return (map == null) ? null : (MeasureDefinition.MeasureValue[])map.getTotalEntry().getValue(); } protected void computeTotals(BucketMap bucketMap) throws JRException { byte dimension = (bucketMap.level < this.rowBucketCount) ? 0 : 1; if (dimension == 1 && !this.allBuckets[this.allBuckets.length - 1].computeTotal()) return; if (!bucketMap.last) for (Iterator it = bucketMap.entryIterator(); it.hasNext(); ) { Map.Entry entry = it.next(); computeTotals((BucketMap)entry.getValue()); } if (this.allBuckets[bucketMap.level].computeTotal()) if (dimension == 1) { computeColumnTotal(bucketMap); } else { computeRowTotals(bucketMap); } } protected void sumVals(MeasureDefinition.MeasureValue[] totals, MeasureDefinition.MeasureValue[] vals) throws JRException { for (int i = 0; i < this.measures.length; i++) totals[i].addValue(vals[i]); } protected void computeColumnTotal(BucketMap bucketMap) throws JRException { MeasureDefinition.MeasureValue[] totals = initMeasureValues(); for (Iterator it = bucketMap.entryIterator(); it.hasNext(); ) { Map.Entry entry = it.next(); for (int j = bucketMap.level + 1; j < this.allBuckets.length; j++) entry = ((BucketMap)entry.getValue()).getTotalEntry(); sumVals(totals, (MeasureDefinition.MeasureValue[])entry.getValue()); } for (int i = bucketMap.level + 1; i < this.allBuckets.length; i++) bucketMap = bucketMap.addTotalNextMap(); bucketMap.addTotalEntry(totals); } protected void computeRowTotals(BucketMap bucketMap) throws JRException { BucketListMap totals = createCollectBucketMap(this.rowBucketCount); for (Iterator it = bucketMap.entryIterator(); it.hasNext(); ) { Map.Entry entry = it.next(); for (int j = bucketMap.level + 1; j < this.rowBucketCount; j++) entry = ((BucketMap)entry.getValue()).getTotalEntry(); totals.collectVals((BucketMap)entry.getValue(), true); } BucketMap totalBucketMap = bucketMap; for (int i = bucketMap.level + 1; i < this.rowBucketCount; i++) totalBucketMap = totalBucketMap.addTotalNextMap(); totalBucketMap.addTotalEntry(totals); } protected static class MapEntry implements Map.Entry, Comparable { final BucketDefinition.Bucket key; final Object value; MapEntry(BucketDefinition.Bucket key, Object value) { this.key = key; this.value = value; } public Object getKey() { return this.key; } public Object getValue() { return this.value; } public Object setValue(Object value) { throw new UnsupportedOperationException(); } public int compareTo(Object o) { return this.key.compareTo(((MapEntry)o).key); } public String toString() { return this.key + ":" + this.value; } } protected abstract class BucketMap { final int level; final boolean last; final BucketDefinition.Bucket totalKey; private final BucketingService this$0; BucketMap(int level) { this.level = level; this.last = (level == BucketingService.this.allBuckets.length - 1); this.totalKey = (BucketingService.this.allBuckets[level]).VALUE_TOTAL; } BucketMap addTotalNextMap() { BucketMap nextMap = BucketingService.this.createBucketMap(this.level + 1); addTotalEntry(nextMap); return nextMap; } abstract void set(BucketDefinition.Bucket param1Bucket, Object param1Object); abstract void clear(); abstract Iterator entryIterator(); abstract Object get(BucketDefinition.Bucket param1Bucket); abstract MeasureDefinition.MeasureValue[] insertMeasureValues(BucketDefinition.Bucket[] param1ArrayOfBucket); abstract void addTotalEntry(Object param1Object); abstract int size(); abstract Map.Entry getTotalEntry(); } protected class BucketTreeMap extends BucketMap { TreeMap map; private final BucketingService this$0; BucketTreeMap(int level) { super(level); this.map = new TreeMap(); } void clear() { this.map.clear(); } Iterator entryIterator() { return this.map.entrySet().iterator(); } Object get(BucketDefinition.Bucket key) { return this.map.get(key); } MeasureDefinition.MeasureValue[] insertMeasureValues(BucketDefinition.Bucket[] bucketValues) { BucketTreeMap levelMap = (BucketTreeMap)BucketingService.this.bucketValueMap; for (int i = 0; i < bucketValues.length - 1; i++) { BucketTreeMap nextMap = (BucketTreeMap)levelMap.get(bucketValues[i]); if (nextMap == null) { nextMap = new BucketTreeMap(i + 1); levelMap.map.put(bucketValues[i], nextMap); } levelMap = nextMap; } MeasureDefinition.MeasureValue[] values = (MeasureDefinition.MeasureValue[])levelMap.get(bucketValues[bucketValues.length - 1]); if (values == null) { values = BucketingService.this.initMeasureValues(); levelMap.map.put(bucketValues[bucketValues.length - 1], values); BucketingService.this.bucketMeasuresCreated(); } return values; } int size() { return this.map.size(); } void addTotalEntry(Object value) { this.map.put(this.totalKey, value); } Map.Entry getTotalEntry() { Object value = get(this.totalKey); return (value == null) ? null : new BucketingService.MapEntry(this.totalKey, value); } public String toString() { return this.map.toString(); } void set(BucketDefinition.Bucket key, Object value) { this.map.put(key, value); } } protected class BucketListMap extends BucketMap { List entries; private final BucketingService this$0; BucketListMap(int level, boolean linked) { super(level); if (linked) { this.entries = new LinkedList(); } else { this.entries = new ArrayList(); } } void clear() { this.entries.clear(); } Iterator entryIterator() { return this.entries.iterator(); } private void add(BucketDefinition.Bucket key, Object value) { this.entries.add(new BucketingService.MapEntry(key, value)); } Object get(BucketDefinition.Bucket key) { int idx = Collections.binarySearch(this.entries, new BucketingService.MapEntry(key, null)); return (idx >= 0) ? ((BucketingService.MapEntry)this.entries.get(idx)).value : null; } MeasureDefinition.MeasureValue[] insertMeasureValues(BucketDefinition.Bucket[] bucketValues) { int i = 0; Object levelObj = this; BucketListMap map = null; while (i < BucketingService.this.allBuckets.length) { map = (BucketListMap)levelObj; int size = map.entries.size(); if (size == 0) break; BucketingService.MapEntry lastEntry = map.entries.get(size - 1); if (!lastEntry.key.equals(bucketValues[i])) break; i++; levelObj = lastEntry.value; } if (i == BucketingService.this.allBuckets.length) return (MeasureDefinition.MeasureValue[])levelObj; while (i < BucketingService.this.allBuckets.length - 1) { BucketListMap nextMap = new BucketListMap(i + 1, false); map.add(bucketValues[i], nextMap); map = nextMap; i++; } MeasureDefinition.MeasureValue[] values = BucketingService.this.initMeasureValues(); map.add(bucketValues[i], values); BucketingService.this.bucketMeasuresCreated(); return values; } int size() { return this.entries.size(); } void addTotalEntry(Object value) { add(this.totalKey, value); } Map.Entry getTotalEntry() { BucketingService.MapEntry lastEntry = this.entries.get(this.entries.size() - 1); if (lastEntry.key.isTotal()) return lastEntry; return null; } void set(BucketDefinition.Bucket key, Object value) { BucketingService.MapEntry mapEntry = new BucketingService.MapEntry(key, value); int idx = Collections.binarySearch(this.entries, mapEntry); int ins = -idx - 1; this.entries.add(ins, mapEntry); } void collectVals(BucketingService.BucketMap map, boolean sum) throws JRException { ListIterator totalIt = this.entries.listIterator(); BucketingService.MapEntry totalItEntry = totalIt.hasNext() ? totalIt.next() : null; Iterator it = map.entryIterator(); Map.Entry entry = it.hasNext() ? it.next() : null; while (entry != null) { BucketDefinition.Bucket key = (BucketDefinition.Bucket)entry.getKey(); int compare = (totalItEntry == null) ? -1 : key.compareTo(totalItEntry.key); if (compare <= 0) { Object addVal = null; if (this.last) { if (sum) { MeasureDefinition.MeasureValue[] totalVals = (compare == 0) ? (MeasureDefinition.MeasureValue[])totalItEntry.value : null; if (totalVals == null) { totalVals = BucketingService.this.initMeasureValues(); addVal = totalVals; } BucketingService.this.sumVals(totalVals, (MeasureDefinition.MeasureValue[])entry.getValue()); } } else { BucketListMap nextTotals = (compare == 0) ? (BucketListMap)totalItEntry.value : null; if (nextTotals == null) { nextTotals = BucketingService.this.createCollectBucketMap(this.level + 1); addVal = nextTotals; } nextTotals.collectVals((BucketingService.BucketMap)entry.getValue(), sum); } if (compare < 0) { if (totalItEntry != null) totalIt.previous(); totalIt.add(new BucketingService.MapEntry(key, addVal)); if (totalItEntry != null) totalIt.next(); } entry = it.hasNext() ? it.next() : null; } if (compare >= 0) totalItEntry = totalIt.hasNext() ? totalIt.next() : null; } } } protected void createCrosstab() throws JRException { BucketListMap collectedCols; CollectedList[] collectedHeaders = new CollectedList[2]; collectedHeaders[0] = createHeadersList((byte)0, this.bucketValueMap, 0, false); if (this.allBuckets[0].computeTotal()) { BucketMap map = this.bucketValueMap; for (int i = 0; i < this.rowBucketCount; i++) map = (BucketMap)map.getTotalEntry().getValue(); collectedCols = (BucketListMap)map; } else { collectedCols = createCollectBucketMap(this.rowBucketCount); collectCols(collectedCols, this.bucketValueMap); } collectedHeaders[1] = createHeadersList((byte)1, collectedCols, 0, false); int rowBuckets = (collectedHeaders[0]).span; int colBuckets = (collectedHeaders[1]).span; int bucketMeasureCount = rowBuckets * colBuckets * this.origMeasureCount; checkBucketMeasureCount(bucketMeasureCount); this.colHeaders = createHeaders((byte)1, collectedHeaders); this.rowHeaders = createHeaders((byte)0, collectedHeaders); this.cells = new CrosstabCell[rowBuckets][colBuckets]; fillCells(collectedHeaders, this.bucketValueMap, 0, new int[] { 0, 0 }, new ArrayList(), new ArrayList()); } protected void checkBucketMeasureCount(int bucketMeasureCount) { if (this.bucketMeasureLimit > 0 && bucketMeasureCount > this.bucketMeasureLimit) throw new JRRuntimeException("Crosstab bucket/measure limit (" + this.bucketMeasureLimit + ") exceeded."); } protected void collectCols(BucketListMap collectedCols, BucketMap bucketMap) throws JRException { if (this.allBuckets[bucketMap.level].computeTotal()) { BucketMap map = bucketMap; for (int i = bucketMap.level; i < this.rowBucketCount; i++) map = (BucketMap)map.getTotalEntry().getValue(); collectedCols.collectVals(map, false); return; } for (Iterator it = bucketMap.entryIterator(); it.hasNext(); ) { Map.Entry entry = it.next(); BucketMap nextMap = (BucketMap)entry.getValue(); if (bucketMap.level == this.rowBucketCount - 1) { collectedCols.collectVals(nextMap, false); continue; } collectCols(collectedCols, nextMap); } } protected CollectedList createHeadersList(byte dimension, BucketMap bucketMap, int level, boolean total) { CollectedList headers = new CollectedList(); for (Iterator it = bucketMap.entryIterator(); it.hasNext(); ) { Map.Entry entry = it.next(); BucketDefinition.Bucket bucketValue = (BucketDefinition.Bucket)entry.getKey(); boolean totalBucket = bucketValue.isTotal(); byte totalPosition = this.allBuckets[bucketMap.level].getTotalPosition(); boolean createHeader = (!totalBucket || total || totalPosition != 0); if (createHeader) { CollectedList nextHeaders; if (level + 1 < (this.buckets[dimension]).length) { BucketMap nextMap = (BucketMap)entry.getValue(); nextHeaders = createHeadersList(dimension, nextMap, level + 1, (total || totalBucket)); } else { nextHeaders = new CollectedList(); nextHeaders.span = 1; } nextHeaders.key = bucketValue; if (totalBucket) { if (totalPosition == 1) { headers.addFirst(nextHeaders); continue; } headers.add(nextHeaders); continue; } headers.add(nextHeaders); } } if (headers.span == 0) headers.span = 1; return headers; } protected HeaderCell[][] createHeaders(byte dimension, CollectedList[] headersLists) { HeaderCell[][] headers = new HeaderCell[(this.buckets[dimension]).length][(headersLists[dimension]).span]; List vals = new ArrayList(); fillHeaders(dimension, headers, 0, 0, headersLists[dimension], vals); return headers; } protected void fillHeaders(byte dimension, HeaderCell[][] headers, int level, int col, CollectedList list, List vals) { if (level == (this.buckets[dimension]).length) return; for (Iterator it = list.iterator(); it.hasNext(); ) { CollectedList subList = (CollectedList)it.next(); vals.add(subList.key); int depthSpan = subList.key.isTotal() ? ((this.buckets[dimension]).length - level) : 1; BucketDefinition.Bucket[] values = new BucketDefinition.Bucket[(this.buckets[dimension]).length]; vals.toArray(values); headers[level][col] = new HeaderCell(values, subList.span, depthSpan); if (!subList.key.isTotal()) fillHeaders(dimension, headers, level + 1, col, subList, vals); col += subList.span; vals.remove(vals.size() - 1); } } protected void fillCells(CollectedList[] collectedHeaders, BucketMap bucketMap, int level, int[] pos, List vals, List bucketMaps) { bucketMaps.add(bucketMap); byte dimension = (level < this.rowBucketCount) ? 0 : 1; boolean last = (level == this.allBuckets.length - 1); CollectedList[] nextCollected = null; if (!last) { nextCollected = new CollectedList[2]; for (int d = 0; d < 2; d++) { if (d != dimension) nextCollected[d] = collectedHeaders[d]; } } boolean incrementRow = (level == (this.buckets[0]).length - 1); CollectedList collectedList = collectedHeaders[dimension]; Iterator bucketIt = (bucketMap == null) ? null : bucketMap.entryIterator(); Map.Entry bucketItEntry = (bucketIt != null && bucketIt.hasNext()) ? bucketIt.next() : null; for (Iterator it = collectedList.iterator(); it.hasNext(); ) { CollectedList list = (CollectedList)it.next(); Map.Entry bucketEntry = null; if (list.key.isTotal()) { if (bucketMap != null) bucketEntry = bucketMap.getTotalEntry(); } else if (bucketItEntry != null && bucketItEntry.getKey().equals(list.key)) { bucketEntry = bucketItEntry; bucketItEntry = bucketIt.hasNext() ? bucketIt.next() : null; } vals.add(list.key); if (last) { fillCell(pos, vals, bucketMaps, bucketEntry); } else { nextCollected[dimension] = list; BucketMap nextMap = (bucketEntry == null) ? null : (BucketMap)bucketEntry.getValue(); fillCells(nextCollected, nextMap, level + 1, pos, vals, bucketMaps); } vals.remove(vals.size() - 1); if (incrementRow) { pos[0] = pos[0] + 1; pos[1] = 0; } } bucketMaps.remove(bucketMaps.size() - 1); } protected void fillCell(int[] pos, List vals, List bucketMaps, Map.Entry bucketEntry) { Iterator valsIt = vals.iterator(); BucketDefinition.Bucket[] rowValues = new BucketDefinition.Bucket[(this.buckets[0]).length]; for (int i = 0; i < rowValues.length; i++) rowValues[i] = valsIt.next(); BucketDefinition.Bucket[] columnValues = new BucketDefinition.Bucket[(this.buckets[1]).length]; for (int j = 0; j < columnValues.length; j++) columnValues[j] = valsIt.next(); MeasureDefinition.MeasureValue[] measureVals = (bucketEntry == null) ? this.zeroUserMeasureValues : getUserMeasureValues((MeasureDefinition.MeasureValue[])bucketEntry.getValue()); MeasureDefinition.MeasureValue[][][] totals = retrieveTotals(vals, bucketMaps); this.cells[pos[0]][pos[1]] = new CrosstabCell(rowValues, columnValues, measureVals, totals); pos[1] = pos[1] + 1; } protected MeasureDefinition.MeasureValue[][][] retrieveTotals(List vals, List bucketMaps) { MeasureDefinition.MeasureValue[][][] totals = new MeasureDefinition.MeasureValue[this.rowBucketCount + 1][this.colBucketCount + 1][]; for (int row = this.rowRetrTotalMax; row >= this.rowRetrTotalMin; row--) { if (this.rowRetrTotals[row]) { BucketMap rowMap = bucketMaps.get(row); for (int i = row; rowMap != null && i < this.rowBucketCount; i++) { Map.Entry totalEntry = rowMap.getTotalEntry(); rowMap = (totalEntry == null) ? null : (BucketMap)totalEntry.getValue(); } for (int col = 0; col <= this.rowRetrColMax[row]; col++) { BucketMap colMap = rowMap; if (col < this.colBucketCount - 1) if (row == this.rowBucketCount) { rowMap = bucketMaps.get(this.rowBucketCount + col + 1); } else if (rowMap != null) { rowMap = (BucketMap)rowMap.get(vals.get(this.rowBucketCount + col)); } if (this.retrieveTotal[row][col]) { for (int j = col + 1; colMap != null && j < this.colBucketCount; j++) colMap = (BucketMap)colMap.getTotalEntry().getValue(); if (colMap != null) if (col == this.colBucketCount) { MeasureDefinition.MeasureValue[] measureValues = (MeasureDefinition.MeasureValue[])colMap.get(vals.get(this.rowBucketCount + this.colBucketCount - 1)); totals[row][col] = getUserMeasureValues(measureValues); } else { Map.Entry totalEntry = colMap.getTotalEntry(); if (totalEntry != null) { MeasureDefinition.MeasureValue[] totalValues = (MeasureDefinition.MeasureValue[])totalEntry.getValue(); totals[row][col] = getUserMeasureValues(totalValues); } } if (totals[row][col] == null) totals[row][col] = this.zeroUserMeasureValues; } } } } return totals; } protected static class CollectedList extends LinkedList { private static final long serialVersionUID = 10200L; int span = 0; BucketDefinition.Bucket key; public boolean add(Object o) { boolean added = super.add(o); incrementSpan(o); return added; } public void addFirst(Object o) { super.addFirst(o); incrementSpan(o); } public void addLast(Object o) { super.add(o); incrementSpan(o); } private void incrementSpan(Object o) { if (o != null && o instanceof CollectedList) { this.span += ((CollectedList)o).span; } else { this.span++; } } public String toString() { return this.key + "/" + this.span + ": " + super.toString(); } } }