Java tutorial
/************************************************************************************** * 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.client.EventType; import com.espertech.esper.client.hook.AggregationFunctionFactory; import com.espertech.esper.client.hook.EPLMethodInvocationContext; 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.epl.agg.service.AggregationSupport; import com.espertech.esper.epl.core.*; import com.espertech.esper.epl.declexpr.ExprDeclaredNode; import com.espertech.esper.epl.enummethod.dot.ExprDeclaredOrLambdaNode; import com.espertech.esper.epl.enummethod.dot.ExprLambdaGoesNode; import com.espertech.esper.epl.spec.OnTriggerSetAssignment; 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 net.sf.cglib.reflect.FastClass; import net.sf.cglib.reflect.FastMethod; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import java.io.StringWriter; import java.lang.reflect.Method; import java.util.*; public class ExprNodeUtility { public static final ExprNode[] EMPTY_EXPR_ARRAY = new ExprNode[0]; public static final ExprDeclaredNode[] EMPTY_DECLARED_ARR = new ExprDeclaredNode[0]; 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) { 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)) { return null; } ExprVariableNode variableNode = (ExprVariableNode) equals.getChildNodes()[0]; return new Pair<String, ExprNode>(variableNode.getVariableNameWithSubProp(), equals.getChildNodes()[1]); } 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(Iterable<EventBean> iterable, ExprEvaluator filterExpression, ExprEvaluatorContext exprEvaluatorContext, Collection<EventBean> eventsInWindow) { EventBean[] events = new EventBean[1]; for (EventBean theEvent : iterable) { events[0] = theEvent; Object result = filterExpression.evaluate(events, true, exprEvaluatorContext); if ((result == null) || (!((Boolean) result))) { continue; } eventsInWindow.add(events[0]); } } public static ExprAndNode connectExpressionsByLogicalAnd(List<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; } /** * 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. * @throws com.espertech.esper.epl.expression.ExprValidationException when the validation fails * @return the root node of the validated subtree, possibly * different than the root node of the unvalidated subtree */ public static ExprNode getValidatedSubtree(ExprNode exprNode, ExprValidationContext validationContext) throws ExprValidationException { if (exprNode instanceof ExprLambdaGoesNode) { return exprNode; } return getValidatedSubtreeInternal(exprNode, validationContext, true); } 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.validate(validationContext); } 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.getMethodResolutionService()); 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 ExprDotNode(chain, validationContext.getMethodResolutionService().isDuckType(), validationContext.getMethodResolutionService().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.getMethodResolutionService() .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.getMethodResolutionService() .resolveAggregationFactory(parse.getMethodName()); ExprNode result = new ExprPlugInAggFunctionFactoryNode(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); } // There is no class name, try an aggregation function (AggregationSupport version, deprecated) try { AggregationSupport aggregation = validationContext.getMethodResolutionService() .resolveAggregation(parse.getMethodName()); ExprNode result = new ExprPlugInAggFunctionNode(false, aggregation, 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); } // absolutly cannot be resolved throw propertyException; } private static ExprConstantNode resolveIdentAsEnumConst(String constant, MethodResolutionService methodResolutionService) throws ExprValidationException { Object enumValue = JavaClassHelper.resolveIdentAsEnumConst(constant, methodResolutionService, null); 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 */ protected 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, MethodResolutionService methodResolutionService, EventAdapterService eventAdapterService, String statementId, boolean allowWildcard, final EventType wildcardType, ExprNodeUtilResolveExceptionHandler exceptionHandler, String functionName) 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 (childNode instanceof ExprLambdaGoesNode) { throw new ExprValidationException( "Unexpected lambda-expression encountered as parameter to UDF or static method '" + methodName + "'"); } if (childNode instanceof ExprNumberSetWildcardMarker) { 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; childEvals[count] = childNode.getExprEvaluator(); childEvalsEventBeanReturnTypes[count] = new ExprNodeUtilExprEvalStreamNumEvent(und.getStreamId()); 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); 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 = methodResolutionService.resolveMethod(optionalClass, methodName, paramTypes, allowEventBeanType, allowEventBeanCollType); } else { method = methodResolutionService.resolveMethod(className, methodName, paramTypes, allowEventBeanType, allowEventBeanCollType); } FastClass declaringClass = FastClass.create(Thread.currentThread().getContextClassLoader(), 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.getParameterTypes().length > 0 && method.getParameterTypes()[method.getParameterTypes().length - 1] == EPLMethodInvocationContext.class) { childEvals = (ExprEvaluator[]) CollectionUtil.arrayExpandAddSingle(childEvals, new ExprNodeUtilExprEvalMethodContext(functionName)); } return new ExprNodeUtilMethodDesc(allConstants, paramTypes, childEvals, method, staticMethod); } public static void validatePlainExpression(String expressionTextualName, ExprNode expression) throws ExprValidationException { ExprNodeSummaryVisitor summaryVisitor = new ExprNodeSummaryVisitor(); expression.accept(summaryVisitor); if (summaryVisitor.isHasAggregation() || summaryVisitor.isHasSubselect() || summaryVisitor.isHasStreamSelect() || summaryVisitor.isHasPreviousPrior()) { throw new ExprValidationException("Invalid expression '" + expressionTextualName + "': Aggregation, sub-select, previous or prior functions are not supported in this context"); } } public static ExprNode validateSimpleGetSubtree(ExprNode expression, StatementContext statementContext, EventType optionalEventType) throws ExprValidationException { ExprNodeUtility.validatePlainExpression(expression.toExpressionString(), 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.getMethodResolutionService(), null, statementContext.getSchedulingService(), statementContext.getVariableService(), new ExprEvaluatorContextStatement(statementContext), statementContext.getEventAdapterService(), statementContext.getStatementName(), statementContext.getStatementId(), statementContext.getAnnotations(), statementContext.getContextDescriptor()); return ExprNodeUtility.getValidatedSubtree(expression, validationContext); } 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; } /** * Encapsulates the parse result parsing a mapped property as a class and method name with args. */ protected 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 final 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 Set<Pair<Integer, String>> getNonAggregatedProps(EventType[] types, List<ExprNode> exprNodes, ContextPropertyRegistry contextPropertyRegistry) { // Determine all event properties in the clause Set<Pair<Integer, String>> nonAggProps = new HashSet<Pair<Integer, String>>(); for (ExprNode node : exprNodes) { ExprNodeIdentifierVisitor visitor = new ExprNodeIdentifierVisitor(false); node.accept(visitor); List<Pair<Integer, String>> propertiesNodes = visitor.getExprProperties(); for (Pair<Integer, String> pair : propertiesNodes) { EventType originType = types.length > pair.getFirst() ? types[pair.getFirst()] : null; if (originType == null || contextPropertyRegistry == null || !contextPropertyRegistry.isPartitionProperty(originType, pair.getSecond())) { nonAggProps.add(pair); } } } return nonAggProps; } public static void addNonAggregatedProps(ExprNode exprNode, Set<Pair<Integer, String>> set) { ExprNodeIdentifierVisitor visitor = new ExprNodeIdentifierVisitor(false); exprNode.accept(visitor); set.addAll(visitor.getExprProperties()); } public static Set<Pair<Integer, String>> getAggregatedProperties(List<ExprAggregateNode> aggregateNodes) { // Get a list of properties being aggregated in the clause. Set<Pair<Integer, String>> propertiesAggregated = new HashSet<Pair<Integer, String>>(); for (ExprNode selectAggExprNode : aggregateNodes) { ExprNodeIdentifierVisitor visitor = new ExprNodeIdentifierVisitor(true); selectAggExprNode.accept(visitor); List<Pair<Integer, String>> properties = visitor.getExprProperties(); 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 * @return indicator */ public static boolean hasRemoveStream(ExprNode child, StreamTypeService streamTypeService) { // 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) { // 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 * @return false if this or all child nodes are not equal, true if equal */ public static boolean deepEquals(ExprNode nodeOne, ExprNode nodeTwo) { if (nodeOne.getChildNodes().length != nodeTwo.getChildNodes().length) { return false; } if (!nodeOne.equalsNode(nodeTwo)) { 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)) { 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 * @return true if the expressions are equal, false if not */ public static boolean deepEquals(ExprNode[] one, ExprNode[] two) { if (one.length != two.length) { return false; } for (int i = 0; i < one.length; i++) { if (!ExprNodeUtility.deepEquals(one[i], two[i])) { 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))) { 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; } protected static void toExpressionString(List<ExprChainedSpec> chainSpec, StringBuilder 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 toExpressionString(List<ExprNode> parameters, StringBuilder buffer) { String delimiter = ""; for (ExprNode param : parameters) { buffer.append(delimiter); delimiter = ", "; buffer.append(param.toExpressionString()); } } public static void toExpressionStringIncludeParen(List<ExprNode> parameters, StringBuilder buffer) { buffer.append("("); toExpressionString(parameters, buffer); buffer.append(")"); } public static void validate(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(expr, validationContext)); } 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, boolean isWildcard, String streamWildcard, boolean firstParamOnly) { writer.append('('); if (isWildcard) { writer.append('*'); } else if (streamWildcard != null) { writer.append(streamWildcard); writer.append(".*"); } else { String delimiter = ""; for (ExprNode childNode : params) { writer.append(delimiter); delimiter = ","; writer.append(childNode.toExpressionString()); if (firstParamOnly) { break; } } } writer.append(')'); } public static String printEvaluators(ExprEvaluator[] evaluators) { StringWriter writer = new StringWriter(); String delimiter = ""; for (int i = 0; i < evaluators.length; i++) { writer.append(delimiter); writer.append(evaluators[i].getClass().getSimpleName()); delimiter = ", "; } return writer.toString(); } public static ScheduleSpec toCrontabSchedule(List<ExprNode> scheduleSpecExpressionList, StatementContext context) throws ExprValidationException { // Validate the expressions ExprEvaluator[] expressions = new ExprEvaluator[scheduleSpecExpressionList.size()]; int count = 0; ExprEvaluatorContextStatement evaluatorContextStmt = new ExprEvaluatorContextStatement(context); for (ExprNode parameters : scheduleSpecExpressionList) { ExprValidationContext validationContext = new ExprValidationContext( new StreamTypeServiceImpl(context.getEngineURI(), false), context.getMethodResolutionService(), null, context.getSchedulingService(), context.getVariableService(), evaluatorContextStmt, context.getEventAdapterService(), context.getStatementName(), context.getStatementId(), context.getAnnotations(), context.getContextDescriptor()); ExprNode node = ExprNodeUtility.getValidatedSubtree(parameters, validationContext); expressions[count++] = node.getExprEvaluator(); } // Build a schedule try { Object[] scheduleSpecParameterList = evaluateExpressions(expressions, evaluatorContextStmt); return ScheduleSpecUtil.computeValues(scheduleSpecParameterList); } catch (ScheduleParameterException e) { throw new ExprValidationException("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()]); } private static final Log log = LogFactory.getLog(ExprNodeUtility.class); }