/**************************************************************************************
* Copyright (C) 2008 EsperTech, Inc. All rights reserved. *
* http://esper.codehaus.org *
* http://www.espertech.com *
* ---------------------------------------------------------------------------------- *
* The software in this package is published under the terms of the GPL license *
* a copy of which has been included with this distribution in the license.txt file. *
**************************************************************************************/
package com.espertech.esper.epl.expression;
import com.espertech.esper.client.EventBean;
import com.espertech.esper.type.MathArithTypeEnum;
import com.espertech.esper.util.JavaClassHelper;
import java.math.BigDecimal;
import java.util.Map;
/**
* Represents a simple Math (+/-/divide/*) in a filter expression tree.
*/
public class ExprMathNode extends ExprNodeBase implements ExprEvaluator
{
private final MathArithTypeEnum mathArithTypeEnum;
private final boolean isIntegerDivision;
private final boolean isDivisionByZeroReturnsNull;
private transient MathArithTypeEnum.Computer arithTypeEnumComputer;
private Class resultType;
private transient ExprEvaluator evaluatorLeft;
private transient ExprEvaluator evaluatorRight;
private static final long serialVersionUID = 6479683588602862158L;
/**
* Ctor.
* @param mathArithTypeEnum - type of math
* @param isIntegerDivision - false for division returns double, true for using Java-standard integer division
* @param isDivisionByZeroReturnsNull - false for division-by-zero returns infinity, true for null
*/
public ExprMathNode(MathArithTypeEnum mathArithTypeEnum, boolean isIntegerDivision, boolean isDivisionByZeroReturnsNull)
{
this.mathArithTypeEnum = mathArithTypeEnum;
this.isIntegerDivision = isIntegerDivision;
this.isDivisionByZeroReturnsNull = isDivisionByZeroReturnsNull;
}
public ExprEvaluator getExprEvaluator()
{
return this;
}
public void validate(ExprValidationContext validationContext) throws ExprValidationException
{
if (this.getChildNodes().size() != 2)
{
throw new ExprValidationException("Arithmatic node must have 2 child nodes");
}
for (ExprNode child : this.getChildNodes())
{
Class childType = child.getExprEvaluator().getType();
if (!JavaClassHelper.isNumeric(childType))
{
throw new ExprValidationException("Implicit conversion from datatype '" +
childType.getSimpleName() +
"' to numeric is not allowed");
}
}
// Determine result type, set up compute function
evaluatorLeft = this.getChildNodes().get(0).getExprEvaluator();
evaluatorRight = this.getChildNodes().get(1).getExprEvaluator();
Class childTypeOne = evaluatorLeft.getType();
Class childTypeTwo = evaluatorRight.getType();
if ((childTypeOne == short.class || childTypeOne == Short.class) &&
(childTypeTwo == short.class || childTypeTwo == Short.class)) {
resultType = Integer.class;
}
else if ((childTypeOne == byte.class || childTypeOne == Byte.class) &&
(childTypeTwo == byte.class || childTypeTwo == Byte.class)) {
resultType = Integer.class;
}
else if (childTypeOne.equals(childTypeTwo))
{
resultType = JavaClassHelper.getBoxedType(childTypeTwo);
}
else
{
resultType = JavaClassHelper.getArithmaticCoercionType(childTypeOne, childTypeTwo);
}
if ((mathArithTypeEnum == MathArithTypeEnum.DIVIDE) && (!isIntegerDivision))
{
if (resultType != BigDecimal.class)
{
resultType = Double.class;
}
}
arithTypeEnumComputer = mathArithTypeEnum.getComputer(resultType, childTypeOne, childTypeTwo, isIntegerDivision, isDivisionByZeroReturnsNull);
}
public Class getType()
{
return resultType;
}
public boolean isConstantResult()
{
return false;
}
public Map<String, Object> getEventType() {
return null;
}
public Object evaluate(EventBean[] eventsPerStream, boolean isNewData, ExprEvaluatorContext exprEvaluatorContext)
{
Object valueChildOne = evaluatorLeft.evaluate(eventsPerStream, isNewData, exprEvaluatorContext);
if (valueChildOne == null)
{
return null;
}
Object valueChildTwo = evaluatorRight.evaluate(eventsPerStream, isNewData, exprEvaluatorContext);
if (valueChildTwo == null)
{
return null;
}
// arithTypeEnumComputer is initialized by validation
return arithTypeEnumComputer.compute((Number) valueChildOne, (Number) valueChildTwo);
}
public String toExpressionString()
{
StringBuilder buffer = new StringBuilder();
buffer.append('(');
buffer.append(this.getChildNodes().get(0).toExpressionString());
buffer.append(mathArithTypeEnum.getExpressionText());
buffer.append(this.getChildNodes().get(1).toExpressionString());
buffer.append(')');
return buffer.toString();
}
public boolean equalsNode(ExprNode node)
{
if (!(node instanceof ExprMathNode))
{
return false;
}
ExprMathNode other = (ExprMathNode) node;
if (other.mathArithTypeEnum != this.mathArithTypeEnum)
{
return false;
}
return true;
}
/**
* Returns the type of math.
* @return math type
*/
public MathArithTypeEnum getMathArithTypeEnum()
{
return mathArithTypeEnum;
}
}