/* *************************************************************************************** * 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.spec; import com.espertech.esper.client.EPException; import com.espertech.esper.client.EventType; import com.espertech.esper.client.annotation.Audit; import com.espertech.esper.client.annotation.AuditEnum; import com.espertech.esper.collection.Pair; import com.espertech.esper.core.service.ExprEvaluatorContextStatement; import com.espertech.esper.core.service.StatementContext; import com.espertech.esper.epl.core.StreamTypeService; import com.espertech.esper.epl.core.StreamTypeServiceImpl; import com.espertech.esper.epl.expression.core.*; import com.espertech.esper.epl.expression.time.ExprTimePeriod; import com.espertech.esper.epl.expression.time.ExprTimePeriodEvalDeltaConst; import com.espertech.esper.epl.expression.time.ExprTimePeriodEvalDeltaConstGivenDelta; import com.espertech.esper.epl.expression.visitor.ExprNodeSummaryVisitor; import com.espertech.esper.epl.property.PropertyEvaluator; import com.espertech.esper.epl.property.PropertyEvaluatorFactory; import com.espertech.esper.event.EventAdapterService; import com.espertech.esper.event.EventTypeSPI; import com.espertech.esper.filter.FilterSpecCompiled; import com.espertech.esper.filter.FilterSpecCompiler; import com.espertech.esper.pattern.*; import com.espertech.esper.pattern.guard.GuardFactory; import com.espertech.esper.pattern.guard.GuardParameterException; import com.espertech.esper.pattern.observer.ObserverFactory; import com.espertech.esper.pattern.observer.ObserverParameterException; import com.espertech.esper.util.JavaClassHelper; import com.espertech.esper.util.UuidGenerator; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.StringWriter; import java.util.*; /** * Pattern specification in unvalidated, unoptimized form. */ public class PatternStreamSpecRaw extends StreamSpecBase implements StreamSpecRaw { private final EvalFactoryNode evalFactoryNode; private final boolean suppressSameEventMatches; private final boolean discardPartialsOnMatch; private static final Logger log = LoggerFactory.getLogger(PatternStreamSpecRaw.class); private static final long serialVersionUID = 6393401926404401433L; public PatternStreamSpecRaw(EvalFactoryNode evalFactoryNode, ViewSpec[] viewSpecs, String optionalStreamName, StreamSpecOptions streamSpecOptions, boolean suppressSameEventMatches, boolean discardPartialsOnMatch) { super(optionalStreamName, viewSpecs, streamSpecOptions); this.evalFactoryNode = evalFactoryNode; this.suppressSameEventMatches = suppressSameEventMatches; this.discardPartialsOnMatch = discardPartialsOnMatch; } /** * Returns the pattern expression evaluation node for the top pattern operator. * * @return parent pattern expression node */ public EvalFactoryNode getEvalFactoryNode() { return evalFactoryNode; } public PatternStreamSpecCompiled compile(StatementContext context, Set<String> eventTypeReferences, boolean isInsertInto, Collection<Integer> assignedTypeNumberStack, boolean isJoin, boolean isContextDeclaration, boolean isOnTrigger, String optionalStreamName) throws ExprValidationException { return compileInternal(context, eventTypeReferences, isInsertInto, assignedTypeNumberStack, null, null, isJoin, isContextDeclaration, isOnTrigger); } public PatternStreamSpecCompiled compile(StatementContext context, Set<String> eventTypeReferences, boolean isInsertInto, Collection<Integer> assignedTypeNumberStack, MatchEventSpec priorTags, Set<String> priorAllTags, boolean isJoin, boolean isContextDeclaration, boolean isOnTrigger) throws ExprValidationException { return compileInternal(context, eventTypeReferences, isInsertInto, assignedTypeNumberStack, priorTags, priorAllTags, isJoin, isContextDeclaration, isOnTrigger); } private PatternStreamSpecCompiled compileInternal(StatementContext context, Set<String> eventTypeReferences, boolean isInsertInto, Collection<Integer> assignedTypeNumberStack, MatchEventSpec tags, Set<String> priorAllTags, boolean isJoin, boolean isContextDeclaration, boolean isOnTrigger) throws ExprValidationException { // validate if ((suppressSameEventMatches || discardPartialsOnMatch) && (isJoin || isContextDeclaration || isOnTrigger)) { throw new ExprValidationException("Discard-partials and suppress-matches is not supported in a joins, context declaration and on-action"); } if (tags == null) { tags = new MatchEventSpec(); } Deque<Integer> subexpressionIdStack = new ArrayDeque<Integer>(assignedTypeNumberStack); ExprEvaluatorContext evaluatorContextStmt = new ExprEvaluatorContextStatement(context, false); Stack<EvalFactoryNode> nodeStack = new Stack<EvalFactoryNode>(); // detemine ordered tags Set<EvalFactoryNode> filterFactoryNodes = EvalNodeUtil.recursiveGetChildNodes(evalFactoryNode, FilterForFilterFactoryNodes.INSTANCE); LinkedHashSet<String> allTagNamesOrdered = new LinkedHashSet<String>(); if (priorAllTags != null) { allTagNamesOrdered.addAll(priorAllTags); } for (EvalFactoryNode filterNode : filterFactoryNodes) { EvalFilterFactoryNode factory = (EvalFilterFactoryNode) filterNode; int tagNumber; if (factory.getEventAsName() != null) { if (!allTagNamesOrdered.contains(factory.getEventAsName())) { allTagNamesOrdered.add(factory.getEventAsName()); tagNumber = allTagNamesOrdered.size() - 1; } else { tagNumber = findTagNumber(factory.getEventAsName(), allTagNamesOrdered); } factory.setEventAsTagNumber(tagNumber); } } recursiveCompile(evalFactoryNode, context, evaluatorContextStmt, eventTypeReferences, isInsertInto, tags, subexpressionIdStack, nodeStack, allTagNamesOrdered); Audit auditPattern = AuditEnum.PATTERN.getAudit(context.getAnnotations()); Audit auditPatternInstance = AuditEnum.PATTERNINSTANCES.getAudit(context.getAnnotations()); EvalFactoryNode compiledEvalFactoryNode = evalFactoryNode; if (context.getPatternNodeFactory().isAuditSupported() && (auditPattern != null || auditPatternInstance != null)) { EvalAuditInstanceCount instanceCount = new EvalAuditInstanceCount(); compiledEvalFactoryNode = recursiveAddAuditNode(context.getPatternNodeFactory(), null, auditPattern != null, auditPatternInstance != null, evalFactoryNode, instanceCount); } return new PatternStreamSpecCompiled(compiledEvalFactoryNode, tags.getTaggedEventTypes(), tags.getArrayEventTypes(), allTagNamesOrdered, this.getViewSpecs(), this.getOptionalStreamName(), this.getOptions(), suppressSameEventMatches, discardPartialsOnMatch); } private static void recursiveCompile(EvalFactoryNode evalNode, StatementContext context, ExprEvaluatorContext evaluatorContext, Set<String> eventTypeReferences, boolean isInsertInto, MatchEventSpec tags, Deque<Integer> subexpressionIdStack, Stack<EvalFactoryNode> parentNodeStack, LinkedHashSet<String> allTagNamesOrdered) throws ExprValidationException { int counter = 0; parentNodeStack.push(evalNode); for (EvalFactoryNode child : evalNode.getChildNodes()) { subexpressionIdStack.addLast(counter++); recursiveCompile(child, context, evaluatorContext, eventTypeReferences, isInsertInto, tags, subexpressionIdStack, parentNodeStack, allTagNamesOrdered); subexpressionIdStack.removeLast(); } parentNodeStack.pop(); LinkedHashMap<String, Pair<EventType, String>> newTaggedEventTypes = null; LinkedHashMap<String, Pair<EventType, String>> newArrayEventTypes = null; if (evalNode instanceof EvalFilterFactoryNode) { EvalFilterFactoryNode filterNode = (EvalFilterFactoryNode) evalNode; String eventName = filterNode.getRawFilterSpec().getEventTypeName(); if (context.getTableService().getTableMetadata(eventName) != null) { throw new ExprValidationException("Tables cannot be used in pattern filter atoms"); } EventType resolvedEventType = FilterStreamSpecRaw.resolveType(context.getEngineURI(), eventName, context.getEventAdapterService(), context.getPlugInTypeResolutionURIs()); EventType finalEventType = resolvedEventType; String optionalTag = filterNode.getEventAsName(); boolean isPropertyEvaluation = false; boolean isParentMatchUntil = isParentMatchUntil(evalNode, parentNodeStack); // obtain property event type, if final event type is properties if (filterNode.getRawFilterSpec().getOptionalPropertyEvalSpec() != null) { PropertyEvaluator optionalPropertyEvaluator = PropertyEvaluatorFactory.makeEvaluator(filterNode.getRawFilterSpec().getOptionalPropertyEvalSpec(), resolvedEventType, filterNode.getEventAsName(), context.getEventAdapterService(), context.getEngineImportService(), context.getSchedulingService(), context.getVariableService(), context.getTableService(), context.getEngineURI(), context.getStatementId(), context.getStatementName(), context.getAnnotations(), subexpressionIdStack, context.getConfigSnapshot(), context.getNamedWindowMgmtService(), context.getStatementExtensionServicesContext()); finalEventType = optionalPropertyEvaluator.getFragmentEventType(); isPropertyEvaluation = true; } if (finalEventType instanceof EventTypeSPI) { eventTypeReferences.add(((EventTypeSPI) finalEventType).getMetadata().getPrimaryName()); } // If a tag was supplied for the type, the tags must stay with this type, i.e. a=BeanA -> b=BeanA -> a=BeanB is a no if (optionalTag != null) { Pair<EventType, String> pair = tags.getTaggedEventTypes().get(optionalTag); EventType existingType = null; if (pair != null) { existingType = pair.getFirst(); } if (existingType == null) { pair = tags.getArrayEventTypes().get(optionalTag); if (pair != null) { throw new ExprValidationException("Tag '" + optionalTag + "' for event '" + eventName + "' used in the repeat-until operator cannot also appear in other filter expressions"); } } if ((existingType != null) && (existingType != finalEventType)) { throw new ExprValidationException("Tag '" + optionalTag + "' for event '" + eventName + "' has already been declared for events of type " + existingType.getUnderlyingType().getName()); } pair = new Pair<EventType, String>(finalEventType, eventName); // add tagged type if (isPropertyEvaluation || isParentMatchUntil) { newArrayEventTypes = new LinkedHashMap<String, Pair<EventType, String>>(); newArrayEventTypes.put(optionalTag, pair); } else { newTaggedEventTypes = new LinkedHashMap<String, Pair<EventType, String>>(); newTaggedEventTypes.put(optionalTag, pair); } } // For this filter, filter types are all known tags at this time, // and additionally stream 0 (self) is our event type. // Stream type service allows resolution by property name event if that name appears in other tags. // by defaulting to stream zero. // Stream zero is always the current event type, all others follow the order of the map (stream 1 to N). String selfStreamName = optionalTag; if (selfStreamName == null) { selfStreamName = "s_" + UuidGenerator.generate(); } LinkedHashMap<String, Pair<EventType, String>> filterTypes = new LinkedHashMap<String, Pair<EventType, String>>(); Pair<EventType, String> typePair = new Pair<EventType, String>(finalEventType, eventName); filterTypes.put(selfStreamName, typePair); filterTypes.putAll(tags.getTaggedEventTypes()); // for the filter, specify all tags used LinkedHashMap<String, Pair<EventType, String>> filterTaggedEventTypes = new LinkedHashMap<String, Pair<EventType, String>>(tags.getTaggedEventTypes()); filterTaggedEventTypes.remove(optionalTag); // handle array tags (match-until clause) LinkedHashMap<String, Pair<EventType, String>> arrayCompositeEventTypes = null; if (tags.getArrayEventTypes() != null && !tags.getArrayEventTypes().isEmpty()) { arrayCompositeEventTypes = new LinkedHashMap<String, Pair<EventType, String>>(); String patternSubexEventType = getPatternSubexEventType(context.getStatementId(), "pattern", subexpressionIdStack); for (Map.Entry<String, Pair<EventType, String>> entry : tags.getArrayEventTypes().entrySet()) { LinkedHashMap<String, Pair<EventType, String>> specificArrayType = new LinkedHashMap<String, Pair<EventType, String>>(); specificArrayType.put(entry.getKey(), entry.getValue()); EventType arrayTagCompositeEventType = context.getEventAdapterService().createSemiAnonymousMapType(patternSubexEventType, Collections.<String, Pair<EventType, String>>emptyMap(), specificArrayType, isInsertInto); context.getStatementSemiAnonymousTypeRegistry().register(arrayTagCompositeEventType); String tag = entry.getKey(); if (!filterTypes.containsKey(tag)) { Pair<EventType, String> pair = new Pair<EventType, String>(arrayTagCompositeEventType, tag); filterTypes.put(tag, pair); arrayCompositeEventTypes.put(tag, pair); } } } StreamTypeService streamTypeService = new StreamTypeServiceImpl(filterTypes, context.getEngineURI(), true, false); List<ExprNode> exprNodes = filterNode.getRawFilterSpec().getFilterExpressions(); FilterSpecCompiled spec = FilterSpecCompiler.makeFilterSpec(resolvedEventType, eventName, exprNodes, filterNode.getRawFilterSpec().getOptionalPropertyEvalSpec(), filterTaggedEventTypes, arrayCompositeEventTypes, streamTypeService, null, context, subexpressionIdStack); filterNode.setFilterSpec(spec); } else if (evalNode instanceof EvalObserverFactoryNode) { EvalObserverFactoryNode observerNode = (EvalObserverFactoryNode) evalNode; try { ObserverFactory observerFactory = context.getPatternResolutionService().create(observerNode.getPatternObserverSpec()); StreamTypeService streamTypeService = getStreamTypeService(context.getEngineURI(), context.getStatementId(), context.getEventAdapterService(), tags.getTaggedEventTypes(), tags.getArrayEventTypes(), subexpressionIdStack, "observer", context); ExprValidationContext validationContext = new ExprValidationContext(streamTypeService, context.getEngineImportService(), context.getStatementExtensionServicesContext(), null, context.getSchedulingService(), context.getVariableService(), context.getTableService(), evaluatorContext, context.getEventAdapterService(), context.getStatementName(), context.getStatementId(), context.getAnnotations(), context.getContextDescriptor(), false, false, false, false, null, false); List<ExprNode> validated = validateExpressions(ExprNodeOrigin.PATTERNOBSERVER, observerNode.getPatternObserverSpec().getObjectParameters(), validationContext); MatchedEventConvertor convertor = new MatchedEventConvertorImpl(tags.getTaggedEventTypes(), tags.getArrayEventTypes(), allTagNamesOrdered, context.getEventAdapterService()); observerNode.setObserverFactory(observerFactory); observerFactory.setObserverParameters(validated, convertor, validationContext); } catch (ObserverParameterException e) { throw new ExprValidationException("Invalid parameter for pattern observer '" + observerNode.toPrecedenceFreeEPL() + "': " + e.getMessage(), e); } catch (PatternObjectException e) { throw new ExprValidationException("Failed to resolve pattern observer '" + observerNode.toPrecedenceFreeEPL() + "': " + e.getMessage(), e); } } else if (evalNode instanceof EvalGuardFactoryNode) { EvalGuardFactoryNode guardNode = (EvalGuardFactoryNode) evalNode; try { GuardFactory guardFactory = context.getPatternResolutionService().create(guardNode.getPatternGuardSpec()); StreamTypeService streamTypeService = getStreamTypeService(context.getEngineURI(), context.getStatementId(), context.getEventAdapterService(), tags.getTaggedEventTypes(), tags.getArrayEventTypes(), subexpressionIdStack, "guard", context); ExprValidationContext validationContext = new ExprValidationContext(streamTypeService, context.getEngineImportService(), context.getStatementExtensionServicesContext(), null, context.getSchedulingService(), context.getVariableService(), context.getTableService(), evaluatorContext, context.getEventAdapterService(), context.getStatementName(), context.getStatementId(), context.getAnnotations(), context.getContextDescriptor(), false, false, false, false, null, false); List<ExprNode> validated = validateExpressions(ExprNodeOrigin.PATTERNGUARD, guardNode.getPatternGuardSpec().getObjectParameters(), validationContext); MatchedEventConvertor convertor = new MatchedEventConvertorImpl(tags.getTaggedEventTypes(), tags.getArrayEventTypes(), allTagNamesOrdered, context.getEventAdapterService()); guardNode.setGuardFactory(guardFactory); guardFactory.setGuardParameters(validated, convertor); } catch (GuardParameterException e) { throw new ExprValidationException("Invalid parameter for pattern guard '" + guardNode.toPrecedenceFreeEPL() + "': " + e.getMessage(), e); } catch (PatternObjectException e) { throw new ExprValidationException("Failed to resolve pattern guard '" + guardNode.toPrecedenceFreeEPL() + "': " + e.getMessage(), e); } } else if (evalNode instanceof EvalEveryDistinctFactoryNode) { EvalEveryDistinctFactoryNode distinctNode = (EvalEveryDistinctFactoryNode) evalNode; MatchEventSpec matchEventFromChildNodes = analyzeMatchEvent(distinctNode); StreamTypeService streamTypeService = getStreamTypeService(context.getEngineURI(), context.getStatementId(), context.getEventAdapterService(), matchEventFromChildNodes.getTaggedEventTypes(), matchEventFromChildNodes.getArrayEventTypes(), subexpressionIdStack, "every-distinct", context); ExprValidationContext validationContext = new ExprValidationContext(streamTypeService, context.getEngineImportService(), context.getStatementExtensionServicesContext(), null, context.getSchedulingService(), context.getVariableService(), context.getTableService(), evaluatorContext, context.getEventAdapterService(), context.getStatementName(), context.getStatementId(), context.getAnnotations(), context.getContextDescriptor(), false, false, false, false, null, false); List<ExprNode> validated; try { validated = validateExpressions(ExprNodeOrigin.PATTERNEVERYDISTINCT, distinctNode.getExpressions(), validationContext); } catch (ExprValidationPropertyException ex) { throw new ExprValidationPropertyException(ex.getMessage() + ", every-distinct requires that all properties resolve from sub-expressions to the every-distinct", ex.getCause()); } MatchedEventConvertor convertor = new MatchedEventConvertorImpl(matchEventFromChildNodes.getTaggedEventTypes(), matchEventFromChildNodes.getArrayEventTypes(), allTagNamesOrdered, context.getEventAdapterService()); distinctNode.setConvertor(convertor); // Determine whether some expressions are constants or time period List<ExprNode> distinctExpressions = new ArrayList<ExprNode>(); ExprTimePeriodEvalDeltaConst timeDeltaComputation = null; ExprNode expiryTimeExp = null; int count = -1; int last = validated.size() - 1; for (ExprNode expr : validated) { count++; if (count == last && expr instanceof ExprTimePeriod) { expiryTimeExp = expr; ExprTimePeriod timePeriodExpr = (ExprTimePeriod) expiryTimeExp; timeDeltaComputation = timePeriodExpr.constEvaluator(new ExprEvaluatorContextStatement(context, false)); } else if (expr.isConstantResult()) { if (count == last) { Object value = expr.getExprEvaluator().evaluate(null, true, evaluatorContext); if (!(value instanceof Number)) { throw new ExprValidationException("Invalid parameter for every-distinct, expected number of seconds constant (constant not considered for distinct)"); } Number secondsExpire = (Number) expr.getExprEvaluator().evaluate(null, true, evaluatorContext); Long timeExpire = secondsExpire == null ? null : context.getTimeAbacus().deltaForSecondsNumber(secondsExpire); if (timeExpire != null && timeExpire > 0) { timeDeltaComputation = new ExprTimePeriodEvalDeltaConstGivenDelta(timeExpire); expiryTimeExp = expr; } else { log.warn("Invalid seconds-expire " + timeExpire + " for " + ExprNodeUtility.toExpressionStringMinPrecedenceSafe(expr)); } } else { log.warn("Every-distinct node utilizes an expression returning a constant value, please check expression '" + ExprNodeUtility.toExpressionStringMinPrecedenceSafe(expr) + "', not adding expression to distinct-value expression list"); } } else { distinctExpressions.add(expr); } } if (distinctExpressions.isEmpty()) { throw new ExprValidationException("Every-distinct node requires one or more distinct-value expressions that each return non-constant result values"); } distinctNode.setDistinctExpressions(distinctExpressions, timeDeltaComputation, expiryTimeExp); } else if (evalNode instanceof EvalMatchUntilFactoryNode) { EvalMatchUntilFactoryNode matchUntilNode = (EvalMatchUntilFactoryNode) evalNode; // compile bounds expressions, if any MatchEventSpec untilMatchEventSpec = new MatchEventSpec(tags.getTaggedEventTypes(), tags.getArrayEventTypes()); StreamTypeService streamTypeService = getStreamTypeService(context.getEngineURI(), context.getStatementId(), context.getEventAdapterService(), untilMatchEventSpec.getTaggedEventTypes(), untilMatchEventSpec.getArrayEventTypes(), subexpressionIdStack, "until", context); ExprValidationContext validationContext = new ExprValidationContext(streamTypeService, context.getEngineImportService(), context.getStatementExtensionServicesContext(), null, context.getSchedulingService(), context.getVariableService(), context.getTableService(), evaluatorContext, context.getEventAdapterService(), context.getStatementName(), context.getStatementId(), context.getAnnotations(), context.getContextDescriptor(), false, false, false, false, null, false); ExprNode lower = validateBounds(matchUntilNode.getLowerBounds(), validationContext); matchUntilNode.setLowerBounds(lower); ExprNode upper = validateBounds(matchUntilNode.getUpperBounds(), validationContext); matchUntilNode.setUpperBounds(upper); ExprNode single = validateBounds(matchUntilNode.getSingleBound(), validationContext); matchUntilNode.setSingleBound(single); MatchedEventConvertor convertor = new MatchedEventConvertorImpl(untilMatchEventSpec.getTaggedEventTypes(), untilMatchEventSpec.getArrayEventTypes(), allTagNamesOrdered, context.getEventAdapterService()); matchUntilNode.setConvertor(convertor); // compile new tag lists Set<String> arrayTags = null; EvalNodeAnalysisResult matchUntilAnalysisResult = EvalNodeUtil.recursiveAnalyzeChildNodes(matchUntilNode.getChildNodes().get(0)); for (EvalFilterFactoryNode filterNode : matchUntilAnalysisResult.getFilterNodes()) { String optionalTag = filterNode.getEventAsName(); if (optionalTag != null) { if (arrayTags == null) { arrayTags = new HashSet<String>(); } arrayTags.add(optionalTag); } } if (arrayTags != null) { for (String arrayTag : arrayTags) { if (!tags.getArrayEventTypes().containsKey(arrayTag)) { tags.getArrayEventTypes().put(arrayTag, tags.getTaggedEventTypes().get(arrayTag)); tags.getTaggedEventTypes().remove(arrayTag); } } } matchUntilNode.setTagsArrayedSet(getIndexesForTags(allTagNamesOrdered, arrayTags)); } else if (evalNode instanceof EvalFollowedByFactoryNode) { EvalFollowedByFactoryNode followedByNode = (EvalFollowedByFactoryNode) evalNode; StreamTypeService streamTypeService = new StreamTypeServiceImpl(context.getEngineURI(), false); ExprValidationContext validationContext = new ExprValidationContext(streamTypeService, context.getEngineImportService(), context.getStatementExtensionServicesContext(), null, context.getSchedulingService(), context.getVariableService(), context.getTableService(), evaluatorContext, context.getEventAdapterService(), context.getStatementName(), context.getStatementId(), context.getAnnotations(), context.getContextDescriptor(), false, false, false, false, null, false); if (followedByNode.getOptionalMaxExpressions() != null) { List<ExprNode> validated = new ArrayList<ExprNode>(); for (ExprNode maxExpr : followedByNode.getOptionalMaxExpressions()) { if (maxExpr == null) { validated.add(null); } else { ExprNodeSummaryVisitor visitor = new ExprNodeSummaryVisitor(); maxExpr.accept(visitor); if (!visitor.isPlain()) { String errorMessage = "Invalid maximum expression in followed-by, " + visitor.getMessage() + " are not allowed within the expression"; log.error(errorMessage); throw new ExprValidationException(errorMessage); } ExprNode validatedExpr = ExprNodeUtility.getValidatedSubtree(ExprNodeOrigin.FOLLOWEDBYMAX, maxExpr, validationContext); validated.add(validatedExpr); if ((validatedExpr.getExprEvaluator().getType() == null) || (!JavaClassHelper.isNumeric(validatedExpr.getExprEvaluator().getType()))) { String message = "Invalid maximum expression in followed-by, the expression must return an integer value"; throw new ExprValidationException(message); } } } followedByNode.setOptionalMaxExpressions(validated); } } if (newTaggedEventTypes != null) { tags.getTaggedEventTypes().putAll(newTaggedEventTypes); } if (newArrayEventTypes != null) { tags.getArrayEventTypes().putAll(newArrayEventTypes); } } private static ExprNode validateBounds(ExprNode bounds, ExprValidationContext validationContext) throws ExprValidationException { String message = "Match-until bounds value expressions must return a numeric value"; if (bounds != null) { ExprNode validated = ExprNodeUtility.getValidatedSubtree(ExprNodeOrigin.PATTERNMATCHUNTILBOUNDS, bounds, validationContext); if ((validated.getExprEvaluator().getType() == null) || (!JavaClassHelper.isNumeric(validated.getExprEvaluator().getType()))) { throw new ExprValidationException(message); } return validated; } return null; } private static int[] getIndexesForTags(LinkedHashSet<String> allTagNamesOrdered, Set<String> arrayTags) { if (arrayTags == null || arrayTags.isEmpty()) { return new int[0]; } int[] indexes = new int[arrayTags.size()]; int count = 0; for (String arrayTag : arrayTags) { int index = 0; int found = findTagNumber(arrayTag, allTagNamesOrdered); indexes[count] = found; count++; } return indexes; } private static int findTagNumber(String findTag, LinkedHashSet<String> allTagNamesOrdered) { int index = 0; for (String tag : allTagNamesOrdered) { if (findTag.equals(tag)) { return index; } index++; } throw new EPException("Failed to find tag '" + findTag + "' among known tags"); } private static boolean isParentMatchUntil(EvalFactoryNode currentNode, Stack<EvalFactoryNode> parentNodeStack) { if (parentNodeStack.isEmpty()) { return false; } for (EvalFactoryNode deepParent : parentNodeStack) { if (deepParent instanceof EvalMatchUntilFactoryNode) { EvalMatchUntilFactoryNode matchUntilFactoryNode = (EvalMatchUntilFactoryNode) deepParent; if (matchUntilFactoryNode.getChildNodes().get(0) == currentNode) { return true; } } } return false; } private static List<ExprNode> validateExpressions(ExprNodeOrigin exprNodeOrigin, List<ExprNode> objectParameters, ExprValidationContext validationContext) throws ExprValidationException { if (objectParameters == null) { return objectParameters; } List<ExprNode> validated = new ArrayList<ExprNode>(); for (ExprNode node : objectParameters) { validated.add(ExprNodeUtility.getValidatedSubtree(exprNodeOrigin, node, validationContext)); } return validated; } private static StreamTypeService getStreamTypeService(String engineURI, int statementId, EventAdapterService eventAdapterService, Map<String, Pair<EventType, String>> taggedEventTypes, Map<String, Pair<EventType, String>> arrayEventTypes, Deque<Integer> subexpressionIdStack, String objectType, StatementContext statementContext) { LinkedHashMap<String, Pair<EventType, String>> filterTypes = new LinkedHashMap<String, Pair<EventType, String>>(); filterTypes.putAll(taggedEventTypes); // handle array tags (match-until clause) if (arrayEventTypes != null) { String patternSubexEventType = getPatternSubexEventType(statementId, objectType, subexpressionIdStack); EventType arrayTagCompositeEventType = eventAdapterService.createSemiAnonymousMapType(patternSubexEventType, new HashMap(), arrayEventTypes, false); statementContext.getStatementSemiAnonymousTypeRegistry().register(arrayTagCompositeEventType); for (Map.Entry<String, Pair<EventType, String>> entry : arrayEventTypes.entrySet()) { String tag = entry.getKey(); if (!filterTypes.containsKey(tag)) { Pair<EventType, String> pair = new Pair<EventType, String>(arrayTagCompositeEventType, tag); filterTypes.put(tag, pair); } } } return new StreamTypeServiceImpl(filterTypes, engineURI, true, false); } private static String getPatternSubexEventType(int statementId, String objectType, Deque<Integer> subexpressionIdStack) { StringWriter writer = new StringWriter(); writer.append(Integer.toString(statementId)); writer.append("_"); writer.append(objectType); for (Integer num : subexpressionIdStack) { writer.append("_"); writer.append(Integer.toString(num)); } return writer.toString(); } private static EvalFactoryNode recursiveAddAuditNode(PatternNodeFactory patternNodeFactory, EvalFactoryNode parentNode, boolean auditPattern, boolean auditPatternInstance, EvalFactoryNode evalNode, EvalAuditInstanceCount instanceCount) { StringWriter writer = new StringWriter(); evalNode.toEPL(writer, PatternExpressionPrecedenceEnum.MINIMUM); String expressionText = writer.toString(); boolean filterChildNonQuitting = parentNode != null && parentNode.isFilterChildNonQuitting(); EvalFactoryNode audit = patternNodeFactory.makeAuditNode(auditPattern, auditPatternInstance, expressionText, instanceCount, filterChildNonQuitting); audit.addChildNode(evalNode); List<EvalFactoryNode> newChildNodes = new ArrayList<EvalFactoryNode>(); for (EvalFactoryNode child : evalNode.getChildNodes()) { newChildNodes.add(recursiveAddAuditNode(patternNodeFactory, evalNode, auditPattern, auditPatternInstance, child, instanceCount)); } evalNode.getChildNodes().clear(); evalNode.addChildNodes(newChildNodes); return audit; } private static MatchEventSpec analyzeMatchEvent(EvalFactoryNode relativeNode) { LinkedHashMap<String, Pair<EventType, String>> taggedEventTypes = new LinkedHashMap<String, Pair<EventType, String>>(); LinkedHashMap<String, Pair<EventType, String>> arrayEventTypes = new LinkedHashMap<String, Pair<EventType, String>>(); // Determine all the filter nodes used in the pattern EvalNodeAnalysisResult evalNodeAnalysisResult = EvalNodeUtil.recursiveAnalyzeChildNodes(relativeNode); // collect all filters underneath for (EvalFilterFactoryNode filterNode : evalNodeAnalysisResult.getFilterNodes()) { String optionalTag = filterNode.getEventAsName(); if (optionalTag != null) { taggedEventTypes.put(optionalTag, new Pair<EventType, String>(filterNode.getFilterSpec().getFilterForEventType(), filterNode.getFilterSpec().getFilterForEventTypeName())); } } // collect those filters under a repeat since they are arrays Set<String> arrayTags = new HashSet<String>(); for (EvalMatchUntilFactoryNode matchUntilNode : evalNodeAnalysisResult.getRepeatNodes()) { EvalNodeAnalysisResult matchUntilAnalysisResult = EvalNodeUtil.recursiveAnalyzeChildNodes(matchUntilNode.getChildNodes().get(0)); for (EvalFilterFactoryNode filterNode : matchUntilAnalysisResult.getFilterNodes()) { String optionalTag = filterNode.getEventAsName(); if (optionalTag != null) { arrayTags.add(optionalTag); } } } // for each array tag change collection for (String arrayTag : arrayTags) { if (taggedEventTypes.get(arrayTag) != null) { arrayEventTypes.put(arrayTag, taggedEventTypes.get(arrayTag)); taggedEventTypes.remove(arrayTag); } } return new MatchEventSpec(taggedEventTypes, arrayEventTypes); } public boolean isSuppressSameEventMatches() { return suppressSameEventMatches; } public boolean isDiscardPartialsOnMatch() { return discardPartialsOnMatch; } public static class FilterForFilterFactoryNodes implements EvalNodeUtilFactoryFilter { public final static FilterForFilterFactoryNodes INSTANCE = new FilterForFilterFactoryNodes(); public boolean consider(EvalFactoryNode node) { return node instanceof EvalFilterFactoryNode; } } }