/************************************************************************************** * 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.CoercionException; import com.espertech.esper.util.JavaClassHelper; import com.espertech.esper.util.SimpleNumberCoercer; import com.espertech.esper.util.SimpleNumberCoercerFactory; import java.lang.reflect.Array; import java.util.*; /** * Represents the in-clause (set check) function in an expression tree. */ public class ExprInNodeImpl extends ExprNodeBase implements ExprEvaluator, ExprInNode { private final boolean isNotIn; private boolean mustCoerce; private boolean hasCollectionOrArray; private transient SimpleNumberCoercer coercer; private transient ExprEvaluator[] evaluators; private static final long serialVersionUID = -601723009914169907L; /** * Ctor. * @param isNotIn is true for "not in" and false for "in" */ public ExprInNodeImpl(boolean isNotIn) { this.isNotIn = isNotIn; } public ExprEvaluator getExprEvaluator() { return this; } /** * Returns true for not-in, false for regular in * @return false for "val in (a,b,c)" or true for "val not in (a,b,c)" */ public boolean isNotIn() { return isNotIn; } public Map<String, Object> getEventType() { return null; } public void validate(ExprValidationContext validationContext) throws ExprValidationException { if (this.getChildNodes().size() < 2) { throw new ExprValidationException("The IN operator requires at least 2 child expressions"); } evaluators = ExprNodeUtility.getEvaluators(this.getChildNodes()); // Must be the same boxed type returned by expressions under this Class typeOne = JavaClassHelper.getBoxedType(evaluators[0].getType()); // collections, array or map not supported if ((typeOne.isArray()) || (JavaClassHelper.isImplementsInterface(typeOne, Collection.class)) || (JavaClassHelper.isImplementsInterface(typeOne, Map.class))) { throw new ExprValidationException("Collection or array comparison is not allowed for the IN, ANY, SOME or ALL keywords"); } List<Class> comparedTypes = new ArrayList<Class>(); comparedTypes.add(typeOne); hasCollectionOrArray = false; for (int i = 0; i < this.getChildNodes().size() - 1; i++) { Class propType = evaluators[i + 1].getType(); if (propType == null) { continue; } if (propType.isArray()) { hasCollectionOrArray = true; if (propType.getComponentType() != Object.class) { comparedTypes.add(propType.getComponentType()); } } else if (JavaClassHelper.isImplementsInterface(propType, Collection.class)) { hasCollectionOrArray = true; } else if (JavaClassHelper.isImplementsInterface(propType, Map.class)) { hasCollectionOrArray = true; } else { comparedTypes.add(propType); } } // Determine common denominator type Class coercionType; try { coercionType = JavaClassHelper.getCommonCoercionType(comparedTypes.toArray(new Class[comparedTypes.size()])); } catch (CoercionException ex) { throw new ExprValidationException("Implicit conversion not allowed: " + ex.getMessage()); } // Check if we need to coerce mustCoerce = false; if (JavaClassHelper.isNumeric(coercionType)) { for (Class compareType : comparedTypes) { if (coercionType != JavaClassHelper.getBoxedType(compareType)) { mustCoerce = true; } } if (mustCoerce) { coercer = SimpleNumberCoercerFactory.getCoercer(null, JavaClassHelper.getBoxedType(coercionType)); } } } public Class getType() { return Boolean.class; } public Object evaluate(EventBean[] eventsPerStream, boolean isNewData, ExprEvaluatorContext exprEvaluatorContext) { Object inPropResult = evaluators[0].evaluate(eventsPerStream, isNewData, exprEvaluatorContext); if (!hasCollectionOrArray) { if ((mustCoerce) && (inPropResult != null)) { inPropResult = coercer.coerceBoxed((Number) inPropResult); } int len = this.getChildNodes().size() - 1; if ((len > 0) && (inPropResult == null)) { return null; } boolean hasNullRow = false; for (int i = 1; i <= len; i++) { Object rightResult = evaluators[i].evaluate(eventsPerStream, isNewData, exprEvaluatorContext); if (rightResult == null) { hasNullRow = true; continue; } if (!mustCoerce) { if (rightResult.equals(inPropResult)) { return !isNotIn; } } else { Number right = coercer.coerceBoxed((Number) rightResult); if (right.equals(inPropResult)) { return !isNotIn; } } } if (hasNullRow) { return null; } return isNotIn; } else { int len = this.getChildNodes().size() - 1; boolean hasNullRow = false; for (int i = 1; i <= len; i++) { Object rightResult = evaluators[i].evaluate(eventsPerStream, isNewData, exprEvaluatorContext); if (rightResult == null) { continue; } if (rightResult instanceof Collection) { if (inPropResult == null) { return null; } Collection coll = (Collection) rightResult; if (coll.contains(inPropResult)) { return !isNotIn; } } else if (rightResult instanceof Map) { if (inPropResult == null) { return null; } Map coll = (Map) rightResult; if (coll.containsKey(inPropResult)) { return !isNotIn; } } else if (rightResult.getClass().isArray()) { int arrayLength = Array.getLength(rightResult); if ((arrayLength > 0) && (inPropResult == null)) { return null; } for (int index = 0; index < arrayLength; index++) { Object item = Array.get(rightResult, index); if (item == null) { hasNullRow = true; continue; } if (!mustCoerce) { if (inPropResult.equals(item)) { return !isNotIn; } } else { if (!(item instanceof Number)) { continue; } Number left = coercer.coerceBoxed((Number) inPropResult); Number right = coercer.coerceBoxed((Number) item); if (left.equals(right)) { return !isNotIn; } } } } else { if (inPropResult == null) { return null; } if (!mustCoerce) { if (inPropResult.equals(rightResult)) { return !isNotIn; } } else { Number left = coercer.coerceBoxed((Number) inPropResult); Number right = coercer.coerceBoxed((Number) rightResult); if (left.equals(right)) { return !isNotIn; } } } } if (hasNullRow) { return null; } return isNotIn; } } public boolean isConstantResult() { return false; } public boolean equalsNode(ExprNode node_) { if (!(node_ instanceof ExprInNodeImpl)) { return false; } ExprInNodeImpl other = (ExprInNodeImpl) node_; return other.isNotIn == this.isNotIn; } public String toExpressionString() { StringBuilder buffer = new StringBuilder(); String delimiter = ""; Iterator<ExprNode> it = this.getChildNodes().iterator(); buffer.append(it.next().toExpressionString()); if (isNotIn) { buffer.append(" not in ("); } else { buffer.append(" in ("); } do { ExprNode inSetValueExpr = it.next(); buffer.append(delimiter); buffer.append(inSetValueExpr.toExpressionString()); delimiter = ","; } while (it.hasNext()); buffer.append(')'); return buffer.toString(); } }