/**
   * Returns the grand total measure values.
   *
   * @return the grand total measure values
   */
  public MeasureValue[] getGrandTotals() {
    BucketMap map = bucketValueMap;

    for (int i = 0; map != null && i < allBuckets.length - 1; ++i) {
      map = (BucketMap) map.getTotalEntry().getValue();
    }

    return map == null ? null : (MeasureValue[]) map.getTotalEntry().getValue();
  }
  protected MeasureValue[][][] retrieveTotals(List vals, List bucketMaps) {
    MeasureValue[][][] totals = new MeasureValue[rowBucketCount + 1][colBucketCount + 1][];

    for (int row = rowRetrTotalMax; row >= rowRetrTotalMin; --row) {
      if (!rowRetrTotals[row]) {
        continue;
      }

      BucketMap rowMap = (BucketMap) bucketMaps.get(row);
      for (int i = row; rowMap != null && i < rowBucketCount; ++i) {
        Entry totalEntry = rowMap.getTotalEntry();
        rowMap = totalEntry == null ? null : (BucketMap) totalEntry.getValue();
      }

      for (int col = 0; col <= rowRetrColMax[row]; ++col) {
        BucketMap colMap = rowMap;

        if (col < colBucketCount - 1) {
          if (row == rowBucketCount) {
            rowMap = (BucketMap) bucketMaps.get(rowBucketCount + col + 1);
          } else if (rowMap != null) {
            rowMap = (BucketMap) rowMap.get((Bucket) vals.get(rowBucketCount + col));
          }
        }

        if (!retrieveTotal[row][col]) {
          continue;
        }

        for (int i = col + 1; colMap != null && i < colBucketCount; ++i) {
          colMap = (BucketMap) colMap.getTotalEntry().getValue();
        }

        if (colMap != null) {
          if (col == colBucketCount) {
            MeasureValue[] measureValues =
                (MeasureValue[]) colMap.get((Bucket) vals.get(rowBucketCount + colBucketCount - 1));
            totals[row][col] = getUserMeasureValues(measureValues);
          } else {
            Map.Entry totalEntry = colMap.getTotalEntry();
            if (totalEntry != null) {
              MeasureValue[] totalValues = (MeasureValue[]) totalEntry.getValue();
              totals[row][col] = getUserMeasureValues(totalValues);
            }
          }
        }

        if (totals[row][col] == null) {
          totals[row][col] = zeroUserMeasureValues;
        }
      }
    }

    return totals;
  }
  protected void createCrosstab() throws JRException {
    CollectedList[] collectedHeaders = new CollectedList[BucketingService.DIMENSIONS];
    collectedHeaders[DIMENSION_ROW] = createHeadersList(DIMENSION_ROW, bucketValueMap, 0, false);

    BucketListMap collectedCols;
    if (allBuckets[0].computeTotal()) {
      BucketMap map = bucketValueMap;
      for (int i = 0; i < rowBucketCount; ++i) {
        map = (BucketMap) map.getTotalEntry().getValue();
      }
      collectedCols = (BucketListMap) map;
    } else {
      collectedCols = createCollectBucketMap(rowBucketCount);
      collectCols(collectedCols, bucketValueMap);
    }
    collectedHeaders[DIMENSION_COLUMN] =
        createHeadersList(DIMENSION_COLUMN, collectedCols, 0, false);

    int rowBuckets = collectedHeaders[BucketingService.DIMENSION_ROW].span;
    int colBuckets = collectedHeaders[BucketingService.DIMENSION_COLUMN].span;

    int bucketMeasureCount = rowBuckets * colBuckets * origMeasureCount;
    checkBucketMeasureCount(bucketMeasureCount);

    colHeaders = createHeaders(BucketingService.DIMENSION_COLUMN, collectedHeaders);
    rowHeaders = createHeaders(BucketingService.DIMENSION_ROW, collectedHeaders);

    cells = new CrosstabCell[rowBuckets][colBuckets];
    fillCells(
        collectedHeaders, bucketValueMap, 0, new int[] {0, 0}, new ArrayList(), new ArrayList());
  }
  protected void collectCols(BucketListMap collectedCols, BucketMap bucketMap) throws JRException {
    if (allBuckets[bucketMap.level].computeTotal()) {
      BucketMap map = bucketMap;
      for (int i = bucketMap.level; i < rowBucketCount; ++i) {
        map = (BucketMap) map.getTotalEntry().getValue();
      }
      collectedCols.collectVals(map, false);

      return;
    }

    for (Iterator it = bucketMap.entryIterator(); it.hasNext(); ) {
      Map.Entry entry = (Map.Entry) it.next();
      BucketMap nextMap = (BucketMap) entry.getValue();
      if (bucketMap.level == rowBucketCount - 1) {
        collectedCols.collectVals(nextMap, false);
      } else {
        collectCols(collectedCols, nextMap);
      }
    }
  }
  protected void fillCells(
      CollectedList[] collectedHeaders,
      BucketMap bucketMap,
      int level,
      int[] pos,
      List vals,
      List bucketMaps) {
    bucketMaps.add(bucketMap);

    byte dimension = level < rowBucketCount ? DIMENSION_ROW : DIMENSION_COLUMN;
    boolean last = level == allBuckets.length - 1;

    CollectedList[] nextCollected = null;
    if (!last) {
      nextCollected = new CollectedList[DIMENSIONS];
      for (int d = 0; d < DIMENSIONS; ++d) {
        if (d != dimension) {
          nextCollected[d] = collectedHeaders[d];
        }
      }
    }

    boolean incrementRow = level == buckets[BucketingService.DIMENSION_ROW].length - 1;

    CollectedList collectedList = collectedHeaders[dimension];

    Iterator bucketIt = bucketMap == null ? null : bucketMap.entryIterator();
    Map.Entry bucketItEntry =
        bucketIt != null && bucketIt.hasNext() ? (Map.Entry) 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() ? (Map.Entry) 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[1] = 0;
      }
    }

    bucketMaps.remove(bucketMaps.size() - 1);
  }