/* *************************************************************************************** * 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.util.JavaClassHelper; import com.espertech.esper.util.SimpleNumberBigDecimalCoercer; import com.espertech.esper.util.SimpleNumberBigIntegerCoercer; import com.espertech.esper.util.SimpleNumberCoercerFactory; import java.io.StringWriter; import java.math.BigDecimal; import java.math.BigInteger; import java.util.Arrays; import java.util.Iterator; /** * Represents the between-clause function in an expression tree. */ public class ExprBetweenNodeImpl extends ExprNodeBase implements ExprEvaluator, ExprBetweenNode { private final boolean isLowEndpointIncluded; private final boolean isHighEndpointIncluded; private final boolean isNotBetween; private boolean isAlwaysFalse; private transient ExprBetweenComp computer; private transient ExprEvaluator[] evaluators; private static final long serialVersionUID = -9089344387956311948L; /** * Ctor. * * @param lowEndpointIncluded is true for the regular 'between' or false for "val in (a:b)" (open range), or * false if the endpoint is not included * @param highEndpointIncluded indicates whether the high endpoint is included * @param notBetween is true for 'not between' or 'not in (a:b), or false for a regular between */ public ExprBetweenNodeImpl(boolean lowEndpointIncluded, boolean highEndpointIncluded, boolean notBetween) { isLowEndpointIncluded = lowEndpointIncluded; isHighEndpointIncluded = highEndpointIncluded; isNotBetween = notBetween; } public ExprEvaluator getExprEvaluator() { return this; } public boolean isConstantResult() { return false; } /** * Returns true if the low endpoint is included, false if not * * @return indicator if endppoint is included */ public boolean isLowEndpointIncluded() { return isLowEndpointIncluded; } /** * Returns true if the high endpoint is included, false if not * * @return indicator if endppoint is included */ public boolean isHighEndpointIncluded() { return isHighEndpointIncluded; } /** * Returns true for inverted range, or false for regular (openn/close/half-open/half-closed) ranges. * * @return true for not betwene, false for between */ public boolean isNotBetween() { return isNotBetween; } public ExprNode validate(ExprValidationContext validationContext) throws ExprValidationException { if (this.getChildNodes().length != 3) { throw new ExprValidationException("The Between operator requires exactly 3 child expressions"); } // Must be either numeric or string evaluators = ExprNodeUtility.getEvaluators(this.getChildNodes()); Class typeOne = JavaClassHelper.getBoxedType(evaluators[0].getType()); Class typeTwo = JavaClassHelper.getBoxedType(evaluators[1].getType()); Class typeThree = JavaClassHelper.getBoxedType(evaluators[2].getType()); if (typeOne == null) { throw new ExprValidationException("Null value not allowed in between-clause"); } Class compareType; if ((typeTwo == null) || (typeThree == null)) { isAlwaysFalse = true; } else { if ((typeOne != String.class) || (typeTwo != String.class) || (typeThree != String.class)) { if (!JavaClassHelper.isNumeric(typeOne)) { throw new ExprValidationException("Implicit conversion from datatype '" + typeOne.getSimpleName() + "' to numeric is not allowed"); } if (!JavaClassHelper.isNumeric(typeTwo)) { throw new ExprValidationException("Implicit conversion from datatype '" + typeTwo.getSimpleName() + "' to numeric is not allowed"); } if (!JavaClassHelper.isNumeric(typeThree)) { throw new ExprValidationException("Implicit conversion from datatype '" + typeThree.getSimpleName() + "' to numeric is not allowed"); } } Class intermedType = JavaClassHelper.getCompareToCoercionType(typeOne, typeTwo); compareType = JavaClassHelper.getCompareToCoercionType(intermedType, typeThree); computer = makeComputer(compareType, typeOne, typeTwo, typeThree); } return null; } public Class getType() { return Boolean.class; } public Object evaluate(EventBean[] eventsPerStream, boolean isNewData, ExprEvaluatorContext exprEvaluatorContext) { if (InstrumentationHelper.ENABLED) { InstrumentationHelper.get().qExprBetween(this); } if (isAlwaysFalse) { if (InstrumentationHelper.ENABLED) { InstrumentationHelper.get().aExprBetween(false); } return false; } // Evaluate first child which is the base value to compare to Object value = evaluators[0].evaluate(eventsPerStream, isNewData, exprEvaluatorContext); if (value == null) { if (InstrumentationHelper.ENABLED) { InstrumentationHelper.get().aExprBetween(false); } return false; } Object lower = evaluators[1].evaluate(eventsPerStream, isNewData, exprEvaluatorContext); Object higher = evaluators[2].evaluate(eventsPerStream, isNewData, exprEvaluatorContext); boolean result = computer.isBetween(value, lower, higher); if (isNotBetween) { if (InstrumentationHelper.ENABLED) { InstrumentationHelper.get().aExprBetween(!result); } return !result; } if (InstrumentationHelper.ENABLED) { InstrumentationHelper.get().aExprBetween(result); } return result; } public boolean equalsNode(ExprNode node, boolean ignoreStreamPrefix) { if (!(node instanceof ExprBetweenNodeImpl)) { return false; } ExprBetweenNodeImpl other = (ExprBetweenNodeImpl) node; return other.isNotBetween == this.isNotBetween; } public void toPrecedenceFreeEPL(StringWriter writer) { Iterator<ExprNode> it = Arrays.asList(this.getChildNodes()).iterator(); it.next().toEPL(writer, getPrecedence()); if (isNotBetween) { writer.append(" not between "); } else { writer.append(" between "); } it.next().toEPL(writer, getPrecedence()); writer.append(" and "); it.next().toEPL(writer, getPrecedence()); } public ExprPrecedenceEnum getPrecedence() { return ExprPrecedenceEnum.RELATIONAL_BETWEEN_IN; } private ExprBetweenComp makeComputer(Class compareType, Class valueType, Class lowType, Class highType) { ExprBetweenComp computer; if (compareType == String.class) { computer = new ExprBetweenCompString(isLowEndpointIncluded, isHighEndpointIncluded); } else if (compareType == BigDecimal.class) { computer = new ExprBetweenCompBigDecimal(isLowEndpointIncluded, isHighEndpointIncluded, valueType, lowType, highType); } else if (compareType == BigInteger.class) { computer = new ExprBetweenCompBigInteger(isLowEndpointIncluded, isHighEndpointIncluded, valueType, lowType, highType); } else if (compareType == Long.class) { computer = new ExprBetweenCompLong(isLowEndpointIncluded, isHighEndpointIncluded); } else { computer = new ExprBetweenCompDouble(isLowEndpointIncluded, isHighEndpointIncluded); } return computer; } private interface ExprBetweenComp { public boolean isBetween(Object value, Object lower, Object upper); } private static class ExprBetweenCompString implements ExprBetweenComp { private boolean isLowIncluded; private boolean isHighIncluded; public ExprBetweenCompString(boolean lowIncluded, boolean isHighIncluded) { this.isLowIncluded = lowIncluded; this.isHighIncluded = isHighIncluded; } public boolean isBetween(Object value, Object lower, Object upper) { if ((value == null) || (lower == null) || ((upper == null))) { return false; } String valueStr = (String) value; String lowerStr = (String) lower; String upperStr = (String) upper; if (upperStr.compareTo(lowerStr) < 0) { String temp = upperStr; upperStr = lowerStr; lowerStr = temp; } if (valueStr.compareTo(lowerStr) < 0) { return false; } if (valueStr.compareTo(upperStr) > 0) { return false; } if (!isLowIncluded) { if (valueStr.equals(lowerStr)) { return false; } } if (!isHighIncluded) { if (valueStr.equals(upperStr)) { return false; } } return true; } public boolean isEqualsEndpoint(Object value, Object endpoint) { return value.equals(endpoint); } } private static class ExprBetweenCompDouble implements ExprBetweenComp { private boolean isLowIncluded; private boolean isHighIncluded; public ExprBetweenCompDouble(boolean lowIncluded, boolean highIncluded) { isLowIncluded = lowIncluded; isHighIncluded = highIncluded; } public boolean isBetween(Object value, Object lower, Object upper) { if ((value == null) || (lower == null) || ((upper == null))) { return false; } double valueD = ((Number) value).doubleValue(); double lowerD = ((Number) lower).doubleValue(); double upperD = ((Number) upper).doubleValue(); if (lowerD > upperD) { double temp = upperD; upperD = lowerD; lowerD = temp; } if (valueD > lowerD) { if (valueD < upperD) { return true; } if (isHighIncluded) { return valueD == upperD; } return false; } if (isLowIncluded && (valueD == lowerD)) { return true; } return false; } } private static class ExprBetweenCompLong implements ExprBetweenComp { private boolean isLowIncluded; private boolean isHighIncluded; public ExprBetweenCompLong(boolean lowIncluded, boolean highIncluded) { isLowIncluded = lowIncluded; isHighIncluded = highIncluded; } public boolean isBetween(Object value, Object lower, Object upper) { if ((value == null) || (lower == null) || ((upper == null))) { return false; } long valueD = ((Number) value).longValue(); long lowerD = ((Number) lower).longValue(); long upperD = ((Number) upper).longValue(); if (lowerD > upperD) { long temp = upperD; upperD = lowerD; lowerD = temp; } if (valueD > lowerD) { if (valueD < upperD) { return true; } if (isHighIncluded) { return valueD == upperD; } return false; } if (isLowIncluded && (valueD == lowerD)) { return true; } return false; } } private static class ExprBetweenCompBigDecimal implements ExprBetweenComp { private boolean isLowIncluded; private boolean isHighIncluded; private SimpleNumberBigDecimalCoercer numberCoercerLower; private SimpleNumberBigDecimalCoercer numberCoercerUpper; private SimpleNumberBigDecimalCoercer numberCoercerValue; public ExprBetweenCompBigDecimal(boolean lowIncluded, boolean highIncluded, Class valueType, Class lowerType, Class upperType) { isLowIncluded = lowIncluded; isHighIncluded = highIncluded; numberCoercerLower = SimpleNumberCoercerFactory.getCoercerBigDecimal(lowerType); numberCoercerUpper = SimpleNumberCoercerFactory.getCoercerBigDecimal(upperType); numberCoercerValue = SimpleNumberCoercerFactory.getCoercerBigDecimal(valueType); } public boolean isBetween(Object valueUncast, Object lowerUncast, Object upperUncast) { if ((valueUncast == null) || (lowerUncast == null) || ((upperUncast == null))) { return false; } BigDecimal value = numberCoercerValue.coerceBoxedBigDec((Number) valueUncast); BigDecimal lower = numberCoercerLower.coerceBoxedBigDec((Number) lowerUncast); BigDecimal upper = numberCoercerUpper.coerceBoxedBigDec((Number) upperUncast); if (lower.compareTo(upper) > 0) { BigDecimal temp = upper; upper = lower; lower = temp; } int valueComparedLower = value.compareTo(lower); if (valueComparedLower > 0) { int valueComparedUpper = value.compareTo(upper); if (valueComparedUpper < 0) { return true; } return isHighIncluded && valueComparedUpper == 0; } if (isLowIncluded && valueComparedLower == 0) { return true; } return false; } } private static class ExprBetweenCompBigInteger implements ExprBetweenComp { private boolean isLowIncluded; private boolean isHighIncluded; private SimpleNumberBigIntegerCoercer numberCoercerLower; private SimpleNumberBigIntegerCoercer numberCoercerUpper; private SimpleNumberBigIntegerCoercer numberCoercerValue; public ExprBetweenCompBigInteger(boolean lowIncluded, boolean highIncluded, Class valueType, Class lowerType, Class upperType) { isLowIncluded = lowIncluded; isHighIncluded = highIncluded; numberCoercerLower = SimpleNumberCoercerFactory.getCoercerBigInteger(lowerType); numberCoercerUpper = SimpleNumberCoercerFactory.getCoercerBigInteger(upperType); numberCoercerValue = SimpleNumberCoercerFactory.getCoercerBigInteger(valueType); } public boolean isBetween(Object value, Object lower, Object upper) { if ((value == null) || (lower == null) || ((upper == null))) { return false; } BigInteger valueD = numberCoercerValue.coerceBoxedBigInt((Number) value); BigInteger lowerD = numberCoercerLower.coerceBoxedBigInt((Number) lower); BigInteger upperD = numberCoercerUpper.coerceBoxedBigInt((Number) upper); if (lowerD.compareTo(upperD) > 0) { BigInteger temp = upperD; upperD = lowerD; lowerD = temp; } if (valueD.compareTo(lowerD) > 0) { if (valueD.compareTo(upperD) < 0) { return true; } if (isHighIncluded) { return valueD.equals(upperD); } return false; } if (isLowIncluded && (valueD.equals(lowerD))) { return true; } return false; } } }