ColumnIndices columnIndices, TreeMap<List<Value>, ColumnLookup> columnLookups)
throws TypeMismatchException {
if (!queryHasAggregation(query) || (table.getNumberOfRows() == 0)) {
return table;
}
QueryGroup group = query.getGroup();
QueryPivot pivot = query.getPivot();
QuerySelection selection = query.getSelection();
List<String> groupByIds = Lists.newArrayList();
if (group != null) {
groupByIds = group.getColumnIds();
}
List<String> pivotByIds = Lists.newArrayList();
if (pivot != null) {
pivotByIds = pivot.getColumnIds(); // contained in groupByIds
}
List<String> groupAndPivotIds = Lists.newArrayList(groupByIds);
groupAndPivotIds.addAll(pivotByIds);
List<AggregationColumn> tmpColumnAggregations = selection.getAggregationColumns();
List<ScalarFunctionColumn> selectedScalarFunctionColumns = selection.getScalarFunctionColumns();
// Remove duplicates from tmpColumnAggregations, creating columnAggregations:
List<AggregationColumn> columnAggregations =
Lists.newArrayListWithExpectedSize(tmpColumnAggregations.size());
for (AggregationColumn aggCol : tmpColumnAggregations) {
if (!columnAggregations.contains(aggCol)) {
columnAggregations.add(aggCol);
}
}
List<String> aggregationIds = Lists.newArrayList();
for (AggregationColumn col : columnAggregations) {
aggregationIds.add(col.getAggregatedColumn().getId());
}
List<ScalarFunctionColumn> groupAndPivotScalarFunctionColumns = Lists.newArrayList();
if (group != null) {
groupAndPivotScalarFunctionColumns.addAll(group.getScalarFunctionColumns());
}
if (pivot != null) {
groupAndPivotScalarFunctionColumns.addAll(pivot.getScalarFunctionColumns());
}
List<ColumnDescription> newColumnDescriptions = Lists.newArrayList();
newColumnDescriptions.addAll(table.getColumnDescriptions());
// Add to the table description the scalar function columns included in the
// group and pivot. The groups of rows are defined according to the
// values of those columns, and so it is necessary to add them before the
// calculations of the groups, pivots and aggregations.
for (ScalarFunctionColumn column : groupAndPivotScalarFunctionColumns) {
newColumnDescriptions.add(new ColumnDescription(column.getId(),
column.getValueType(table),
ScalarFunctionColumnTitle.getColumnDescriptionLabel(table, column)));
}
DataTable tempTable = new DataTable();
tempTable.addColumns(newColumnDescriptions);
// Calculate the values of the added scalar function columns in each row.
DataTableColumnLookup lookup = new DataTableColumnLookup(table);
for (TableRow sourceRow : table.getRows()) {
TableRow newRow = new TableRow();
for (TableCell sourceCell : sourceRow.getCells()) {
newRow.addCell(sourceCell);
}
for (ScalarFunctionColumn column : groupAndPivotScalarFunctionColumns) {
newRow.addCell(new TableCell(column.getValue(lookup, sourceRow)));
}
try {
tempTable.addRow(newRow);
} catch (TypeMismatchException e) {
// Should not happen, given that the original table is OK.
}
}
table = tempTable;
// Calculate the aggregations.
TableAggregator aggregator = new TableAggregator(groupAndPivotIds,
Sets.newHashSet(aggregationIds), table);
Set<AggregationPath> paths = aggregator.getPathsToLeaves();
// These variables will hold the "titles" of the rows and columns.
// They are TreeSets because their order matters.
SortedSet<RowTitle> rowTitles =
Sets.newTreeSet(GroupingComparators.ROW_TITLE_COMPARATOR);
SortedSet<ColumnTitle> columnTitles = Sets.newTreeSet(
GroupingComparators.getColumnTitleDynamicComparator(columnAggregations));
// A tree set containing all pivot value lists (the set is for the
// uniqueness and the tree for the order).
TreeSet<List<Value>> pivotValuesSet =
Sets.newTreeSet(GroupingComparators.VALUE_LIST_COMPARATOR);
// This MetaTable holds all the data in the table, this data is then
// dumped into the real table.
MetaTable metaTable = new MetaTable();
for (AggregationColumn columnAggregation : columnAggregations) {
for (AggregationPath path : paths) {
// A ColumnTitle is composed of all the values for the pivot-by
// columns, and a ColumnAggregation. That is why it is necessary to iterate over all
// ColumnAggregations and create a ColumnTitle for each one.
List<Value> originalValues = path.getValues();
// Separate originalValues into the rowValues and columnValues. The
// rowValues are the values of the group-by columns and the columnValues
// are the values of the pivot-by columns.
List<Value> rowValues = originalValues.subList(0, groupByIds.size());
RowTitle rowTitle = new RowTitle(rowValues);
rowTitles.add(rowTitle);
List<Value> columnValues = originalValues.subList(groupByIds.size(), originalValues.size());
pivotValuesSet.add(columnValues);
ColumnTitle columnTitle = new ColumnTitle(columnValues,
columnAggregation, (columnAggregations.size() > 1));
columnTitles.add(columnTitle);
metaTable.put(rowTitle, columnTitle, new TableCell(aggregator.getAggregationValue(path,
columnAggregation.getAggregatedColumn().getId(),
columnAggregation.getAggregationType())));
}
}
// Create the scalar function column titles for the scalar function columns
// that contain aggregations.
List<ScalarFunctionColumnTitle> scalarFunctionColumnTitles =
Lists.newArrayList();
for (ScalarFunctionColumn scalarFunctionColumn :
selectedScalarFunctionColumns) {
if (scalarFunctionColumn.getAllAggregationColumns().size() != 0) {
for (List<Value> columnValues : pivotValuesSet) {
scalarFunctionColumnTitles.add(new ScalarFunctionColumnTitle(columnValues,
scalarFunctionColumn));
}
}
}
// Create the new table description.
DataTable result = createDataTable(groupByIds, columnTitles, table, scalarFunctionColumnTitles);
List<ColumnDescription> colDescs = result.getColumnDescriptions();
// Fill the columnIndices and columnLookups parameters for the group-by
// columns and the aggregation columns.
columnIndices.clear();
int columnIndex = 0;
if (group != null) {
List<Value> empytListOfValues = Lists.newArrayList();
columnLookups.put(empytListOfValues, new GenericColumnLookup());
for (AbstractColumn column : group.getColumns()) {
columnIndices.put(column, columnIndex);
if (!(column instanceof ScalarFunctionColumn)) {
((GenericColumnLookup) columnLookups.get(empytListOfValues)).put(column, columnIndex);
for (List<Value> columnValues : pivotValuesSet) {
if (!columnLookups.containsKey(columnValues)) {