gt:                     Object o = rawRows.getObject(columnIndex + 1);
context: /*
  // $Id$
  // This software is subject to the terms of the Eclipse Public License v1.0
  // Agreement, available at the following URL:
  // http://www.eclipse.org/legal/epl-v10.html.
  // Copyright (C) 2002-2010 Julian Hyde and others
  // All Rights Reserved.
  // You must accept the terms of that agreement to use this software.
  */
  package mondrian.rolap.agg;
  
  import mondrian.rolap.*;
  import mondrian.olap.*;
  
  import java.sql.*;
  import java.util.*;
  
  /**
   * <p>The <code>SegmentLoader</code> queries database and loads the data into
   * the given set of segments.</p>
   *
   * <p>It reads a segment of <code>measure</code>, where <code>columns</code> are
   * constrained to <code>values</code>.  Each entry in <code>values</code>
   * can be null, meaning don't constrain, or can have several values. For
   * example, <code>getSegment({Unit_sales}, {Region, State, Year}, {"West"},
   * {"CA", "OR", "WA"}, null})</code> returns sales in states CA, OR and WA
   * in the Western region, for all years.</p>
   *
   * @author Thiyagu
   * @version $Id$
   * @since 24 May 2007
   */
  public class SegmentLoader {
  
      private static final Comparator<Object> BOOLEAN_COMPARATOR;
  
      static {
          if (Util.PreJdk15) {
              // Work around the fact that Boolean is not Comparable until JDK
              // 1.5.
              assert !(Comparable.class.isAssignableFrom(Boolean.class));
              BOOLEAN_COMPARATOR =
                  new Comparator<Object>() {
                      public int compare(Object o1, Object o2) {
                          if (o1 instanceof Boolean) {
                              boolean b1 = (Boolean) o1;
                              if (o2 instanceof Boolean) {
                                  boolean b2 = (Boolean) o2;
                                  return b1 == b2
                                      ? 0
                                      : (b1 ? 1 : -1);
                              } else {
                                  return -1;
                              }
                          } else {
                              return ((Comparable) o1).compareTo(o2);
                          }
                      }
                  };
          } else {
              assert Comparable.class.isAssignableFrom(Boolean.class);
              BOOLEAN_COMPARATOR = null;
          }
      }
  
      /**
       * Creates a SegmentLoader.
       */
      public SegmentLoader() {
      }
  
      /**
       * Loads data for all the segments of the GroupingSets. If the grouping sets
       * list contains more than one Grouping Set then data is loaded using the
       * GROUP BY GROUPING SETS sql. Else if only one grouping set is passed in
       * the list data is loaded without using GROUP BY GROUPING SETS sql. If the
       * database does not support grouping sets
       * {@link mondrian.spi.Dialect#supportsGroupingSets()} then
       * grouping sets list should always have only one element in it.
       *
       * <p>For example, if list has 2 grouping sets with columns A, B, C and B, C
       * respectively, then the SQL will be
       * "GROUP BY GROUPING SETS ((A, B, C), (B, C))".
       *
       * <p>Else if the list has only one grouping set then sql would be without
       * grouping sets.
       *
       * <p>The <code>groupingSets</code> list should be topological order, with
       * more detailed higher-level grouping sets occuring first. In other words,
       * the first element of the list should always be the detailed grouping
       * set (default grouping set), followed by grouping sets which can be
       * rolled-up on this detailed grouping set.
       * In the example (A, B, C) is the detailed grouping set and (B, C) is
       * rolled-up using the detailed.
       *
       * @param groupingSets   List of grouping sets whose segments are loaded
       * @param pinnedSegments Pinned segments
       */
      public void load(
          List<GroupingSet> groupingSets,
          RolapAggregationManager.PinSet pinnedSegments,
          List<StarPredicate> compoundPredicateList)
      {
          GroupingSetsList groupingSetsList =
              new GroupingSetsList(groupingSets);
          RolapStar.Column[] defaultColumns =
              groupingSetsList.getDefaultColumns();
          SqlStatement stmt = null;
          try {
              stmt = createExecuteSql(
                  groupingSetsList,
                  compoundPredicateList);
              int arity = defaultColumns.length;
              SortedSet<Comparable<?>>[] axisValueSets =
                  getDistinctValueWorkspace(arity);
  
              boolean[] axisContainsNull = new boolean[arity];
  
              RowList rows =
                  processData(
                      stmt,
                      axisContainsNull,
                      axisValueSets,
                      groupingSetsList);
  
              boolean sparse =
                  setAxisDataAndDecideSparseUse(
                      axisValueSets,
                      axisContainsNull,
                      groupingSetsList,
                      rows);
  
              final Map<BitKey, GroupingSetsList.Cohort> groupingDataSetsMap =
                  createDataSetsForGroupingSets(
                      groupingSetsList,
                      sparse,
                      rows.getTypes().subList(
                          arity, rows.getTypes().size()));
  
              loadDataToDataSets(
                  groupingSetsList, rows, groupingDataSetsMap);
  
              setDataToSegments(
                  groupingSetsList, groupingDataSetsMap, pinnedSegments);
          } catch (SQLException e) {
              throw stmt.handle(e);
          } finally {
              if (stmt != null) {
                  stmt.close();
              }
              // Any segments which are still loading have failed.
              setFailOnStillLoadingSegments(groupingSetsList);
          }
      }
  
      void setFailOnStillLoadingSegments(GroupingSetsList groupingSetsList) {
          for (GroupingSet groupingset : groupingSetsList.getGroupingSets()) {
              for (Segment segment : groupingset.getSegments()) {
                  segment.setFailIfStillLoading();
              }
          }
      }
  
      /**
       * Loads data to the datasets. If the grouping sets is used,
       * dataset is fetched from groupingDataSetMap using grouping bit keys of
       * the row data. If grouping sets is not used, data is loaded on to
       * nonGroupingDataSets.
       */
      private void loadDataToDataSets(
          GroupingSetsList groupingSetsList,
          RowList rows,
          Map<BitKey, GroupingSetsList.Cohort> groupingDataSetMap)
      {
          int arity = groupingSetsList.getDefaultColumns().length;
          Aggregation.Axis[] axes = groupingSetsList.getDefaultAxes();
          int segmentLength = groupingSetsList.getDefaultSegments().size();
  
          final List<SqlStatement.Type> types = rows.getTypes();
          final boolean useGroupingSet = groupingSetsList.useGroupingSets();
          for (rows.first(); rows.next();) {
              final BitKey groupingBitKey;
              final GroupingSetsList.Cohort cohort;
              if (useGroupingSet) {
                  groupingBitKey =
                      (BitKey) rows.getObject(
                          groupingSetsList.getGroupingBitKeyIndex());
                  cohort = groupingDataSetMap.get(groupingBitKey);
              } else {
                  groupingBitKey = null;
                  cohort = groupingDataSetMap.get(BitKey.EMPTY);
              }
              final int[] pos = cohort.pos;
              for (int j = 0, k = 0; j < arity; j++) {
                  final SqlStatement.Type type = types.get(j);
                  switch (type) {
                  // TODO: different treatment for INT, DOUBLE
                  case OBJECT:
                  case INT:
                  case DOUBLE:
                      Object o = rows.getObject(j);
                      if (useGroupingSet
                          && (o == null || o == RolapUtil.sqlNullValue)
                          && groupingBitKey.get(
                              groupingSetsList.findGroupingFunctionIndex(j)))
                      {
                          continue;
                      }
                      Aggregation.Axis axis = axes[j];
                      if (o == null) {
                          o = RolapUtil.sqlNullValue;
                      }
                      int offset = axis.getOffset(o);
                      pos[k++] = offset;
                      break;
                  default:
                      throw Util.unexpected(type);
                  }
              }
  
              for (int j = 0; j < segmentLength; j++) {
                  cohort.segmentDatasetList.get(j).populateFrom(
                      pos, rows, arity + j);
              }
          }
      }
  
      private boolean setAxisDataAndDecideSparseUse(
          SortedSet<Comparable<?>>[] axisValueSets,
          boolean[] axisContainsNull,
          GroupingSetsList groupingSetsList,
          RowList rows)
      {
          Aggregation.Axis[] axes = groupingSetsList.getDefaultAxes();
          RolapStar.Column[] allColumns = groupingSetsList.getDefaultColumns();
          // Figure out size of dense array, and allocate it, or use a sparse
          // array if appropriate.
          boolean sparse = false;
          int n = 1;
          for (int i = 0; i < axes.length; i++) {
              Aggregation.Axis axis = axes[i];
              SortedSet<Comparable<?>> valueSet = axisValueSets[i];
              int size = axis.loadKeys(valueSet, axisContainsNull[i]);
              setAxisDataToGroupableList(
                  groupingSetsList,
                  valueSet,
                  axisContainsNull[i],
                  allColumns[i]);
              int previous = n;
              n *= size;
              if ((n < previous) || (n < size)) {
                  // Overflow has occurred.
                  n = Integer.MAX_VALUE;
                  sparse = true;
              }
          }
          return useSparse(sparse, n, rows);
      }
  
      boolean useSparse(boolean sparse, int n, RowList rows) {
          sparse = sparse || useSparse((double) n, (double) rows.size());
          return sparse;
      }
  
      private void setDataToSegments(
          GroupingSetsList groupingSetsList,
          Map<BitKey, GroupingSetsList.Cohort> datasetsMap,
          RolapAggregationManager.PinSet pinnedSegments)
      {
          List<GroupingSet> groupingSets = groupingSetsList.getGroupingSets();
          for (int i = 0; i < groupingSets.size(); i++) {
              List<Segment> groupedSegments = groupingSets.get(i).getSegments();
              GroupingSetsList.Cohort cohort =
                  datasetsMap.get(
                      groupingSetsList.getRollupColumnsBitKeyList().get(i));
              for (int j = 0; j < groupedSegments.size(); j++) {
                  Segment groupedSegment = groupedSegments.get(j);
                  final SegmentDataset segmentDataset =
                      cohort.segmentDatasetList.get(j);
                  groupedSegment.setData(segmentDataset, pinnedSegments);
              }
          }
      }
  
      private Map<BitKey, GroupingSetsList.Cohort> createDataSetsForGroupingSets(
          GroupingSetsList groupingSetsList,
          boolean sparse,
          List<SqlStatement.Type> types)
      {
          if (!groupingSetsList.useGroupingSets()) {
              final GroupingSetsList.Cohort datasets = createDataSets(
                  sparse,
                  groupingSetsList.getDefaultSegments(),
                  groupingSetsList.getDefaultAxes(),
                  types);
              return Collections.singletonMap(BitKey.EMPTY, datasets);
          }
          Map<BitKey, GroupingSetsList.Cohort> datasetsMap =
              new HashMap<BitKey, GroupingSetsList.Cohort>();
          List<GroupingSet> groupingSets = groupingSetsList.getGroupingSets();
          List<BitKey> groupingColumnsBitKeyList =
              groupingSetsList.getRollupColumnsBitKeyList();
          for (int i = 0; i < groupingSets.size(); i++) {
              GroupingSet groupingSet = groupingSets.get(i);
              GroupingSetsList.Cohort cohort =
                  createDataSets(
                      sparse,
                      groupingSet.getSegments(),
                      groupingSet.getAxes(),
                      types);
              datasetsMap.put(groupingColumnsBitKeyList.get(i), cohort);
          }
          return datasetsMap;
      }
  
      private int calculateMaxDataSize(Aggregation.Axis[] axes) {
          int n = 1;
          for (Aggregation.Axis axis : axes) {
              n *= axis.getKeys().length;
          }
          return n;
      }
  
      private GroupingSetsList.Cohort createDataSets(
          boolean sparse,
          List<Segment> segments,
          Aggregation.Axis[] axes,
          List<SqlStatement.Type> types)
      {
          final List<SegmentDataset> datasets =
              new ArrayList<SegmentDataset>(segments.size());
          final int n;
          if (sparse) {
              n = 0;
          } else {
              n = calculateMaxDataSize(axes);
          }
          for (int i = 0; i < segments.size(); i++) {
              final Segment segment = segments.get(i);
              datasets.add(segment.createDataset(sparse, types.get(i), n));
          }
          return new GroupingSetsList.Cohort(datasets, axes.length);
      }
  
      private void setAxisDataToGroupableList(
          GroupingSetsList groupingSetsList,
          SortedSet<Comparable<?>> valueSet,
          boolean axisContainsNull,
          RolapStar.Column column)
      {
          for (GroupingSet groupingSet
              : groupingSetsList.getRollupGroupingSets())
          {
              RolapStar.Column[] columns = groupingSet.getColumns();
              for (int i = 0; i < columns.length; i++) {
                  if (columns[i].equals(column)) {
                      groupingSet.getAxes()[i].loadKeys(
                          valueSet, axisContainsNull);
                  }
              }
          }
      }
  
      /**
       * Creates and executes a SQL statement to retrieve the set of cells
       * specified by a GroupingSetsList.
       *
       * <p>This method may be overridden in tests.
       *
       * @param groupingSetsList Grouping
       * @return An executed SQL statement, or null
       */
      SqlStatement createExecuteSql(
          GroupingSetsList groupingSetsList,
          List<StarPredicate> compoundPredicateList)
      {
          RolapStar star = groupingSetsList.getStar();
          String sql =
              AggregationManager.instance().generateSql(
                  groupingSetsList, compoundPredicateList);
          return RolapUtil.executeQuery(
              star.getDataSource(), sql, "Segment.load",
              "Error while loading segment");
      }
  
      RowList processData(
          SqlStatement stmt,
          final boolean[] axisContainsNull,
          final SortedSet<Comparable<?>>[] axisValueSets,
          final GroupingSetsList groupingSetsList) throws SQLException
      {
          List<Segment> segments = groupingSetsList.getDefaultSegments();
          int measureCount = segments.size();
          ResultSet rawRows = loadData(stmt, groupingSetsList);
          final List<SqlStatement.Type> types =
              stmt == null
                  ? Collections.nCopies(
                  rawRows.getMetaData().getColumnCount(),
                  SqlStatement.Type.OBJECT)
                  : stmt.guessTypes();
          int arity = axisValueSets.length;
          final int groupingColumnStartIndex = arity + measureCount;
  
          // If we're using grouping sets, the SQL query will have a number of
          // indicator columns, and we roll these into a single BitSet column in
          // the processed data set.
          final List<SqlStatement.Type> processedTypes;
          if (groupingSetsList.useGroupingSets()) {
              processedTypes =
                  new ArrayList<SqlStatement.Type>(
                      types.subList(0, groupingColumnStartIndex));
              processedTypes.add(SqlStatement.Type.OBJECT);
          } else {
              processedTypes = types;
          }
          final RowList processedRows = new RowList(processedTypes, 100);
  
          while (rawRows.next()) {
              processedRows.createRow();
              // get the columns
              int columnIndex = 0;
              for (int axisIndex = 0; axisIndex < arity;
                   axisIndex++, columnIndex++)
              {
                  final SqlStatement.Type type = types.get(columnIndex);
                  switch (type) {
                  case OBJECT:
  