/*
***************************************************************************************
* Copyright (C) 2006 EsperTech, Inc. All rights reserved. *
* http://www.espertech.com/esper *
* 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.ops;
import com.espertech.esper.client.EventBean;
import com.espertech.esper.epl.expression.core.*;
import com.espertech.esper.metrics.instrumentation.InstrumentationHelper;
import com.espertech.esper.type.MathArithTypeEnum;
import com.espertech.esper.util.JavaClassHelper;
import java.io.StringWriter;
import java.math.BigDecimal;
/**
* 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 ExprNode validate(ExprValidationContext validationContext) throws ExprValidationException {
if (this.getChildNodes().length != 2) {
throw new ExprValidationException("Arithmatic node must have 2 parameters");
}
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()[0].getExprEvaluator();
evaluatorRight = this.getChildNodes()[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, validationContext.getEngineImportService().getDefaultMathContext());
return null;
}
public Class getType() {
return resultType;
}
public boolean isConstantResult() {
return false;
}
public Object evaluate(EventBean[] eventsPerStream, boolean isNewData, ExprEvaluatorContext exprEvaluatorContext) {
if (InstrumentationHelper.ENABLED) {
InstrumentationHelper.get().qExprMath(this, mathArithTypeEnum.getExpressionText());
}
Object valueChildOne = evaluatorLeft.evaluate(eventsPerStream, isNewData, exprEvaluatorContext);
if (valueChildOne == null) {
if (InstrumentationHelper.ENABLED) {
InstrumentationHelper.get().aExprMath(null);
}
return null;
}
Object valueChildTwo = evaluatorRight.evaluate(eventsPerStream, isNewData, exprEvaluatorContext);
if (valueChildTwo == null) {
if (InstrumentationHelper.ENABLED) {
InstrumentationHelper.get().aExprMath(null);
}
return null;
}
// arithTypeEnumComputer is initialized by validation
if (InstrumentationHelper.ENABLED) {
Object result = arithTypeEnumComputer.compute((Number) valueChildOne, (Number) valueChildTwo);
InstrumentationHelper.get().aExprMath(result);
return result;
}
return arithTypeEnumComputer.compute((Number) valueChildOne, (Number) valueChildTwo);
}
public void toPrecedenceFreeEPL(StringWriter writer) {
this.getChildNodes()[0].toEPL(writer, getPrecedence());
writer.append(mathArithTypeEnum.getExpressionText());
this.getChildNodes()[1].toEPL(writer, getPrecedence());
}
public ExprPrecedenceEnum getPrecedence() {
if (mathArithTypeEnum == MathArithTypeEnum.MULTIPLY ||
mathArithTypeEnum == MathArithTypeEnum.DIVIDE ||
mathArithTypeEnum == MathArithTypeEnum.MODULO) {
return ExprPrecedenceEnum.MULTIPLY;
} else {
return ExprPrecedenceEnum.ADDITIVE;
}
}
public boolean equalsNode(ExprNode node, boolean ignoreStreamPrefix) {
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;
}
}