package edu.washington.escience.myria.expression;
import java.io.Serializable;
import java.util.LinkedList;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.google.common.collect.Lists;
import edu.washington.escience.myria.Type;
import edu.washington.escience.myria.column.builder.ColumnBuilder;
import edu.washington.escience.myria.expression.evaluate.ExpressionOperatorParameter;
/**
* An expression that can be applied to a tuple.
*/
public class Expression implements Serializable {
/***/
private static final long serialVersionUID = 1L;
/**
* Name of the column that the result will be written to.
*/
@JsonProperty private final String outputName;
/**
* The java expression to be evaluated.
*/
@JsonProperty private String javaExpression;
/**
* Expression encoding reference is needed to get the output type.
*/
@JsonProperty private final ExpressionOperator rootExpressionOperator;
/** Variable name of input tuple batch. */
public static final String INPUT = "input";
/** Variable name of row index of input. */
public static final String INPUTROW = "inputRow";
/** Variable name of state. */
public static final String STATE = "state";
/** Variable name of row index of state. */
public static final String STATEROW = "stateRow";
/** Variable name of result. */
public static final String RESULT = "result";
/** Variable name of result count. */
public static final String COUNT = "count";
/** Variable name of column offset of state. */
public static final String STATECOLOFFSET = "stateColOffset";
/**
* This is not really unused, it's used automagically by Jackson deserialization.
*/
public Expression() {
outputName = null;
rootExpressionOperator = null;
}
/**
* Constructs the Expression object.
*
* @param rootExpressionOperator the root of the AST representing this expression.
*/
public Expression(final ExpressionOperator rootExpressionOperator) {
this.rootExpressionOperator = rootExpressionOperator;
outputName = null;
}
/**
* Constructs the Expression object.
*
* @param outputName the name of the resulting element
* @param rootExpressionOperator the root of the AST representing this expression.
*/
public Expression(final String outputName, final ExpressionOperator rootExpressionOperator) {
this.rootExpressionOperator = rootExpressionOperator;
this.outputName = outputName;
}
/**
* @return the rootExpressionOperator
*/
public ExpressionOperator getRootExpressionOperator() {
return rootExpressionOperator;
}
/**
* @return the output name
*/
public String getOutputName() {
return outputName;
}
/**
* @param parameters parameters that are needed to create the java expression
* @return the Java form of this expression.
*/
public String getJavaExpression(final ExpressionOperatorParameter parameters) {
if (javaExpression == null) {
return rootExpressionOperator.getJavaString(parameters);
}
return javaExpression;
}
/**
* @param parameters parameters that are needed to create the java expression
* @return the Java form of this expression that also writes the results to a {@link ColumnBuilder}.
*/
public String getJavaExpressionWithAppend(final ExpressionOperatorParameter parameters) {
String appendExpression = rootExpressionOperator.getJavaExpressionWithAppend(parameters);
if (appendExpression == null) {
if (isMultiValued()) {
String primitiveTypeName = getOutputType(parameters).toJavaArrayType().getSimpleName();
appendExpression =
new StringBuilder(primitiveTypeName)
.append(" results = ")
.append(getJavaExpression(parameters))
.append(";\n")
.append(COUNT)
.append(".appendInt(results.length);\n")
.append("for (int i = 0; i < results.length; ++i) {\n")
.append(RESULT)
.append(".append")
.append(getOutputType(parameters).getName())
.append("(results[i]);\n}")
.toString();
} else {
appendExpression =
new StringBuilder(RESULT)
.append(".append")
.append(getOutputType(parameters).getName())
.append("(")
.append(getJavaExpression(parameters))
.append(");")
.toString();
}
}
return appendExpression;
}
/**
* @param parameters parameters that are needed to determine the output type
* @return the type of the output
*/
public Type getOutputType(final ExpressionOperatorParameter parameters) {
return rootExpressionOperator.getOutputType(parameters);
}
/**
* Reset {@link #javaExpression}.
*/
public void resetJavaExpression() {
javaExpression = null;
}
/**
* @param optype Class to find
* @return true if the operator is in the expression
*/
public boolean hasOperator(final Class<?> optype) {
LinkedList<ExpressionOperator> ops = Lists.newLinkedList();
ops.add(getRootExpressionOperator());
while (!ops.isEmpty()) {
final ExpressionOperator op = ops.pop();
if (op.getClass().equals(optype)) {
return true;
}
ops.addAll(op.getChildren());
}
return false;
}
/**
* An expression is a constant expression when it has to be evaluated only once. This means that an expression with
* variables, state or random is likely not a constant.
*
* @return if this expression evaluates to a constant
*/
public boolean isConstant() {
return !hasOperator(VariableExpression.class)
&& !hasOperator(StateExpression.class)
&& !hasOperator(RandomExpression.class);
}
/**
* An expression is a registeredUDF expression when it contains a python expression.
*
* @return if this expression contains a python UDF.
*/
public boolean isRegisteredPythonUDF() {
return hasOperator(PyUDFExpression.class);
}
/**
* An expression is "multivalued" when it has a primitive array return type.
*
* @return if the root expression has a primitive array return type
*/
public boolean isMultiValued() {
return rootExpressionOperator.hasArrayOutputType();
}
}