/*
***************************************************************************************
* 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.core;
import com.espertech.esper.client.EPException;
import com.espertech.esper.client.EventBean;
import com.espertech.esper.client.EventType;
import com.espertech.esper.client.hook.AggregationFunctionFactory;
import com.espertech.esper.client.hook.EPLMethodInvocationContext;
import com.espertech.esper.client.util.TimePeriod;
import com.espertech.esper.collection.Pair;
import com.espertech.esper.core.context.util.ContextPropertyRegistry;
import com.espertech.esper.core.service.ExprEvaluatorContextStatement;
import com.espertech.esper.core.service.StatementContext;
import com.espertech.esper.core.start.EPStatementStartMethodHelperSubselect;
import com.espertech.esper.epl.core.*;
import com.espertech.esper.epl.declexpr.ExprDeclaredNode;
import com.espertech.esper.epl.enummethod.dot.EnumMethodEnum;
import com.espertech.esper.epl.enummethod.dot.ExprDeclaredOrLambdaNode;
import com.espertech.esper.epl.enummethod.dot.ExprLambdaGoesNode;
import com.espertech.esper.epl.expression.baseagg.ExprAggregateNode;
import com.espertech.esper.epl.expression.baseagg.ExprAggregateNodeUtil;
import com.espertech.esper.epl.expression.dot.ExprDotNodeImpl;
import com.espertech.esper.epl.expression.funcs.ExprPlugInSingleRowNode;
import com.espertech.esper.epl.expression.methodagg.ExprPlugInAggNode;
import com.espertech.esper.epl.expression.ops.ExprAndNode;
import com.espertech.esper.epl.expression.ops.ExprAndNodeImpl;
import com.espertech.esper.epl.expression.ops.ExprEqualsNode;
import com.espertech.esper.epl.expression.subquery.ExprSubselectNode;
import com.espertech.esper.epl.expression.table.ExprTableAccessNode;
import com.espertech.esper.epl.expression.time.ExprTimePeriod;
import com.espertech.esper.epl.expression.visitor.*;
import com.espertech.esper.epl.join.hint.ExcludePlanHint;
import com.espertech.esper.epl.join.plan.FilterExprAnalyzer;
import com.espertech.esper.epl.join.plan.QueryGraph;
import com.espertech.esper.epl.spec.ExpressionScriptProvided;
import com.espertech.esper.epl.spec.OnTriggerSetAssignment;
import com.espertech.esper.epl.table.mgmt.TableMetadata;
import com.espertech.esper.epl.table.mgmt.TableService;
import com.espertech.esper.event.EventAdapterService;
import com.espertech.esper.event.EventBeanUtility;
import com.espertech.esper.schedule.ScheduleParameterException;
import com.espertech.esper.schedule.ScheduleSpec;
import com.espertech.esper.schedule.ScheduleSpecUtil;
import com.espertech.esper.util.CollectionUtil;
import com.espertech.esper.util.JavaClassHelper;
import com.espertech.esper.util.SimpleNumberCoercer;
import com.espertech.esper.util.SimpleNumberCoercerFactory;
import net.sf.cglib.reflect.FastClass;
import net.sf.cglib.reflect.FastMethod;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.StringWriter;
import java.lang.reflect.Array;
import java.lang.reflect.Method;
import java.util.*;
public class ExprNodeUtility {
private static final Logger log = LoggerFactory.getLogger(ExprNodeUtility.class);
public static final ExprNode[] EMPTY_EXPR_ARRAY = new ExprNode[0];
public static final ExprDeclaredNode[] EMPTY_DECLARED_ARR = new ExprDeclaredNode[0];
public static final ExpressionScriptProvided[] EMPTY_SCRIPTS = new ExpressionScriptProvided[0];
public static Object evaluateValidationTimeNoStreams(ExprEvaluator evaluator, ExprEvaluatorContext context, String expressionName) throws ExprValidationException {
try {
return evaluator.evaluate(null, true, context);
} catch (EPException ex) {
throw new ExprValidationException("Invalid " + expressionName + " expression: " + ex.getMessage(), ex);
} catch (RuntimeException ex) {
log.warn("Invalid " + expressionName + " expression evaluation: {}", ex.getMessage(), ex);
throw new ExprValidationException("Invalid " + expressionName + " expression");
}
}
public static boolean deepEqualsIsSubset(ExprNode[] subset, ExprNode[] superset) {
for (ExprNode subsetNode : subset) {
boolean found = false;
for (ExprNode supersetNode : superset) {
if (deepEquals(subsetNode, supersetNode, false)) {
found = true;
break;
}
}
if (!found) {
return false;
}
}
return true;
}
public static boolean deepEqualsIgnoreDupAndOrder(ExprNode[] setOne, ExprNode[] setTwo) {
if ((setOne.length == 0 && setTwo.length != 0) || (setOne.length != 0 && setTwo.length == 0)) {
return false;
}
// find set-one expressions in set two
boolean[] foundTwo = new boolean[setTwo.length];
for (ExprNode one : setOne) {
boolean found = false;
for (int i = 0; i < setTwo.length; i++) {
if (deepEquals(one, setTwo[i], false)) {
found = true;
foundTwo[i] = true;
}
}
if (!found) {
return false;
}
}
// find any remaining set-two expressions in set one
for (int i = 0; i < foundTwo.length; i++) {
if (foundTwo[i]) {
continue;
}
for (ExprNode one : setOne) {
if (deepEquals(one, setTwo[i], false)) {
break;
}
}
return false;
}
return true;
}
public static Map<ExprDeclaredNode, List<ExprDeclaredNode>> getDeclaredExpressionCallHierarchy(ExprDeclaredNode[] declaredExpressions) {
ExprNodeSubselectDeclaredDotVisitor visitor = new ExprNodeSubselectDeclaredDotVisitor();
Map<ExprDeclaredNode, List<ExprDeclaredNode>> calledToCallerMap = new HashMap<ExprDeclaredNode, List<ExprDeclaredNode>>();
for (ExprDeclaredNode node : declaredExpressions) {
visitor.reset();
node.accept(visitor);
for (ExprDeclaredNode called : visitor.getDeclaredExpressions()) {
if (called == node) {
continue;
}
List<ExprDeclaredNode> callers = calledToCallerMap.get(called);
if (callers == null) {
callers = new ArrayList<ExprDeclaredNode>(2);
calledToCallerMap.put(called, callers);
}
callers.add(node);
}
if (!calledToCallerMap.containsKey(node)) {
calledToCallerMap.put(node, Collections.<ExprDeclaredNode>emptyList());
}
}
return calledToCallerMap;
}
public static String toExpressionStringMinPrecedenceSafe(ExprNode node) {
try {
StringWriter writer = new StringWriter();
node.toEPL(writer, ExprPrecedenceEnum.MINIMUM);
return writer.toString();
} catch (RuntimeException ex) {
log.debug("Failed to render expression text: " + ex.getMessage(), ex);
return "";
}
}
public static String[] toExpressionStringMinPrecedenceAsArray(ExprNode[] nodes) {
String[] expressions = new String[nodes.length];
for (int i = 0; i < expressions.length; i++) {
StringWriter writer = new StringWriter();
nodes[i].toEPL(writer, ExprPrecedenceEnum.MINIMUM);
expressions[i] = writer.toString();
}
return expressions;
}
public static String toExpressionStringMinPrecedenceAsList(ExprNode[] nodes) {
StringWriter writer = new StringWriter();
toExpressionStringMinPrecedenceAsList(nodes, writer);
return writer.toString();
}
public static void toExpressionStringMinPrecedenceAsList(ExprNode[] nodes, StringWriter writer) {
String delimiter = "";
for (ExprNode node : nodes) {
writer.append(delimiter);
node.toEPL(writer, ExprPrecedenceEnum.MINIMUM);
delimiter = ",";
}
}
public static Pair<String, ExprNode> checkGetAssignmentToProp(ExprNode node) {
if (!(node instanceof ExprEqualsNode)) {
return null;
}
ExprEqualsNode equals = (ExprEqualsNode) node;
if (!(equals.getChildNodes()[0] instanceof ExprIdentNode)) {
return null;
}
ExprIdentNode identNode = (ExprIdentNode) equals.getChildNodes()[0];
return new Pair<String, ExprNode>(identNode.getFullUnresolvedName(), equals.getChildNodes()[1]);
}
public static Pair<String, ExprNode> checkGetAssignmentToVariableOrProp(ExprNode node)
throws ExprValidationException {
Pair<String, ExprNode> prop = checkGetAssignmentToProp(node);
if (prop != null) {
return prop;
}
if (!(node instanceof ExprEqualsNode)) {
return null;
}
ExprEqualsNode equals = (ExprEqualsNode) node;
if (equals.getChildNodes()[0] instanceof ExprVariableNode) {
ExprVariableNode variableNode = (ExprVariableNode) equals.getChildNodes()[0];
return new Pair<String, ExprNode>(variableNode.getVariableNameWithSubProp(), equals.getChildNodes()[1]);
}
if (equals.getChildNodes()[0] instanceof ExprTableAccessNode) {
throw new ExprValidationException("Table access expression not allowed on the left hand side, please remove the table prefix");
}
return null;
}
public static void applyFilterExpressionsIterable(Iterable<EventBean> iterable, List<ExprNode> filterExpressions, ExprEvaluatorContext exprEvaluatorContext, Collection<EventBean> eventsInWindow) {
ExprEvaluator[] evaluators = ExprNodeUtility.getEvaluators(filterExpressions);
EventBean[] events = new EventBean[1];
for (EventBean theEvent : iterable) {
events[0] = theEvent;
boolean add = true;
for (ExprEvaluator filter : evaluators) {
Object result = filter.evaluate(events, true, exprEvaluatorContext);
if ((result == null) || (!((Boolean) result))) {
add = false;
break;
}
}
if (add) {
eventsInWindow.add(events[0]);
}
}
}
public static void applyFilterExpressionIterable(Iterator<EventBean> iterator, ExprEvaluator filterExpression, ExprEvaluatorContext exprEvaluatorContext, Collection<EventBean> eventsInWindow) {
EventBean[] events = new EventBean[1];
for (; iterator.hasNext(); ) {
events[0] = iterator.next();
Object result = filterExpression.evaluate(events, true, exprEvaluatorContext);
if ((result == null) || (!((Boolean) result))) {
continue;
}
eventsInWindow.add(events[0]);
}
}
public static ExprNode connectExpressionsByLogicalAnd(List<ExprNode> nodes, ExprNode optionalAdditionalFilter) {
if (nodes.isEmpty()) {
return optionalAdditionalFilter;
}
if (optionalAdditionalFilter == null) {
if (nodes.size() == 1) {
return nodes.get(0);
}
return connectExpressionsByLogicalAnd(nodes);
}
if (nodes.size() == 1) {
return connectExpressionsByLogicalAnd(Arrays.asList(nodes.get(0), optionalAdditionalFilter));
}
ExprAndNode andNode = connectExpressionsByLogicalAnd(nodes);
andNode.addChildNode(optionalAdditionalFilter);
return andNode;
}
public static ExprAndNode connectExpressionsByLogicalAnd(Collection<ExprNode> nodes) {
if (nodes.size() < 2) {
throw new IllegalArgumentException("Invalid empty or 1-element list of nodes");
}
ExprAndNode andNode = new ExprAndNodeImpl();
for (ExprNode node : nodes) {
andNode.addChildNode(node);
}
return andNode;
}
public static ExprNode connectExpressionsByLogicalAndWhenNeeded(Collection<ExprNode> nodes) {
if (nodes.isEmpty()) {
return null;
}
if (nodes.size() == 1) {
return nodes.iterator().next();
}
return connectExpressionsByLogicalAnd(nodes);
}
/**
* Walk expression returning properties used.
*
* @param exprNode to walk
* @param visitAggregateNodes true to visit aggregation nodes
* @return list of props
*/
public static List<Pair<Integer, String>> getExpressionProperties(ExprNode exprNode, boolean visitAggregateNodes) {
ExprNodeIdentifierVisitor visitor = new ExprNodeIdentifierVisitor(visitAggregateNodes);
exprNode.accept(visitor);
return visitor.getExprProperties();
}
public static boolean isConstantValueExpr(ExprNode exprNode) {
if (!(exprNode instanceof ExprConstantNode)) {
return false;
}
ExprConstantNode constantNode = (ExprConstantNode) exprNode;
return constantNode.isConstantValue();
}
/**
* Validates the expression node subtree that has this
* node as root. Some of the nodes of the tree, including the
* root, might be replaced in the process.
*
* @param origin validate origin
* @param exprNode node
* @param validationContext context
* @return the root node of the validated subtree, possibly
* different than the root node of the unvalidated subtree
* @throws ExprValidationException when the validation fails
*/
public static ExprNode getValidatedSubtree(ExprNodeOrigin origin, ExprNode exprNode, ExprValidationContext validationContext) throws ExprValidationException {
if (exprNode instanceof ExprLambdaGoesNode) {
return exprNode;
}
try {
return getValidatedSubtreeInternal(exprNode, validationContext, true);
} catch (ExprValidationException ex) {
try {
String text;
if (exprNode instanceof ExprSubselectNode) {
ExprSubselectNode subselect = (ExprSubselectNode) exprNode;
text = EPStatementStartMethodHelperSubselect.getSubqueryInfoText(subselect.getSubselectNumber() - 1, subselect);
} else {
text = ExprNodeUtility.toExpressionStringMinPrecedenceSafe(exprNode);
if (text.length() > 40) {
String shortened = text.substring(0, 35);
text = shortened + "...(" + text.length() + " chars)";
}
text = "'" + text + "'";
}
throw new ExprValidationException("Failed to validate " +
origin.getClauseName() +
" expression " +
text + ": " +
ex.getMessage(), ex);
} catch (RuntimeException rtex) {
log.debug("Failed to render nice validation message text: " + rtex.getMessage(), rtex);
throw ex;
}
}
}
public static void getValidatedSubtree(ExprNodeOrigin origin, ExprNode[] exprNode, ExprValidationContext validationContext) throws ExprValidationException {
if (exprNode == null) {
return;
}
for (int i = 0; i < exprNode.length; i++) {
exprNode[i] = getValidatedSubtree(origin, exprNode[i], validationContext);
}
}
public static void getValidatedSubtree(ExprNodeOrigin origin, ExprNode[][] exprNode, ExprValidationContext validationContext) throws ExprValidationException {
if (exprNode == null) {
return;
}
for (ExprNode[] anExprNode : exprNode) {
getValidatedSubtree(origin, anExprNode, validationContext);
}
}
public static ExprNode getValidatedAssignment(OnTriggerSetAssignment assignment, ExprValidationContext validationContext) throws ExprValidationException {
Pair<String, ExprNode> strictAssignment = checkGetAssignmentToVariableOrProp(assignment.getExpression());
if (strictAssignment != null) {
ExprNode validatedRightSide = getValidatedSubtreeInternal(strictAssignment.getSecond(), validationContext, true);
assignment.getExpression().setChildNode(1, validatedRightSide);
return assignment.getExpression();
} else {
return getValidatedSubtreeInternal(assignment.getExpression(), validationContext, true);
}
}
private static ExprNode getValidatedSubtreeInternal(ExprNode exprNode, ExprValidationContext validationContext, boolean isTopLevel) throws ExprValidationException {
ExprNode result = exprNode;
if (exprNode instanceof ExprLambdaGoesNode) {
return exprNode;
}
for (int i = 0; i < exprNode.getChildNodes().length; i++) {
ExprNode childNode = exprNode.getChildNodes()[i];
if (childNode instanceof ExprDeclaredOrLambdaNode) {
ExprDeclaredOrLambdaNode node = (ExprDeclaredOrLambdaNode) childNode;
if (node.validated()) {
continue;
}
}
ExprNode childNodeValidated = getValidatedSubtreeInternal(childNode, validationContext, false);
exprNode.setChildNode(i, childNodeValidated);
}
try {
ExprNode optionalReplacement = exprNode.validate(validationContext);
if (optionalReplacement != null) {
return getValidatedSubtreeInternal(optionalReplacement, validationContext, isTopLevel);
}
} catch (ExprValidationException e) {
if (exprNode instanceof ExprIdentNode) {
ExprIdentNode identNode = (ExprIdentNode) exprNode;
try {
result = resolveStaticMethodOrField(identNode, e, validationContext);
} catch (ExprValidationException ex) {
e = ex;
result = resolveAsStreamName(identNode, e, validationContext);
}
} else {
throw e;
}
}
// For top-level expressions check if we perform audit
if (isTopLevel) {
if (validationContext.isExpressionAudit()) {
return (ExprNode) ExprNodeProxy.newInstance(validationContext.getStreamTypeService().getEngineURIQualifier(), validationContext.getStatementName(), result);
}
} else {
if (validationContext.isExpressionNestedAudit() && !(result instanceof ExprIdentNode) && !(ExprNodeUtility.isConstantValueExpr(result))) {
return (ExprNode) ExprNodeProxy.newInstance(validationContext.getStreamTypeService().getEngineURIQualifier(), validationContext.getStatementName(), result);
}
}
return result;
}
private static ExprNode resolveAsStreamName(ExprIdentNode identNode, ExprValidationException existingException, ExprValidationContext validationContext)
throws ExprValidationException {
ExprStreamUnderlyingNode exprStream = new ExprStreamUnderlyingNodeImpl(identNode.getUnresolvedPropertyName(), false);
try {
exprStream.validate(validationContext);
} catch (ExprValidationException ex) {
throw existingException;
}
return exprStream;
}
// Since static method calls such as "Class.method('a')" and mapped properties "Stream.property('key')"
// look the same, however as the validation could not resolve "Stream.property('key')" before calling this method,
// this method tries to resolve the mapped property as a static method.
// Assumes that this is an ExprIdentNode.
private static ExprNode resolveStaticMethodOrField(ExprIdentNode identNode, ExprValidationException propertyException, ExprValidationContext validationContext)
throws ExprValidationException {
// Reconstruct the original string
StringBuilder mappedProperty = new StringBuilder(identNode.getUnresolvedPropertyName());
if (identNode.getStreamOrPropertyName() != null) {
mappedProperty.insert(0, identNode.getStreamOrPropertyName() + '.');
}
// Parse the mapped property format into a class name, method and single string parameter
MappedPropertyParseResult parse = parseMappedProperty(mappedProperty.toString());
if (parse == null) {
ExprConstantNode constNode = resolveIdentAsEnumConst(mappedProperty.toString(), validationContext.getEngineImportService());
if (constNode == null) {
throw propertyException;
} else {
return constNode;
}
}
// If there is a class name, assume a static method is possible.
if (parse.getClassName() != null) {
List<ExprNode> parameters = Collections.singletonList((ExprNode) new ExprConstantNodeImpl(parse.getArgString()));
List<ExprChainedSpec> chain = new ArrayList<ExprChainedSpec>();
chain.add(new ExprChainedSpec(parse.getClassName(), Collections.<ExprNode>emptyList(), false));
chain.add(new ExprChainedSpec(parse.getMethodName(), parameters, false));
ExprNode result = new ExprDotNodeImpl(chain, validationContext.getEngineImportService().isDuckType(), validationContext.getEngineImportService().isUdfCache());
// Validate
try {
result.validate(validationContext);
} catch (ExprValidationException e) {
throw new ExprValidationException("Failed to resolve enumeration method, date-time method or mapped property '" + mappedProperty + "': " + e.getMessage());
}
return result;
}
// There is no class name, try a single-row function
String functionName = parse.getMethodName();
try {
Pair<Class, EngineImportSingleRowDesc> classMethodPair = validationContext.getEngineImportService().resolveSingleRow(functionName);
List<ExprNode> parameters = Collections.singletonList((ExprNode) new ExprConstantNodeImpl(parse.getArgString()));
List<ExprChainedSpec> chain = Collections.singletonList(new ExprChainedSpec(classMethodPair.getSecond().getMethodName(), parameters, false));
ExprNode result = new ExprPlugInSingleRowNode(functionName, classMethodPair.getFirst(), chain, classMethodPair.getSecond());
// Validate
try {
result.validate(validationContext);
} catch (RuntimeException e) {
throw new ExprValidationException("Plug-in aggregation function '" + parse.getMethodName() + "' failed validation: " + e.getMessage());
}
return result;
} catch (EngineImportUndefinedException e) {
// Not an single-row function
} catch (EngineImportException e) {
throw new IllegalStateException("Error resolving single-row function: " + e.getMessage(), e);
}
// Try an aggregation function factory
try {
AggregationFunctionFactory aggregationFactory = validationContext.getEngineImportService().resolveAggregationFactory(parse.getMethodName());
ExprNode result = new ExprPlugInAggNode(false, aggregationFactory, parse.getMethodName());
result.addChildNode(new ExprConstantNodeImpl(parse.getArgString()));
// Validate
try {
result.validate(validationContext);
} catch (RuntimeException e) {
throw new ExprValidationException("Plug-in aggregation function '" + parse.getMethodName() + "' failed validation: " + e.getMessage());
}
return result;
} catch (EngineImportUndefinedException e) {
// Not an aggregation function
} catch (EngineImportException e) {
throw new IllegalStateException("Error resolving aggregation: " + e.getMessage(), e);
}
// absolutely cannot be resolved
throw propertyException;
}
private static ExprConstantNode resolveIdentAsEnumConst(String constant, EngineImportService engineImportService)
throws ExprValidationException {
Object enumValue = JavaClassHelper.resolveIdentAsEnumConst(constant, engineImportService, false);
if (enumValue != null) {
return new ExprConstantNodeImpl(enumValue);
}
return null;
}
/**
* Parse the mapped property into classname, method and string argument.
* Mind this has been parsed already and is a valid mapped property.
*
* @param property is the string property to be passed as a static method invocation
* @return descriptor object
*/
public static MappedPropertyParseResult parseMappedProperty(String property) {
// get argument
int indexFirstDoubleQuote = property.indexOf("\"");
int indexFirstSingleQuote = property.indexOf("'");
int startArg;
if ((indexFirstSingleQuote == -1) && (indexFirstDoubleQuote == -1)) {
return null;
}
if ((indexFirstSingleQuote != -1) && (indexFirstDoubleQuote != -1)) {
if (indexFirstSingleQuote < indexFirstDoubleQuote) {
startArg = indexFirstSingleQuote;
} else {
startArg = indexFirstDoubleQuote;
}
} else if (indexFirstSingleQuote != -1) {
startArg = indexFirstSingleQuote;
} else {
startArg = indexFirstDoubleQuote;
}
int indexLastDoubleQuote = property.lastIndexOf("\"");
int indexLastSingleQuote = property.lastIndexOf("'");
int endArg;
if ((indexLastSingleQuote == -1) && (indexLastDoubleQuote == -1)) {
return null;
}
if ((indexLastSingleQuote != -1) && (indexLastDoubleQuote != -1)) {
if (indexLastSingleQuote > indexLastDoubleQuote) {
endArg = indexLastSingleQuote;
} else {
endArg = indexLastDoubleQuote;
}
} else if (indexLastSingleQuote != -1) {
if (indexLastSingleQuote == indexFirstSingleQuote) {
return null;
}
endArg = indexLastSingleQuote;
} else {
if (indexLastDoubleQuote == indexFirstDoubleQuote) {
return null;
}
endArg = indexLastDoubleQuote;
}
String argument = property.substring(startArg + 1, endArg);
// get method
String[] splitDots = property.split("[\\.]");
if (splitDots.length == 0) {
return null;
}
// find which element represents the method, its the element with the parenthesis
int indexMethod = -1;
for (int i = 0; i < splitDots.length; i++) {
if (splitDots[i].contains("(")) {
indexMethod = i;
break;
}
}
if (indexMethod == -1) {
return null;
}
String method = splitDots[indexMethod];
int indexParan = method.indexOf("(");
method = method.substring(0, indexParan);
if (method.length() == 0) {
return null;
}
if (splitDots.length == 1) {
// no class name
return new MappedPropertyParseResult(null, method, argument);
}
// get class
StringBuilder clazz = new StringBuilder();
for (int i = 0; i < indexMethod; i++) {
if (i > 0) {
clazz.append('.');
}
clazz.append(splitDots[i]);
}
return new MappedPropertyParseResult(clazz.toString(), method, argument);
}
public static boolean isAllConstants(List<ExprNode> parameters) {
for (ExprNode node : parameters) {
if (!node.isConstantResult()) {
return false;
}
}
return true;
}
public static ExprIdentNode getExprIdentNode(EventType[] typesPerStream, int streamId, String property) {
return new ExprIdentNodeImpl(typesPerStream[streamId], property, streamId);
}
public static Class[] getExprResultTypes(ExprEvaluator[] evaluators) {
Class[] returnTypes = new Class[evaluators.length];
for (int i = 0; i < evaluators.length; i++) {
returnTypes[i] = evaluators[i].getType();
}
return returnTypes;
}
public static Class[] getExprResultTypes(List<ExprNode> expressions) {
Class[] returnTypes = new Class[expressions.size()];
for (int i = 0; i < expressions.size(); i++) {
returnTypes[i] = expressions.get(i).getExprEvaluator().getType();
}
return returnTypes;
}
public static ExprNodeUtilMethodDesc resolveMethodAllowWildcardAndStream(String className,
Class optionalClass,
String methodName,
List<ExprNode> parameters,
EngineImportService engineImportService,
EventAdapterService eventAdapterService,
int statementId,
boolean allowWildcard,
final EventType wildcardType,
ExprNodeUtilResolveExceptionHandler exceptionHandler,
String functionName,
TableService tableService,
String engineURI) throws ExprValidationException {
Class[] paramTypes = new Class[parameters.size()];
ExprEvaluator[] childEvals = new ExprEvaluator[parameters.size()];
int count = 0;
boolean[] allowEventBeanType = new boolean[parameters.size()];
boolean[] allowEventBeanCollType = new boolean[parameters.size()];
ExprEvaluator[] childEvalsEventBeanReturnTypes = new ExprEvaluator[parameters.size()];
boolean allConstants = true;
for (ExprNode childNode : parameters) {
if (!EnumMethodEnum.isEnumerationMethod(methodName) && childNode instanceof ExprLambdaGoesNode) {
throw new ExprValidationException("Unexpected lambda-expression encountered as parameter to UDF or static method '" + methodName + "'");
}
if (childNode instanceof ExprWildcard) {
if (wildcardType == null || !allowWildcard) {
throw new ExprValidationException("Failed to resolve wildcard parameter to a given event type");
}
childEvals[count] = new ExprNodeUtilExprEvalStreamNumUnd(0, wildcardType.getUnderlyingType());
childEvalsEventBeanReturnTypes[count] = new ExprNodeUtilExprEvalStreamNumEvent(0);
paramTypes[count] = wildcardType.getUnderlyingType();
allowEventBeanType[count] = true;
allConstants = false;
count++;
continue;
}
if (childNode instanceof ExprStreamUnderlyingNode) {
ExprStreamUnderlyingNode und = (ExprStreamUnderlyingNode) childNode;
TableMetadata tableMetadata = tableService.getTableMetadataFromEventType(und.getEventType());
if (tableMetadata == null) {
childEvals[count] = childNode.getExprEvaluator();
childEvalsEventBeanReturnTypes[count] = new ExprNodeUtilExprEvalStreamNumEvent(und.getStreamId());
} else {
childEvals[count] = new BindProcessorEvaluatorStreamTable(und.getStreamId(), und.getEventType().getUnderlyingType(), tableMetadata);
childEvalsEventBeanReturnTypes[count] = new ExprNodeUtilExprEvalStreamNumEventTable(und.getStreamId(), tableMetadata);
}
paramTypes[count] = childEvals[count].getType();
allowEventBeanType[count] = true;
allConstants = false;
count++;
continue;
}
if (childNode instanceof ExprEvaluatorEnumeration) {
ExprEvaluatorEnumeration enumeration = (ExprEvaluatorEnumeration) childNode;
EventType eventType = enumeration.getEventTypeSingle(eventAdapterService, statementId);
childEvals[count] = childNode.getExprEvaluator();
paramTypes[count] = childEvals[count].getType();
allConstants = false;
if (eventType != null) {
childEvalsEventBeanReturnTypes[count] = new ExprNodeUtilExprEvalStreamNumEnumSingle(enumeration);
allowEventBeanType[count] = true;
count++;
continue;
}
EventType eventTypeColl = enumeration.getEventTypeCollection(eventAdapterService, statementId);
if (eventTypeColl != null) {
childEvalsEventBeanReturnTypes[count] = new ExprNodeUtilExprEvalStreamNumEnumColl(enumeration);
allowEventBeanCollType[count] = true;
count++;
continue;
}
}
ExprEvaluator eval = childNode.getExprEvaluator();
childEvals[count] = eval;
paramTypes[count] = eval.getType();
count++;
if (!(childNode.isConstantResult())) {
allConstants = false;
}
}
// Try to resolve the method
final FastMethod staticMethod;
Method method;
try {
if (optionalClass != null) {
method = engineImportService.resolveMethod(optionalClass, methodName, paramTypes, allowEventBeanType, allowEventBeanCollType);
} else {
method = engineImportService.resolveMethodOverloadChecked(className, methodName, paramTypes, allowEventBeanType, allowEventBeanCollType);
}
FastClass declaringClass = FastClass.create(engineImportService.getFastClassClassLoader(method.getDeclaringClass()), method.getDeclaringClass());
staticMethod = declaringClass.getMethod(method);
} catch (Exception e) {
throw exceptionHandler.handle(e);
}
// rewrite those evaluator that should return the event itself
if (CollectionUtil.isAnySet(allowEventBeanType)) {
for (int i = 0; i < parameters.size(); i++) {
if (allowEventBeanType[i] && method.getParameterTypes()[i] == EventBean.class) {
childEvals[i] = childEvalsEventBeanReturnTypes[i];
}
}
}
// rewrite those evaluators that should return the event collection
if (CollectionUtil.isAnySet(allowEventBeanCollType)) {
for (int i = 0; i < parameters.size(); i++) {
if (allowEventBeanCollType[i] && method.getParameterTypes()[i] == Collection.class) {
childEvals[i] = childEvalsEventBeanReturnTypes[i];
}
}
}
// add an evaluator if the method expects a context object
if (!method.isVarArgs() && method.getParameterTypes().length > 0 &&
method.getParameterTypes()[method.getParameterTypes().length - 1] == EPLMethodInvocationContext.class) {
childEvals = (ExprEvaluator[]) CollectionUtil.arrayExpandAddSingle(childEvals, new ExprNodeUtilExprEvalMethodContext(engineURI, functionName, eventAdapterService));
}
// handle varargs
if (method.isVarArgs()) {
// handle context parameter
int numMethodParams = method.getParameterTypes().length;
if (numMethodParams > 1 && method.getParameterTypes()[numMethodParams - 2] == EPLMethodInvocationContext.class) {
ExprEvaluator[] rewritten = new ExprEvaluator[childEvals.length + 1];
System.arraycopy(childEvals, 0, rewritten, 0, numMethodParams - 2);
rewritten[numMethodParams - 2] = new ExprNodeUtilExprEvalMethodContext(engineURI, functionName, eventAdapterService);
System.arraycopy(childEvals, numMethodParams - 2, rewritten, numMethodParams - 1, childEvals.length - (numMethodParams - 2));
childEvals = rewritten;
}
childEvals = makeVarargArrayEval(method, childEvals);
}
return new ExprNodeUtilMethodDesc(allConstants, paramTypes, childEvals, method, staticMethod, null);
}
public static void validatePlainExpression(ExprNodeOrigin origin, ExprNode expression) throws ExprValidationException {
ExprNodeSummaryVisitor summaryVisitor = new ExprNodeSummaryVisitor();
validatePlainExpression(origin, expression, summaryVisitor);
}
public static void validatePlainExpression(ExprNodeOrigin origin, ExprNode[] expressions) throws ExprValidationException {
ExprNodeSummaryVisitor summaryVisitor = new ExprNodeSummaryVisitor();
for (ExprNode expression : expressions) {
validatePlainExpression(origin, expression, summaryVisitor);
}
}
private static void validatePlainExpression(ExprNodeOrigin origin, ExprNode expression, ExprNodeSummaryVisitor summaryVisitor) throws ExprValidationException {
expression.accept(summaryVisitor);
if (summaryVisitor.isHasAggregation() || summaryVisitor.isHasSubselect() || summaryVisitor.isHasStreamSelect() || summaryVisitor.isHasPreviousPrior()) {
String text = toExpressionStringMinPrecedenceSafe(expression);
throw new ExprValidationException("Invalid " + origin.getClauseName() + " expression '" + text + "': Aggregation, sub-select, previous or prior functions are not supported in this context");
}
}
public static ExprNode validateSimpleGetSubtree(ExprNodeOrigin origin, ExprNode expression, StatementContext statementContext, EventType optionalEventType, boolean allowBindingConsumption)
throws ExprValidationException {
ExprNodeUtility.validatePlainExpression(origin, expression);
StreamTypeServiceImpl streamTypes;
if (optionalEventType != null) {
streamTypes = new StreamTypeServiceImpl(optionalEventType, null, true, statementContext.getEngineURI());
} else {
streamTypes = new StreamTypeServiceImpl(statementContext.getEngineURI(), false);
}
ExprValidationContext validationContext = new ExprValidationContext(streamTypes, statementContext.getEngineImportService(), statementContext.getStatementExtensionServicesContext(), null, statementContext.getSchedulingService(), statementContext.getVariableService(), statementContext.getTableService(), new ExprEvaluatorContextStatement(statementContext, false), statementContext.getEventAdapterService(), statementContext.getStatementName(), statementContext.getStatementId(), statementContext.getAnnotations(), statementContext.getContextDescriptor(), false, false, allowBindingConsumption, false, null, false);
return ExprNodeUtility.getValidatedSubtree(origin, expression, validationContext);
}
public static ExprValidationContext getExprValidationContextStatementOnly(StatementContext statementContext) {
return new ExprValidationContext(new StreamTypeServiceImpl(statementContext.getEngineURI(), false), statementContext.getEngineImportService(), statementContext.getStatementExtensionServicesContext(), null, statementContext.getSchedulingService(), statementContext.getVariableService(), statementContext.getTableService(), new ExprEvaluatorContextStatement(statementContext, false), statementContext.getEventAdapterService(), statementContext.getStatementName(), statementContext.getStatementId(), statementContext.getAnnotations(), statementContext.getContextDescriptor(), false, false, false, false, null, false);
}
public static Set<String> getPropertyNamesIfAllProps(ExprNode[] expressions) {
for (ExprNode expression : expressions) {
if (!(expression instanceof ExprIdentNode)) {
return null;
}
}
Set<String> uniquePropertyNames = new HashSet<String>();
for (ExprNode expression : expressions) {
ExprIdentNode identNode = (ExprIdentNode) expression;
uniquePropertyNames.add(identNode.getUnresolvedPropertyName());
}
return uniquePropertyNames;
}
public static String[] toExpressionStringsMinPrecedence(ExprNode[] expressions) {
String[] texts = new String[expressions.length];
for (int i = 0; i < expressions.length; i++) {
texts[i] = toExpressionStringMinPrecedenceSafe(expressions[i]);
}
return texts;
}
public static List<Pair<ExprNode, ExprNode>> findExpression(ExprNode selectExpression, ExprNode searchExpression) {
List<Pair<ExprNode, ExprNode>> pairs = new ArrayList<Pair<ExprNode, ExprNode>>();
if (deepEquals(selectExpression, searchExpression, false)) {
pairs.add(new Pair<ExprNode, ExprNode>(null, selectExpression));
return pairs;
}
findExpressionChildRecursive(selectExpression, searchExpression, pairs);
return pairs;
}
private static void findExpressionChildRecursive(ExprNode parent, ExprNode searchExpression, List<Pair<ExprNode, ExprNode>> pairs) {
for (ExprNode child : parent.getChildNodes()) {
if (deepEquals(child, searchExpression, false)) {
pairs.add(new Pair<ExprNode, ExprNode>(parent, child));
continue;
}
findExpressionChildRecursive(child, searchExpression, pairs);
}
}
public static void toExpressionStringParameterList(ExprNode[] childNodes, StringWriter buffer) {
String delimiter = "";
for (ExprNode childNode : childNodes) {
buffer.append(delimiter);
buffer.append(ExprNodeUtility.toExpressionStringMinPrecedenceSafe(childNode));
delimiter = ",";
}
}
public static void toExpressionStringWFunctionName(String functionName, ExprNode[] childNodes, StringWriter writer) {
writer.append(functionName);
writer.append("(");
toExpressionStringParameterList(childNodes, writer);
writer.append(')');
}
public static String[] getIdentResolvedPropertyNames(ExprNode[] nodes) {
String[] propertyNames = new String[nodes.length];
for (int i = 0; i < propertyNames.length; i++) {
if (!(nodes[i] instanceof ExprIdentNode)) {
throw new IllegalArgumentException("Expressions are not ident nodes");
}
propertyNames[i] = ((ExprIdentNode) nodes[i]).getResolvedPropertyName();
}
return propertyNames;
}
public static Class[] getExprResultTypes(ExprNode[] groupByNodes) {
Class[] types = new Class[groupByNodes.length];
for (int i = 0; i < types.length; i++) {
types[i] = groupByNodes[i].getExprEvaluator().getType();
}
return types;
}
public static ExprEvaluator makeUnderlyingEvaluator(final int streamNum, final Class resultType, TableMetadata tableMetadata) {
if (tableMetadata != null) {
return new ExprNodeUtilUnderlyingEvaluatorTable(streamNum, resultType, tableMetadata);
}
return new ExprNodeUtilUnderlyingEvaluator(streamNum, resultType);
}
public static boolean hasStreamSelect(List<ExprNode> exprNodes) {
ExprNodeStreamSelectVisitor visitor = new ExprNodeStreamSelectVisitor(false);
for (ExprNode node : exprNodes) {
node.accept(visitor);
if (visitor.hasStreamSelect()) {
return true;
}
}
return false;
}
public static void validateNoSpecialsGroupByExpressions(ExprNode[] groupByNodes) throws ExprValidationException {
ExprNodeSubselectDeclaredDotVisitor visitorSubselects = new ExprNodeSubselectDeclaredDotVisitor();
ExprNodeGroupingVisitorWParent visitorGrouping = new ExprNodeGroupingVisitorWParent();
List<ExprAggregateNode> aggNodesInGroupBy = new ArrayList<ExprAggregateNode>(1);
for (ExprNode groupByNode : groupByNodes) {
// no subselects
groupByNode.accept(visitorSubselects);
if (visitorSubselects.getSubselects().size() > 0) {
throw new ExprValidationException("Subselects not allowed within group-by");
}
// no special grouping-clauses
groupByNode.accept(visitorGrouping);
if (!visitorGrouping.getGroupingIdNodes().isEmpty()) {
throw ExprGroupingIdNode.makeException("grouping_id");
}
if (!visitorGrouping.getGroupingNodes().isEmpty()) {
throw ExprGroupingIdNode.makeException("grouping");
}
// no aggregations allowed
ExprAggregateNodeUtil.getAggregatesBottomUp(groupByNode, aggNodesInGroupBy);
if (!aggNodesInGroupBy.isEmpty()) {
throw new ExprValidationException("Group-by expressions cannot contain aggregate functions");
}
}
}
public static Map<String, ExprNamedParameterNode> getNamedExpressionsHandleDups(List<ExprNode> parameters) throws ExprValidationException {
Map<String, ExprNamedParameterNode> nameds = null;
for (ExprNode node : parameters) {
if (node instanceof ExprNamedParameterNode) {
ExprNamedParameterNode named = (ExprNamedParameterNode) node;
if (nameds == null) {
nameds = new HashMap<String, ExprNamedParameterNode>();
}
String lowerCaseName = named.getParameterName().toLowerCase(Locale.ENGLISH);
if (nameds.containsKey(lowerCaseName)) {
throw new ExprValidationException("Duplicate parameter '" + lowerCaseName + "'");
}
nameds.put(lowerCaseName, named);
}
}
if (nameds == null) {
return Collections.emptyMap();
}
return nameds;
}
public static void validateNamed(Map<String, ExprNamedParameterNode> namedExpressions, String[] namedParameters) throws ExprValidationException {
for (Map.Entry<String, ExprNamedParameterNode> entry : namedExpressions.entrySet()) {
boolean found = false;
for (String named : namedParameters) {
if (named.equals(entry.getKey())) {
found = true;
break;
}
}
if (!found) {
throw new ExprValidationException("Unexpected named parameter '" + entry.getKey() + "', expecting any of the following: " + CollectionUtil.toStringArray(namedParameters));
}
}
}
public static boolean validateNamedExpectType(ExprNamedParameterNode namedParameterNode, Class[] expectedTypes) throws ExprValidationException {
if (namedParameterNode.getChildNodes().length != 1) {
throw getNamedValidationException(namedParameterNode.getParameterName(), expectedTypes);
}
ExprNode childNode = namedParameterNode.getChildNodes()[0];
Class returnType = JavaClassHelper.getBoxedType(childNode.getExprEvaluator().getType());
boolean found = false;
for (Class expectedType : expectedTypes) {
if (expectedType == TimePeriod.class && childNode instanceof ExprTimePeriod) {
found = true;
break;
}
if (returnType == JavaClassHelper.getBoxedType(expectedType)) {
found = true;
break;
}
}
if (found) {
return namedParameterNode.getChildNodes()[0].isConstantResult();
}
throw getNamedValidationException(namedParameterNode.getParameterName(), expectedTypes);
}
private static ExprValidationException getNamedValidationException(String parameterName, Class[] expected) {
String expectedType;
if (expected.length == 1) {
expectedType = "a " + JavaClassHelper.getSimpleNameForClass(expected[0]) + "-typed value";
} else {
StringWriter buf = new StringWriter();
buf.append("any of the following types: ");
String delimiter = "";
for (Class clazz : expected) {
buf.append(delimiter);
buf.append(JavaClassHelper.getSimpleNameForClass(clazz));
delimiter = ",";
}
expectedType = buf.toString();
}
String message = "Failed to validate named parameter '" + parameterName + "', expected a single expression returning " + expectedType;
return new ExprValidationException(message);
}
public static ExprNode[] addExpression(ExprNode[] expressions, ExprNode expression) {
ExprNode[] target = new ExprNode[expressions.length + 1];
System.arraycopy(expressions, 0, target, 0, expressions.length);
target[expressions.length] = expression;
return target;
}
public static QueryGraph validateFilterGetQueryGraphSafe(ExprNode filterExpression, StatementContext statementContext, StreamTypeServiceImpl typeService) {
ExcludePlanHint excludePlanHint = null;
try {
excludePlanHint = ExcludePlanHint.getHint(typeService.getStreamNames(), statementContext);
} catch (ExprValidationException ex) {
log.warn("Failed to consider exclude-plan hint: " + ex.getMessage(), ex);
}
QueryGraph queryGraph = new QueryGraph(1, excludePlanHint, false);
validateFilterWQueryGraphSafe(queryGraph, filterExpression, statementContext, typeService);
return queryGraph;
}
public static void validateFilterWQueryGraphSafe(QueryGraph queryGraph, ExprNode filterExpression, StatementContext statementContext, StreamTypeServiceImpl typeService) {
try {
ExprEvaluatorContextStatement evaluatorContextStmt = new ExprEvaluatorContextStatement(statementContext, false);
ExprValidationContext validationContext = new ExprValidationContext(typeService, statementContext.getEngineImportService(), statementContext.getStatementExtensionServicesContext(), null, statementContext.getTimeProvider(), statementContext.getVariableService(), statementContext.getTableService(), evaluatorContextStmt, statementContext.getEventAdapterService(), statementContext.getStatementName(), statementContext.getStatementId(), statementContext.getAnnotations(), statementContext.getContextDescriptor(), false, false, true, false, null, true);
ExprNode validated = ExprNodeUtility.getValidatedSubtree(ExprNodeOrigin.FILTER, filterExpression, validationContext);
FilterExprAnalyzer.analyze(validated, queryGraph, false);
} catch (Exception ex) {
log.warn("Unexpected exception analyzing filterable expression '" + toExpressionStringMinPrecedenceSafe(filterExpression) + "': " + ex.getMessage(), ex);
}
}
/**
* Encapsulates the parse result parsing a mapped property as a class and method name with args.
*/
public static class MappedPropertyParseResult {
private String className;
private String methodName;
private String argString;
/**
* Returns class name.
*
* @return name of class
*/
public String getClassName() {
return className;
}
/**
* Returns the method name.
*
* @return method name
*/
public String getMethodName() {
return methodName;
}
/**
* Returns the method argument.
*
* @return arg
*/
public String getArgString() {
return argString;
}
/**
* Returns the parse result of the mapped property.
*
* @param className is the class name, or null if there isn't one
* @param methodName is the method name
* @param argString is the argument
*/
public MappedPropertyParseResult(String className, String methodName, String argString) {
this.className = className;
this.methodName = methodName;
this.argString = argString;
}
}
public static void acceptChain(ExprNodeVisitor visitor, List<ExprChainedSpec> chainSpec) {
for (ExprChainedSpec chain : chainSpec) {
for (ExprNode param : chain.getParameters()) {
param.accept(visitor);
}
}
}
public static void acceptChain(ExprNodeVisitorWithParent visitor, List<ExprChainedSpec> chainSpec) {
for (ExprChainedSpec chain : chainSpec) {
for (ExprNode param : chain.getParameters()) {
param.accept(visitor);
}
}
}
public static void acceptChain(ExprNodeVisitorWithParent visitor, List<ExprChainedSpec> chainSpec, ExprNode parent) {
for (ExprChainedSpec chain : chainSpec) {
for (ExprNode param : chain.getParameters()) {
param.acceptChildnodes(visitor, parent);
}
}
}
public static void replaceChildNode(ExprNode parentNode, ExprNode nodeToReplace, ExprNode newNode) {
int index = ExprNodeUtility.findChildNode(parentNode, nodeToReplace);
if (index == -1) {
parentNode.replaceUnlistedChildNode(nodeToReplace, newNode);
} else {
parentNode.setChildNode(index, newNode);
}
}
private static int findChildNode(ExprNode parentNode, ExprNode childNode) {
for (int i = 0; i < parentNode.getChildNodes().length; i++) {
if (parentNode.getChildNodes()[i] == childNode) {
return i;
}
}
return -1;
}
public static void replaceChainChildNode(ExprNode nodeToReplace, ExprNode newNode, List<ExprChainedSpec> chainSpec) {
for (ExprChainedSpec chained : chainSpec) {
int index = chained.getParameters().indexOf(nodeToReplace);
if (index != -1) {
chained.getParameters().set(index, newNode);
}
}
}
public static ExprNodePropOrStreamSet getNonAggregatedProps(EventType[] types, List<ExprNode> exprNodes, ContextPropertyRegistry contextPropertyRegistry) {
// Determine all event properties in the clause
ExprNodePropOrStreamSet nonAggProps = new ExprNodePropOrStreamSet();
ExprNodeIdentifierAndStreamRefVisitor visitor = new ExprNodeIdentifierAndStreamRefVisitor(false);
for (ExprNode node : exprNodes) {
visitor.reset();
node.accept(visitor);
addNonAggregatedProps(nonAggProps, visitor.getRefs(), types, contextPropertyRegistry);
}
return nonAggProps;
}
private static void addNonAggregatedProps(ExprNodePropOrStreamSet nonAggProps, List<ExprNodePropOrStreamDesc> refs, EventType[] types, ContextPropertyRegistry contextPropertyRegistry) {
for (ExprNodePropOrStreamDesc pair : refs) {
if (pair instanceof ExprNodePropOrStreamPropDesc) {
ExprNodePropOrStreamPropDesc propDesc = (ExprNodePropOrStreamPropDesc) pair;
EventType originType = types.length > pair.getStreamNum() ? types[pair.getStreamNum()] : null;
if (originType == null || contextPropertyRegistry == null || !contextPropertyRegistry.isPartitionProperty(originType, propDesc.getPropertyName())) {
nonAggProps.add(pair);
}
} else {
nonAggProps.add(pair);
}
}
}
public static void addNonAggregatedProps(ExprNode exprNode, ExprNodePropOrStreamSet set, EventType[] types, ContextPropertyRegistry contextPropertyRegistry) {
ExprNodeIdentifierAndStreamRefVisitor visitor = new ExprNodeIdentifierAndStreamRefVisitor(false);
exprNode.accept(visitor);
addNonAggregatedProps(set, visitor.getRefs(), types, contextPropertyRegistry);
}
public static ExprNodePropOrStreamSet getAggregatedProperties(List<ExprAggregateNode> aggregateNodes) {
// Get a list of properties being aggregated in the clause.
ExprNodePropOrStreamSet propertiesAggregated = new ExprNodePropOrStreamSet();
ExprNodeIdentifierAndStreamRefVisitor visitor = new ExprNodeIdentifierAndStreamRefVisitor(true);
for (ExprNode selectAggExprNode : aggregateNodes) {
visitor.reset();
selectAggExprNode.accept(visitor);
List<ExprNodePropOrStreamDesc> properties = visitor.getRefs();
propertiesAggregated.addAll(properties);
}
return propertiesAggregated;
}
public static ExprEvaluator[] getEvaluators(ExprNode[] exprNodes) {
if (exprNodes == null) {
return null;
}
ExprEvaluator[] eval = new ExprEvaluator[exprNodes.length];
for (int i = 0; i < exprNodes.length; i++) {
ExprNode node = exprNodes[i];
if (node != null) {
eval[i] = node.getExprEvaluator();
}
}
return eval;
}
public static ExprEvaluator[] getEvaluators(List<ExprNode> childNodes) {
ExprEvaluator[] eval = new ExprEvaluator[childNodes.size()];
for (int i = 0; i < childNodes.size(); i++) {
eval[i] = childNodes.get(i).getExprEvaluator();
}
return eval;
}
public static Set<Integer> getIdentStreamNumbers(ExprNode child) {
Set<Integer> streams = new HashSet<Integer>();
ExprNodeIdentifierCollectVisitor visitor = new ExprNodeIdentifierCollectVisitor();
child.accept(visitor);
for (ExprIdentNode node : visitor.getExprProperties()) {
streams.add(node.getStreamId());
}
return streams;
}
/**
* Returns true if all properties within the expression are witin data window'd streams.
*
* @param child expression to interrogate
* @param streamTypeService streams
* @param unidirectionalJoin indicator unidirection join
* @return indicator
*/
public static boolean hasRemoveStreamForAggregations(ExprNode child, StreamTypeService streamTypeService, boolean unidirectionalJoin) {
// Determine whether all streams are istream-only or irstream
boolean[] isIStreamOnly = streamTypeService.getIStreamOnly();
boolean isAllIStream = true; // all true?
boolean isAllIRStream = true; // all false?
for (boolean anIsIStreamOnly : isIStreamOnly) {
if (!anIsIStreamOnly) {
isAllIStream = false;
} else {
isAllIRStream = false;
}
}
// determine if a data-window applies to this max function
boolean hasDataWindows = true;
if (isAllIStream) {
hasDataWindows = false;
} else if (!isAllIRStream) {
if (streamTypeService.getEventTypes().length > 1) {
if (unidirectionalJoin) {
return false;
}
// In a join we assume that a data window is present or implicit via unidirectional
} else {
hasDataWindows = false;
// get all aggregated properties to determine if any is from a windowed stream
ExprNodeIdentifierCollectVisitor visitor = new ExprNodeIdentifierCollectVisitor();
child.accept(visitor);
for (ExprIdentNode node : visitor.getExprProperties()) {
if (!isIStreamOnly[node.getStreamId()]) {
hasDataWindows = true;
break;
}
}
}
}
return hasDataWindows;
}
/**
* Apply a filter expression.
*
* @param filter expression
* @param streamZeroEvent the event that represents stream zero
* @param streamOneEvents all events thate are stream one events
* @param exprEvaluatorContext context for expression evaluation
* @return filtered stream one events
*/
public static EventBean[] applyFilterExpression(ExprEvaluator filter, EventBean streamZeroEvent, EventBean[] streamOneEvents, ExprEvaluatorContext exprEvaluatorContext) {
EventBean[] eventsPerStream = new EventBean[2];
eventsPerStream[0] = streamZeroEvent;
EventBean[] filtered = new EventBean[streamOneEvents.length];
int countPass = 0;
for (EventBean eventBean : streamOneEvents) {
eventsPerStream[1] = eventBean;
Boolean result = (Boolean) filter.evaluate(eventsPerStream, true, exprEvaluatorContext);
if ((result != null) && result) {
filtered[countPass] = eventBean;
countPass++;
}
}
if (countPass == streamOneEvents.length) {
return streamOneEvents;
}
return EventBeanUtility.resizeArray(filtered, countPass);
}
/**
* Apply a filter expression returning a pass indicator.
*
* @param filter to apply
* @param eventsPerStream events per stream
* @param exprEvaluatorContext context for expression evaluation
* @return pass indicator
*/
public static boolean applyFilterExpression(ExprEvaluator filter, EventBean[] eventsPerStream, ExprEvaluatorContext exprEvaluatorContext) {
Boolean result = (Boolean) filter.evaluate(eventsPerStream, true, exprEvaluatorContext);
return (result != null) && result;
}
/**
* Compare two expression nodes and their children in exact child-node sequence,
* returning true if the 2 expression nodes trees are equals, or false if they are not equals.
* <p>
* Recursive call since it uses this method to compare child nodes in the same exact sequence.
* Nodes are compared using the equalsNode method.
*
* @param nodeOne - first expression top node of the tree to compare
* @param nodeTwo - second expression top node of the tree to compare
* @param ignoreStreamPrefix when the equals-comparison can ignore prefix of event properties
* @return false if this or all child nodes are not equal, true if equal
*/
public static boolean deepEquals(ExprNode nodeOne, ExprNode nodeTwo, boolean ignoreStreamPrefix) {
if (nodeOne.getChildNodes().length != nodeTwo.getChildNodes().length) {
return false;
}
if (!nodeOne.equalsNode(nodeTwo, ignoreStreamPrefix)) {
return false;
}
for (int i = 0; i < nodeOne.getChildNodes().length; i++) {
ExprNode childNodeOne = nodeOne.getChildNodes()[i];
ExprNode childNodeTwo = nodeTwo.getChildNodes()[i];
if (!ExprNodeUtility.deepEquals(childNodeOne, childNodeTwo, ignoreStreamPrefix)) {
return false;
}
}
return true;
}
/**
* Compares two expression nodes via deep comparison, considering all
* child nodes of either side.
*
* @param one array of expressions
* @param two array of expressions
* @param ignoreStreamPrefix indicator whether we ignore stream prefixes and instead use resolved property name
* @return true if the expressions are equal, false if not
*/
public static boolean deepEquals(ExprNode[] one, ExprNode[] two, boolean ignoreStreamPrefix) {
if (one.length != two.length) {
return false;
}
for (int i = 0; i < one.length; i++) {
if (!ExprNodeUtility.deepEquals(one[i], two[i], ignoreStreamPrefix)) {
return false;
}
}
return true;
}
public static boolean deepEquals(List<ExprNode> one, List<ExprNode> two) {
if (one.size() != two.size()) {
return false;
}
for (int i = 0; i < one.size(); i++) {
if (!ExprNodeUtility.deepEquals(one.get(i), two.get(i), false)) {
return false;
}
}
return true;
}
/**
* Check if the expression is minimal: does not have a subselect, aggregation and does not need view resources
*
* @param expression to inspect
* @return null if minimal, otherwise name of offending sub-expression
*/
public static String isMinimalExpression(ExprNode expression) {
ExprNodeSubselectDeclaredDotVisitor subselectVisitor = new ExprNodeSubselectDeclaredDotVisitor();
expression.accept(subselectVisitor);
if (subselectVisitor.getSubselects().size() > 0) {
return "a subselect";
}
ExprNodeViewResourceVisitor viewResourceVisitor = new ExprNodeViewResourceVisitor();
expression.accept(viewResourceVisitor);
if (viewResourceVisitor.getExprNodes().size() > 0) {
return "a function that requires view resources (prior, prev)";
}
List<ExprAggregateNode> aggregateNodes = new LinkedList<ExprAggregateNode>();
ExprAggregateNodeUtil.getAggregatesBottomUp(expression, aggregateNodes);
if (!aggregateNodes.isEmpty()) {
return "an aggregation function";
}
return null;
}
public static void toExpressionString(List<ExprChainedSpec> chainSpec, StringWriter buffer, boolean prefixDot, String functionName) {
String delimiterOuter = "";
if (prefixDot) {
delimiterOuter = ".";
}
boolean isFirst = true;
for (ExprChainedSpec element : chainSpec) {
buffer.append(delimiterOuter);
if (functionName != null) {
buffer.append(functionName);
} else {
buffer.append(element.getName());
}
// the first item without dot-prefix and empty parameters should not be appended with parenthesis
if (!isFirst || prefixDot || !element.getParameters().isEmpty()) {
toExpressionStringIncludeParen(element.getParameters(), buffer);
}
delimiterOuter = ".";
isFirst = false;
}
}
public static void toExpressionStringParameterList(List<ExprNode> parameters, StringWriter buffer) {
String delimiter = "";
for (ExprNode param : parameters) {
buffer.append(delimiter);
delimiter = ",";
buffer.append(ExprNodeUtility.toExpressionStringMinPrecedenceSafe(param));
}
}
public static void toExpressionString(ExprNode node, StringWriter buffer) {
node.toEPL(buffer, ExprPrecedenceEnum.MINIMUM);
}
public static void toExpressionStringIncludeParen(List<ExprNode> parameters, StringWriter buffer) {
buffer.append("(");
toExpressionStringParameterList(parameters, buffer);
buffer.append(")");
}
public static void validate(ExprNodeOrigin origin, List<ExprChainedSpec> chainSpec, ExprValidationContext validationContext) throws ExprValidationException {
// validate all parameters
for (ExprChainedSpec chainElement : chainSpec) {
List<ExprNode> validated = new ArrayList<ExprNode>();
for (ExprNode expr : chainElement.getParameters()) {
validated.add(ExprNodeUtility.getValidatedSubtree(origin, expr, validationContext));
if (expr instanceof ExprNamedParameterNode) {
throw new ExprValidationException("Named parameters are not allowed");
}
}
chainElement.setParameters(validated);
}
}
public static List<ExprNode> collectChainParameters(List<ExprChainedSpec> chainSpec) {
List<ExprNode> result = new ArrayList<ExprNode>();
for (ExprChainedSpec chainElement : chainSpec) {
result.addAll(chainElement.getParameters());
}
return result;
}
public static void toExpressionStringParams(StringWriter writer, ExprNode[] params) {
writer.append('(');
String delimiter = "";
for (ExprNode childNode : params) {
writer.append(delimiter);
delimiter = ",";
writer.append(ExprNodeUtility.toExpressionStringMinPrecedenceSafe(childNode));
}
writer.append(')');
}
public static String printEvaluators(ExprEvaluator[] evaluators) {
StringWriter writer = new StringWriter();
String delimiter = "";
for (ExprEvaluator evaluator : evaluators) {
writer.append(delimiter);
writer.append(evaluator.getClass().getSimpleName());
delimiter = ", ";
}
return writer.toString();
}
public static ExprEvaluator[] crontabScheduleValidate(ExprNodeOrigin origin, List<ExprNode> scheduleSpecExpressionList, StatementContext context, boolean allowBindingConsumption)
throws ExprValidationException {
// Validate the expressions
ExprEvaluator[] expressions = new ExprEvaluator[scheduleSpecExpressionList.size()];
int count = 0;
ExprEvaluatorContextStatement evaluatorContextStmt = new ExprEvaluatorContextStatement(context, false);
for (ExprNode parameters : scheduleSpecExpressionList) {
ExprValidationContext validationContext = new ExprValidationContext(new StreamTypeServiceImpl(context.getEngineURI(), false), context.getEngineImportService(), context.getStatementExtensionServicesContext(), null, context.getSchedulingService(), context.getVariableService(), context.getTableService(), evaluatorContextStmt, context.getEventAdapterService(), context.getStatementName(), context.getStatementId(), context.getAnnotations(), context.getContextDescriptor(), false, false, allowBindingConsumption, false, null, false);
ExprNode node = ExprNodeUtility.getValidatedSubtree(origin, parameters, validationContext);
expressions[count++] = node.getExprEvaluator();
}
if (expressions.length <= 4 || expressions.length >= 8) {
throw new ExprValidationException("Invalid schedule specification: " + ScheduleSpecUtil.getExpressionCountException(expressions.length));
}
return expressions;
}
public static ScheduleSpec crontabScheduleBuild(ExprEvaluator[] scheduleSpecEvaluators, ExprEvaluatorContext context) {
// Build a schedule
try {
Object[] scheduleSpecParameterList = evaluateExpressions(scheduleSpecEvaluators, context);
return ScheduleSpecUtil.computeValues(scheduleSpecParameterList);
} catch (ScheduleParameterException e) {
throw new EPException("Invalid schedule specification: " + e.getMessage(), e);
}
}
public static Object[] evaluateExpressions(ExprEvaluator[] parameters, ExprEvaluatorContext exprEvaluatorContext) {
Object[] results = new Object[parameters.length];
int count = 0;
for (ExprEvaluator expr : parameters) {
try {
results[count] = expr.evaluate(null, true, exprEvaluatorContext);
count++;
} catch (RuntimeException ex) {
String message = "Failed expression evaluation in crontab timer-at for parameter " + count + ": " + ex.getMessage();
log.error(message, ex);
throw new IllegalArgumentException(message);
}
}
return results;
}
public static ExprNode[] toArray(Collection<ExprNode> expressions) {
if (expressions.isEmpty()) {
return EMPTY_EXPR_ARRAY;
}
return expressions.toArray(new ExprNode[expressions.size()]);
}
public static ExprDeclaredNode[] toArray(List<ExprDeclaredNode> declaredNodes) {
if (declaredNodes.isEmpty()) {
return EMPTY_DECLARED_ARR;
}
return declaredNodes.toArray(new ExprDeclaredNode[declaredNodes.size()]);
}
public static ExprNodePropOrStreamSet getGroupByPropertiesValidateHasOne(ExprNode[] groupByNodes)
throws ExprValidationException {
// Get the set of properties refered to by all group-by expression nodes.
ExprNodePropOrStreamSet propertiesGroupBy = new ExprNodePropOrStreamSet();
ExprNodeIdentifierAndStreamRefVisitor visitor = new ExprNodeIdentifierAndStreamRefVisitor(true);
for (ExprNode groupByNode : groupByNodes) {
visitor.reset();
groupByNode.accept(visitor);
List<ExprNodePropOrStreamDesc> propertiesNode = visitor.getRefs();
propertiesGroupBy.addAll(propertiesNode);
// For each group-by expression node, require at least one property.
if (propertiesNode.isEmpty()) {
throw new ExprValidationException("Group-by expressions must refer to property names");
}
}
return propertiesGroupBy;
}
private static ExprEvaluator[] makeVarargArrayEval(Method method, final ExprEvaluator[] childEvals) {
ExprEvaluator[] evals = new ExprEvaluator[method.getParameterTypes().length];
Class varargClass = method.getParameterTypes()[method.getParameterTypes().length - 1].getComponentType();
Class varargClassBoxed = JavaClassHelper.getBoxedType(varargClass);
if (method.getParameterTypes().length > 1) {
System.arraycopy(childEvals, 0, evals, 0, evals.length - 1);
}
final int varargArrayLength = childEvals.length - method.getParameterTypes().length + 1;
// handle passing array along
if (varargArrayLength == 1) {
ExprEvaluator last = childEvals[method.getParameterTypes().length - 1];
Class lastReturns = last.getType();
if (lastReturns != null && lastReturns.isArray()) {
evals[method.getParameterTypes().length - 1] = last;
return evals;
}
}
// handle parameter conversion to vararg parameter
ExprEvaluator[] varargEvals = new ExprEvaluator[varargArrayLength];
SimpleNumberCoercer[] coercers = new SimpleNumberCoercer[varargEvals.length];
boolean needCoercion = false;
for (int i = 0; i < varargArrayLength; i++) {
int childEvalIndex = i + method.getParameterTypes().length - 1;
Class resultType = childEvals[childEvalIndex].getType();
varargEvals[i] = childEvals[childEvalIndex];
if (JavaClassHelper.isSubclassOrImplementsInterface(resultType, varargClass)) {
// no need to coerce
continue;
}
if (JavaClassHelper.getBoxedType(resultType) != varargClassBoxed) {
needCoercion = true;
coercers[i] = SimpleNumberCoercerFactory.getCoercer(resultType, varargClassBoxed);
}
}
ExprEvaluator varargEval;
if (!needCoercion) {
varargEval = new VarargOnlyArrayEvalNoCoerce(varargEvals, varargClass);
} else {
varargEval = new VarargOnlyArrayEvalWithCoerce(varargEvals, varargClass, coercers);
}
evals[method.getParameterTypes().length - 1] = varargEval;
return evals;
}
private static class VarargOnlyArrayEvalNoCoerce implements ExprEvaluator {
private final ExprEvaluator[] evals;
private final Class varargClass;
public VarargOnlyArrayEvalNoCoerce(ExprEvaluator[] evals, Class varargClass) {
this.evals = evals;
this.varargClass = varargClass;
}
public Object evaluate(EventBean[] eventsPerStream, boolean isNewData, ExprEvaluatorContext context) {
Object array = Array.newInstance(varargClass, evals.length);
for (int i = 0; i < evals.length; i++) {
Object value = evals[i].evaluate(eventsPerStream, isNewData, context);
Array.set(array, i, value);
}
return array;
}
public Class getType() {
return JavaClassHelper.getArrayType(varargClass);
}
}
private static class VarargOnlyArrayEvalWithCoerce implements ExprEvaluator {
private final ExprEvaluator[] evals;
private final Class varargClass;
private final SimpleNumberCoercer[] coercers;
public VarargOnlyArrayEvalWithCoerce(ExprEvaluator[] evals, Class varargClass, SimpleNumberCoercer[] coercers) {
this.evals = evals;
this.varargClass = varargClass;
this.coercers = coercers;
}
public Object evaluate(EventBean[] eventsPerStream, boolean isNewData, ExprEvaluatorContext context) {
Object array = Array.newInstance(varargClass, evals.length);
for (int i = 0; i < evals.length; i++) {
Object value = evals[i].evaluate(eventsPerStream, isNewData, context);
if (coercers[i] != null) {
value = coercers[i].coerceBoxed((Number) value);
}
Array.set(array, i, value);
}
return array;
}
public Class getType() {
return JavaClassHelper.getArrayType(varargClass);
}
}
}