package edu.washington.escience.myria.expression; import java.util.List; import java.util.Objects; import com.fasterxml.jackson.annotation.JsonProperty; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; import edu.washington.escience.myria.SimplePredicate; import edu.washington.escience.myria.Type; import edu.washington.escience.myria.expression.evaluate.ExpressionOperatorParameter; /** * An ExpressionOperator with two children. */ public abstract class BinaryExpression extends ExpressionOperator { /***/ private static final long serialVersionUID = 1L; /** The left child. */ @JsonProperty private final ExpressionOperator left; /** The right child. */ @JsonProperty private final ExpressionOperator right; /** * This is not really unused, it's used automagically by Jackson deserialization. */ protected BinaryExpression() { left = null; right = null; } /** * @param left the left child. * @param right the right child. */ protected BinaryExpression(final ExpressionOperator left, final ExpressionOperator right) { this.left = left; this.right = right; } /** * @return the left child; */ public final ExpressionOperator getLeft() { return left; } /** * @return the right child; */ public final ExpressionOperator getRight() { return right; } @Override public List<ExpressionOperator> getChildren() { ImmutableList.Builder<ExpressionOperator> children = ImmutableList.builder(); return children.add(getLeft()).add(getRight()).build(); } /** * Returns the infix binary string: "(" + left + infix + right + ")". E.g, for {@link PlusExpression}, * <code>infix</code> is <code>"+"</code>. * * @param infix the string representation of the Operator. * @param parameters parameters that are needed to create the java expression * @return the Java string for this operator. */ protected final String getInfixBinaryString( final String infix, final ExpressionOperatorParameter parameters) { return new StringBuilder("(") .append(getLeft().getJavaString(parameters)) .append(infix) .append(getRight().getJavaString(parameters)) .append(')') .toString(); } /** * Returns the object comparison string: left + ".compareTo(" + right + ")" + op + "0". E.g, for * {@link EqualsExpression}, <code>op</code> is <code>==</code> and <code>value</code> is <code>0</code>. * * @param op integer comparison operator >, <, ==, >=, <=. * @param parameters parameters that are needed to create the java expression * @return the Java string for this operator. */ protected final String getObjectComparisonString( final SimplePredicate.Op op, final ExpressionOperatorParameter parameters) { return new StringBuilder("(") .append(getLeft().getJavaString(parameters)) .append(".compareTo(") .append(getRight().getJavaString(parameters)) .append(')') .append(op.toJavaString()) .append(0) .append(")") .toString(); } /** * Returns the function call binary string: functionName + '(' + left + ',' + right + ")". E.g, for * {@link PowExpression}, <code>functionName</code> is <code>"Math.pow"</code>. * * @param functionName the string representation of the Java function name. * @param parameters parameters that are needed to create the java expression * @return the Java string for this operator. */ protected final String getFunctionCallBinaryString( final String functionName, final ExpressionOperatorParameter parameters) { return new StringBuilder(functionName) .append('(') .append(getLeft().getJavaString(parameters)) .append(',') .append(getRight().getJavaString(parameters)) .append(')') .toString(); } /** * Returns the left function call string: functionName + '(' + left+ ')'. * * @param functionName the string representation of the Java function name. * @param parameters parameters that are needed to create the java expression * @return the Java string for this operator. */ protected final String getLeftFunctionCallString( final String functionName, final ExpressionOperatorParameter parameters) { return new StringBuilder(functionName) .append('(') .append(getLeft().getJavaString(parameters)) .append(')') .toString(); } /** * Returns the left function call string with additional parameter: functionName+'('+left+','+'parameter'+')'. * * @param functionName the string representation of the Java function name. * @param parameters parameters that are needed to create the java expression * @param additionalParameter user specified additional parameter. * @return the Java string for this operator. */ protected final String getLeftFunctionCallWithParameterString( final String functionName, final ExpressionOperatorParameter parameters, final String additionalParameter) { return new StringBuilder(functionName) .append('(') .append(getLeft().getJavaString(parameters)) .append(",") .append(additionalParameter) .append(')') .toString(); } /** * A function that could be used as the default hash code for a binary expression. * * @return a hash of (getClass().getCanonicalName(), left, right). */ protected final int defaultHashCode() { return Objects.hash(getClass().getCanonicalName(), left, right); } @Override public int hashCode() { return defaultHashCode(); } @Override public boolean equals(final Object other) { if (other == null || !getClass().equals(other.getClass())) { return false; } BinaryExpression otherExpr = (BinaryExpression) other; return Objects.equals(left, otherExpr.left) && Objects.equals(right, otherExpr.right); } /** * A function that could be used as the default type checker for a binary expression where both operands must be * numeric. * * @param parameters parameters that are needed to determine the output type * @return the default numeric type, based on the types of the children and Java type precedence. */ protected final Type checkAndReturnDefaultNumericType( final ExpressionOperatorParameter parameters) { return checkAndReturnDefaultNumericType( parameters, ImmutableList.of(Type.DOUBLE_TYPE, Type.FLOAT_TYPE, Type.LONG_TYPE, Type.INT_TYPE)); } /** * A function that could be used as the default type checker for a binary expression where both operands must be * numeric. * * @param parameters parameters that are needed to determine the output type * @param validTypes a list of valid types ordered by their precedence * @return the default numeric type, based on the types of the children and type precedence. */ protected final Type checkAndReturnDefaultNumericType( final ExpressionOperatorParameter parameters, final List<Type> validTypes) { Type leftType = getLeft().getOutputType(parameters); Type rightType = getRight().getOutputType(parameters); int leftIdx = validTypes.indexOf(leftType); int rightIdx = validTypes.indexOf(rightType); Preconditions.checkArgument( leftIdx != -1, "%s cannot handle left child [%s] of Type %s", getClass().getSimpleName(), getLeft(), leftType); Preconditions.checkArgument( rightIdx != -1, "%s cannot handle right child [%s] of Type %s", getClass().getSimpleName(), getRight(), rightType); return validTypes.get(Math.min(leftIdx, rightIdx)); } /** * A function that could be used as the default type checker for a binary expression where each operand must be of a * specified type. * * @param leftExpectedType expected type of left operand * @param rightExpectedType expected type of right operand * @param parameters parameters that are needed to determine the output type */ protected final void checkOperandTypes( final Type leftExpectedType, final Type rightExpectedType, final ExpressionOperatorParameter parameters) { Type leftType = getLeft().getOutputType(parameters); Type rightType = getRight().getOutputType(parameters); Preconditions.checkArgument( leftType == leftExpectedType, "%s cannot handle left child [%s] of Type %s", getClass().getSimpleName(), getLeft(), leftType); Preconditions.checkArgument( rightType == rightExpectedType, "%s cannot handle right child [%s] of Type %s", getClass().getSimpleName(), getRight(), rightType); } /** * A function that could be used as the default type checker for a binary expression where both operands must be * boolean. * * @param parameters parameters that are needed to determine the output type */ protected final void checkBooleanType(final ExpressionOperatorParameter parameters) { checkOperandTypes(Type.BOOLEAN_TYPE, Type.BOOLEAN_TYPE, parameters); } }