844 lines
30 KiB
Java
844 lines
30 KiB
Java
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();
|
|
}
|
|
}
|
|
}
|