package edu.washington.escience.myria.expression.evaluate; import java.lang.reflect.InvocationTargetException; import org.codehaus.commons.compiler.CompileException; import org.codehaus.janino.ExpressionEvaluator; import com.google.common.base.Preconditions; import edu.washington.escience.myria.DbException; import edu.washington.escience.myria.MyriaConstants; import edu.washington.escience.myria.Schema; import edu.washington.escience.myria.Type; import edu.washington.escience.myria.column.ConstantValueColumn; import edu.washington.escience.myria.column.builder.WritableColumn; import edu.washington.escience.myria.expression.Expression; import edu.washington.escience.myria.expression.StateExpression; import edu.washington.escience.myria.expression.VariableExpression; import edu.washington.escience.myria.operator.StatefulApply; import edu.washington.escience.myria.storage.ReadableTable; import edu.washington.escience.myria.storage.TupleBatch; import edu.washington.escience.myria.storage.TupleUtils; /** * An Expression evaluator for generic expressions that produces a constant such as the initial state in * {@link StatefulApply}. */ public final class ConstantEvaluator extends GenericEvaluator { /** An empty object array passed to Janino as no arguments. */ private static final Object[] NO_ARGS = new Object[] {}; /** The value of this expression. */ private final Object value; /** The type of the value of this expression. */ private final Type type; /** * Default constructor. * * @param expression the expression for the evaluator * @param parameters parameters that are passed to the expression * @throws DbException if there is an error compiling the expression */ public ConstantEvaluator( final Expression expression, final ExpressionOperatorParameter parameters) throws DbException { super(expression, parameters); Preconditions.checkArgument( !expression.hasOperator(VariableExpression.class) && !expression.hasOperator(StateExpression.class), "Expression %s does not evaluate to a constant", expression); type = expression.getOutputType(parameters); String java; try { java = expression.getJavaExpression(parameters); } catch (Exception e) { throw new DbException("Error when generating Java expression " + this, e); } evaluator = new ExpressionEvaluator(); evaluator.setParameters(new String[] {}, new Class<?>[] {}); evaluator.setDefaultImports(MyriaConstants.DEFAULT_JANINO_IMPORTS); try { evaluator.setExpressionType(type.toJavaType()); evaluator.cook(java); value = evaluator.evaluate(NO_ARGS); } catch (CompileException e) { throw new DbException("Error when compiling expression " + java, e); } catch (InvocationTargetException e) { throw new DbException("Error when evaluating expression " + java, e); } } /** * Expression evaluator. */ private final ExpressionEvaluator evaluator; /** * Evaluates the {@link #getJavaExpressionWithAppend()} using the {@link #evaluator}. * * @return the result from the evaluation */ public Object eval() { return value; } @Override public void eval( final ReadableTable input, final int inputRow, final ReadableTable state, final int stateRow, final WritableColumn result, final WritableColumn count) { result.appendObject(value); count.appendInt(1); } @Override public EvaluatorResult evalTupleBatch(final TupleBatch tb, final Schema outputSchema) throws DbException { if (TupleUtils.getBatchSize(outputSchema) == tb.getBatchSize()) { return new EvaluatorResult( new ConstantValueColumn((Comparable<?>) value, type, tb.numTuples()), new ConstantValueColumn(1, Type.INT_TYPE, tb.numTuples())); } return super.evalTupleBatch(tb, outputSchema); } }