/************************************************************************************** * 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.util.JavaClassHelper; import com.espertech.esper.util.SimpleNumberBigDecimalCoercer; import com.espertech.esper.util.SimpleNumberBigIntegerCoercer; import com.espertech.esper.util.SimpleNumberCoercerFactory; import java.math.BigDecimal; import java.math.BigInteger; import java.util.Iterator; import java.util.Map; /** * 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 Map<String, Object> getEventType() { return null; } public void validate(ExprValidationContext validationContext) throws ExprValidationException { if (this.getChildNodes().size() != 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); } } public Class getType() { return Boolean.class; } public Object evaluate(EventBean[] eventsPerStream, boolean isNewData, ExprEvaluatorContext exprEvaluatorContext) { if (isAlwaysFalse) { return false; } // Evaluate first child which is the base value to compare to Object value = evaluators[0].evaluate(eventsPerStream, isNewData, exprEvaluatorContext); if (value == null) { 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) { return !result; } return result; } public boolean equalsNode(ExprNode node_) { if (!(node_ instanceof ExprBetweenNodeImpl)) { return false; } ExprBetweenNodeImpl other = (ExprBetweenNodeImpl) node_; return other.isNotBetween == this.isNotBetween; } public String toExpressionString() { StringBuilder buffer = new StringBuilder(); Iterator<ExprNode> it = this.getChildNodes().iterator(); buffer.append(it.next().toExpressionString()); if (isNotBetween) { buffer.append(" not between "); } else { buffer.append(" between "); } buffer.append(it.next().toExpressionString()); buffer.append(" and "); buffer.append(it.next().toExpressionString()); return buffer.toString(); } 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 value, Object lower, Object upper) { if ((value == null) || (lower == null) || ((upper == null))) { return false; } BigDecimal valueD = numberCoercerValue.coerceBoxedBigDec((Number) value); BigDecimal lowerD = numberCoercerLower.coerceBoxedBigDec((Number) lower); BigDecimal upperD = numberCoercerUpper.coerceBoxedBigDec((Number) upper); if (lowerD.compareTo(upperD) > 0) { BigDecimal 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; } } 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; } } }