MeasureValue[] insertMeasureValues(Bucket[] bucketValues, boolean createValues, int offset) {
      BucketMapMap levelMap = this;
      for (int i = offset; i < bucketValues.length - 1; i++) {
        BucketMapMap nextMap = (BucketMapMap) levelMap.get(bucketValues[i]);
        if (nextMap == null) {
          nextMap = createBucketMapMap(i + 1);
          levelMap.map.put(bucketValues[i], nextMap);
        }

        levelMap = nextMap;
      }

      MeasureValue[] values = (MeasureValue[]) levelMap.get(bucketValues[bucketValues.length - 1]);
      if (values == null) {
        if (createValues) {
          values = initMeasureValues();
          bucketMeasuresCreated();
        } else {
          values = zeroMeasureValues;
        }

        levelMap.map.put(bucketValues[bucketValues.length - 1], values);
      }

      return values;
    }
    void copyEntries(BucketMap bucketMap) {
      for (Iterator<Entry<Bucket, Object>> bucketIterator = bucketMap.entryIterator();
          bucketIterator.hasNext(); ) {
        Entry<Bucket, Object> bucketEntry = bucketIterator.next();
        Bucket bucketKey = bucketEntry.getKey();

        Object copyBucketValue;
        if (bucketMap.last) {
          copyBucketValue = initMeasureValues();
        } else {
          BucketMap bucketSubMap = (BucketMap) bucketEntry.getValue();
          BucketMapMap copyBucketSubMap = new BucketMapMap(level + 1, false);
          copyBucketSubMap.copyEntries(bucketSubMap);
          copyBucketValue = copyBucketSubMap;
        }

        map.put(bucketKey, copyBucketValue);
      }
    }
  protected void computeRowTotals(BucketMap bucketMap) throws JRException {
    BucketMapMap totals = createRowTotalsBucketMap();

    for (Iterator<Map.Entry<Bucket, Object>> it = bucketMap.entryIterator(); it.hasNext(); ) {
      Map.Entry<Bucket, Object> entry = it.next();

      for (int i = bucketMap.level + 1; i < rowBucketCount; ++i) {
        entry = ((BucketMap) entry.getValue()).getTotalEntry();
      }

      totals.sumValues((BucketMap) entry.getValue());
    }

    BucketMap totalBucketMap = bucketMap;
    for (int i = bucketMap.level + 1; i < rowBucketCount; ++i) {
      totalBucketMap = totalBucketMap.addTotalNextMap();
    }

    totalBucketMap.addTotalEntry(totals);
  }
    void sumValues(BucketMap bucketMap) throws JRException {
      for (Iterator<Map.Entry<Bucket, Object>> it = bucketMap.entryIterator(); it.hasNext(); ) {
        Map.Entry<Bucket, Object> entry = it.next();

        // find the total entry that matches the map entry.
        // the total map is should contain all collected entries
        // FIXME optimize this for sorted maps where we can assume that the order is the same
        Object value = get(entry.getKey());
        if (last) {
          // last level, sum the values
          sumVals((MeasureValue[]) value, (MeasureValue[]) entry.getValue());
        } else {
          // go to the next level
          ((BucketMapMap) value).sumValues((BucketMap) entry.getValue());
        }
      }
    }
 protected BucketMapMap createRowTotalsBucketMap() {
   BucketMapMap totalsBucketMap = new BucketMapMap(rowBucketCount, false);
   totalsBucketMap.copyEntries(columnBucketMap);
   return totalsBucketMap;
 }