package jeql.syntax; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import jeql.api.error.ExecutionException; import jeql.api.error.JeqlException; import jeql.api.row.ArrayRowList; import jeql.api.row.BasicRow; import jeql.api.row.Row; import jeql.api.row.RowSchema; import jeql.api.table.Table; import jeql.engine.Scope; import jeql.util.TableUtil; import jeql.util.TypeUtil; public class TableValueNode extends ParseTreeNode { // a List<List<ParseTreeNode>> private List rows = new ArrayList(); private TableSchemaNode schemaNode = null; public TableValueNode() { } public void setSchema(TableSchemaNode schemaNode) { this.schemaNode = schemaNode; } public void add(List row) { rows.add(row); } public Class getType(Scope scope) { throw new UnsupportedOperationException(); } public void bind(Scope scope) { for (Iterator i = rows.iterator(); i.hasNext(); ) { List rowValues = (List) i.next(); for (Iterator iRow = rowValues.iterator(); iRow.hasNext(); ) { ParseTreeNode node = (ParseTreeNode) iRow.next(); node.bind(scope); } } } public Object eval(Scope scope) { RowSchema schema = extractSchema(scope); ArrayRowList rs = new ArrayRowList(schema); Row firstRow = null; for (Iterator i = rows.iterator(); i.hasNext(); ) { List rowValues = (List) i.next(); Row row = evalRow(rowValues, schema, scope); rs.add(row); if (firstRow == null) firstRow = row; else { // check consistency of subsequent rows if (row.size() != firstRow.size()) { // get row parse node (if possible) to determine src location ParseTreeNode rowFirstNode = null; if (rowValues.size() >= 1) rowFirstNode = (ParseTreeNode) rowValues.get(0); throw new ExecutionException(rowFirstNode, "Table rows must have same number of columns"); } } } Table tbl = new Table(rs); if (schemaNode != null) tbl.setName(schemaNode.getName()); return tbl; } private Row evalRow(List rowValues, RowSchema schema, Scope scope) { BasicRow row = new BasicRow(rowValues.size()); int i = 0; for (Iterator iRow = rowValues.iterator(); iRow.hasNext(); ) { ParseTreeNode node = (ParseTreeNode) iRow.next(); Object val = node.eval(scope); // check that value is of correct type for schema column Class reqClass = schema.getType(i); try { TypeUtil.checkCorrectType(val, reqClass); } catch (JeqlException ex) { ex.setLocation(node); throw ex; } row.setValue(i++, val); } return row; } private RowSchema extractSchema(Scope scope) { if (rows.size() == 0) return TableUtil.emptySchema(); return extractRowSchema((List) rows.get(0), scope); } private RowSchema extractRowSchema(List rowValues, Scope scope) { if (schemaNode != null && schemaNode.getColumnNames().size() != rowValues.size()) { throw new ExecutionException(this, "Schema and row data must have same number of columns"); } RowSchema schema = new RowSchema(rowValues.size()); int i = 0; for (Iterator iRow = rowValues.iterator(); iRow.hasNext(); ) { ParseTreeNode node = (ParseTreeNode) iRow.next(); Class type = node.getType(scope); schema.setColumnDef(i, getColumnName(i), type); i++; } return schema; } private String getColumnName(int i) { if (schemaNode != null) return (String) schemaNode.getColumnNames().get(i); return RowSchema.getDefaultColumnName(i); } }