@SuppressWarnings("rawtypes")
  ObserverAggregationCache buildAggrCache(
      final RegionScanner innerScanner,
      CoprocessorRowType type,
      CoprocessorProjector projector,
      ObserverAggregators aggregators,
      CoprocessorFilter filter,
      Stats stats)
      throws IOException {

    ObserverAggregationCache aggCache = new ObserverAggregationCache(aggregators);

    ObserverTuple tuple = new ObserverTuple(type);
    boolean hasMore = true;
    List<Cell> results = new ArrayList<Cell>();
    while (hasMore) {
      results.clear();
      hasMore = innerScanner.nextRaw(results);
      if (results.isEmpty()) continue;

      if (stats != null) stats.countInputRow(results);

      Cell cell = results.get(0);
      tuple.setUnderlying(cell.getRowArray(), cell.getRowOffset(), cell.getRowLength());

      if (behavior.ordinal() >= ObserverBehavior.SCAN_FILTER.ordinal()) {
        if (filter != null && filter.evaluate(tuple) == false) continue;

        if (behavior.ordinal() >= ObserverBehavior.SCAN_FILTER_AGGR.ordinal()) {
          AggrKey aggKey = projector.getAggrKey(results);
          MeasureAggregator[] bufs = aggCache.getBuffer(aggKey);
          aggregators.aggregate(bufs, results);

          aggCache.checkMemoryUsage();
        }
      }
    }
    return aggCache;
  }
  public AggregationScanner(
      CoprocessorRowType type,
      CoprocessorFilter filter,
      CoprocessorProjector groupBy,
      ObserverAggregators aggrs,
      RegionScanner innerScanner,
      ObserverBehavior behavior)
      throws IOException {

    AggregateRegionObserver.LOG.info("Kylin Coprocessor start");

    this.behavior = behavior;

    ObserverAggregationCache aggCache;
    Stats stats = new Stats();

    aggCache = buildAggrCache(innerScanner, type, groupBy, aggrs, filter, stats);
    stats.countOutputRow(aggCache.getSize());
    this.outerScanner = aggCache.getScanner(innerScanner);

    AggregateRegionObserver.LOG.info("Kylin Coprocessor aggregation done: " + stats);
  }