package jeql.engine.query.group; import java.util.Iterator; import java.util.List; import jeql.api.error.ExecutionException; import jeql.api.function.AggregateFunction; import jeql.api.function.Aggregator; import jeql.api.row.Row; import jeql.api.table.Table; import jeql.engine.EngineContext; import jeql.engine.Scope; import jeql.engine.query.QueryScope; import jeql.syntax.TableColumnNode; import com.vividsolutions.jts.util.Assert; public class GroupScope implements QueryScope { private QueryScope baseScope; private List groupByList; private List aggFun; private Row currentRow; private int groupBySize = 0; private int rowSize = 0; private Class[] aggRowColType; public GroupScope(QueryScope baseScope, List groupByList, List aggFun) { this.baseScope = baseScope; this.groupByList = groupByList; this.aggFun = aggFun; groupBySize = groupByList.size(); rowSize = groupBySize + aggFun.size(); aggRowColType = new Class[rowSize]; computeAggRowTypes(); } public Scope getParent() { return baseScope.getParent(); } public EngineContext getContext() { return baseScope.getContext(); } public Table resolveTable(String name) { Assert.shouldNeverReachHere("Tables not defined in group scope"); return null; } public boolean hasVariable(String name) { return baseScope.hasVariable(name); } public Object getVariable(String name) { return baseScope.getVariable(name); } public void setVariable(String name, Object value) { baseScope.setVariable(name, value); } public void setVariableType(String name, Class varType) { baseScope.setVariableType(name, varType); } public Class getVariableType(String name) { return baseScope.getVariableType(name); } public void setRow(Row row) { currentRow = row; } /** * Gets the current row at the execution point of this select context. * * @return the current row */ public Row getRow() { return currentRow; } public int getColumnIndex(String tblName, String colName) { // look up index in this scope for table.column for (int i = 0; i < groupByList.size(); i++) { TableColumnNode col = (TableColumnNode) groupByList.get(i); if (col.isMatch(tblName, colName)) return i; } return -1; } public Object getColumnValue(int colIndex) { return currentRow.getValue(colIndex); } public Class getColumnType(int colIndex) { return (Class) aggRowColType[colIndex]; } public Class[] getAggRowTypes() { return aggRowColType; } private void computeAggRowTypes() { int colCount = 0; for (Iterator i = groupByList.iterator(); i.hasNext(); ) { TableColumnNode col = (TableColumnNode) i.next(); int baseColIndex = baseScope.getColumnIndex(col.getTableName(), col.getColName()); if (baseColIndex < 0) throw new ExecutionException(col, "Unknown GROUP BY column: " + col.getColName()); Class colType = baseScope.getColumnType(baseColIndex); aggRowColType[colCount++] = colType; } for (int j = 0; j < aggFun.size(); j++) { aggRowColType[colCount++] = ((AggregateFunction) aggFun.get(j)).getType(); } } public Aggregator[] createAggregatorVector() { Aggregator[] agg = new Aggregator[aggFun.size()]; for (int i = 0; i < agg.length; i++) { agg[i] = ((AggregateFunction) aggFun.get(i)).createAggregator(); } return agg; } public int getAggregatorFunctionCount() { return aggFun.size(); } /** * Gets the indices of the GROUP BY columns * in the base scope. * * @return an array of column indices */ public int[] getGroupKeyIndices() { int[] attrIndex = new int[groupBySize]; int j = 0; for (Iterator i = groupByList.iterator(); i.hasNext(); ) { TableColumnNode col = (TableColumnNode) i.next(); int baseColIndex = baseScope.getColumnIndex(col.getTableName(), col.getColName()); attrIndex[j++] = baseColIndex; } return attrIndex; } public boolean hasValue(Object key) { return baseScope.hasValue(key); } public void setValue(Object key, Object value) { baseScope.setValue(key, value); } public Object getValue(Object key) { return baseScope.getValue(key); } // TODO: is this semantics correct? Or does grouping need its own rownum? public void setRowNum(int rowNum) { baseScope.setRowNum(rowNum); } public int getRowNum() { return baseScope.getRowNum(); } }