/* *************************************************************************************** * 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.pattern; import com.espertech.esper.client.EventBean; import com.espertech.esper.metrics.instrumentation.InstrumentationHelper; import java.util.ArrayList; import java.util.Arrays; import java.util.Set; /** * This class represents the state of a match-until node in the evaluation state tree. */ public class EvalMatchUntilStateNode extends EvalStateNode implements Evaluator { protected final EvalMatchUntilNode evalMatchUntilNode; protected MatchedEventMap beginState; protected final ArrayList<EventBean>[] matchedEventArrays; protected EvalStateNode stateMatcher; protected EvalStateNode stateUntil; protected int numMatches; protected Integer lowerbounds; protected Integer upperbounds; /** * Constructor. * * @param parentNode is the parent evaluator to call to indicate truth value * @param evalMatchUntilNode is the factory node associated to the state */ public EvalMatchUntilStateNode(Evaluator parentNode, EvalMatchUntilNode evalMatchUntilNode) { super(parentNode); this.matchedEventArrays = (ArrayList<EventBean>[]) new ArrayList[evalMatchUntilNode.getFactoryNode().getTagsArrayed().length]; this.evalMatchUntilNode = evalMatchUntilNode; } public void removeMatch(Set<EventBean> matchEvent) { boolean quit = PatternConsumptionUtil.containsEvent(matchEvent, beginState); if (!quit) { for (ArrayList<EventBean> list : matchedEventArrays) { if (list == null) { continue; } for (EventBean event : list) { if (matchEvent.contains(event)) { quit = true; break; } } if (quit) { break; } } } if (quit) { quit(); this.getParentEvaluator().evaluateFalse(this, true); } else { if (stateMatcher != null) { stateMatcher.removeMatch(matchEvent); } if (stateUntil != null) { stateUntil.removeMatch(matchEvent); } } } @Override public EvalNode getFactoryNode() { return evalMatchUntilNode; } public final void start(MatchedEventMap beginState) { if (InstrumentationHelper.ENABLED) { InstrumentationHelper.get().qPatternMatchUntilStart(evalMatchUntilNode, beginState); } this.beginState = beginState; EvalNode childMatcher = evalMatchUntilNode.getChildNodeSub(); stateMatcher = childMatcher.newState(this, null, 0L); if (evalMatchUntilNode.getChildNodeUntil() != null) { EvalNode childUntil = evalMatchUntilNode.getChildNodeUntil(); stateUntil = childUntil.newState(this, null, 0L); } // start until first, it controls the expression // if the same event fires both match and until, the match should not count if (stateUntil != null) { stateUntil.start(beginState); } EvalMatchUntilStateBounds bounds = EvalMatchUntilStateBounds.initBounds(evalMatchUntilNode.getFactoryNode(), beginState, evalMatchUntilNode.getContext()); this.lowerbounds = bounds.getLowerbounds(); this.upperbounds = bounds.getUpperbounds(); if (stateMatcher != null) { stateMatcher.start(beginState); } if (InstrumentationHelper.ENABLED) { InstrumentationHelper.get().aPatternMatchUntilStart(); } } public final void evaluateTrue(MatchedEventMap matchEvent, EvalStateNode fromNode, boolean isQuitted) { if (InstrumentationHelper.ENABLED) { InstrumentationHelper.get().qPatternMatchUntilEvaluateTrue(evalMatchUntilNode, matchEvent, fromNode == stateUntil); } boolean isMatcher = false; if (fromNode == stateMatcher) { // Add the additional tagged events to the list for later posting isMatcher = true; numMatches++; int[] tags = evalMatchUntilNode.getFactoryNode().getTagsArrayed(); for (int i = 0; i < tags.length; i++) { Object theEvent = matchEvent.getMatchingEventAsObject(tags[i]); if (theEvent != null) { if (matchedEventArrays[i] == null) { matchedEventArrays[i] = new ArrayList<EventBean>(); } if (theEvent instanceof EventBean) { matchedEventArrays[i].add((EventBean) theEvent); } else { EventBean[] arrayEvents = (EventBean[]) theEvent; matchedEventArrays[i].addAll(Arrays.asList(arrayEvents)); } } } } if (isQuitted) { if (isMatcher) { stateMatcher = null; } else { stateUntil = null; } } // handle matcher evaluating true if (isMatcher) { if ((isTightlyBound()) && (numMatches == lowerbounds)) { quitInternal(); MatchedEventMap consolidated = consolidate(matchEvent, matchedEventArrays, evalMatchUntilNode.getFactoryNode().getTagsArrayed()); this.getParentEvaluator().evaluateTrue(consolidated, this, true); } else { // restart or keep started if not bounded, or not upper bounds, or upper bounds not reached boolean restart = (!isBounded()) || (upperbounds == null) || (upperbounds > numMatches); if (stateMatcher == null) { if (restart) { EvalNode childMatcher = evalMatchUntilNode.getChildNodeSub(); stateMatcher = childMatcher.newState(this, null, 0L); stateMatcher.start(beginState); } } else { if (!restart) { stateMatcher.quit(); stateMatcher = null; } } } } else { // handle until-node quitInternal(); // consolidate multiple matched events into a single event MatchedEventMap consolidated = consolidate(matchEvent, matchedEventArrays, evalMatchUntilNode.getFactoryNode().getTagsArrayed()); if ((lowerbounds != null) && (numMatches < lowerbounds)) { this.getParentEvaluator().evaluateFalse(this, true); } else { this.getParentEvaluator().evaluateTrue(consolidated, this, true); } } if (InstrumentationHelper.ENABLED) { InstrumentationHelper.get().aPatternMatchUntilEvaluateTrue(stateMatcher == null && stateUntil == null); } } public static MatchedEventMap consolidate(MatchedEventMap beginState, ArrayList<EventBean>[] matchedEventList, int[] tagsArrayed) { if (tagsArrayed == null) { return beginState; } for (int i = 0; i < tagsArrayed.length; i++) { if (matchedEventList[i] == null) { continue; } EventBean[] eventsForTag = matchedEventList[i].toArray(new EventBean[matchedEventList[i].size()]); beginState.add(tagsArrayed[i], eventsForTag); } return beginState; } public final void evaluateFalse(EvalStateNode fromNode, boolean restartable) { if (InstrumentationHelper.ENABLED) { InstrumentationHelper.get().qPatternMatchUntilEvalFalse(evalMatchUntilNode, fromNode == stateUntil); } boolean isMatcher = false; if (fromNode == stateMatcher) { isMatcher = true; } if (isMatcher) { stateMatcher.quit(); stateMatcher = null; } else { stateUntil.quit(); stateUntil = null; } this.getParentEvaluator().evaluateFalse(this, true); if (InstrumentationHelper.ENABLED) { InstrumentationHelper.get().aPatternMatchUntilEvalFalse(); } } public final void quit() { if (stateMatcher == null && stateUntil == null) { return; } if (InstrumentationHelper.ENABLED) { InstrumentationHelper.get().qPatternMatchUntilQuit(evalMatchUntilNode); } quitInternal(); if (InstrumentationHelper.ENABLED) { InstrumentationHelper.get().aPatternMatchUntilQuit(); } } public final void accept(EvalStateNodeVisitor visitor) { visitor.visitMatchUntil(evalMatchUntilNode.getFactoryNode(), this, matchedEventArrays, beginState); if (stateMatcher != null) { stateMatcher.accept(visitor); } if (stateUntil != null) { stateUntil.accept(visitor); } } public final String toString() { return "EvalMatchUntilStateNode"; } public boolean isNotOperator() { return false; } public boolean isFilterStateNode() { return false; } public boolean isFilterChildNonQuitting() { return true; } public boolean isObserverStateNodeNonRestarting() { return false; } private boolean isTightlyBound() { return lowerbounds != null && upperbounds != null && upperbounds.equals(lowerbounds); } private boolean isBounded() { return lowerbounds != null || upperbounds != null; } private void quitInternal() { if (stateMatcher != null) { stateMatcher.quit(); stateMatcher = null; } if (stateUntil != null) { stateUntil.quit(); stateUntil = null; } } }