/* * ************************************************************************************* * 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.rowregex; import com.espertech.esper.client.EventBean; import com.espertech.esper.client.EventType; import com.espertech.esper.collection.Pair; import com.espertech.esper.collection.SingleEventIterator; import com.espertech.esper.core.context.util.AgentInstanceContext; import com.espertech.esper.core.service.EPStatementHandleCallback; import com.espertech.esper.core.service.ExtensionServicesContext; import com.espertech.esper.epl.agg.service.AggregationServiceMatchRecognize; import com.espertech.esper.epl.expression.ExprEvaluator; import com.espertech.esper.epl.expression.ExprNode; import com.espertech.esper.epl.expression.ExprNodeUtility; import com.espertech.esper.epl.expression.ExprPreviousMatchRecognizeNode; import com.espertech.esper.epl.spec.MatchRecognizeDefineItem; import com.espertech.esper.epl.spec.MatchRecognizeMeasureItem; import com.espertech.esper.epl.spec.MatchRecognizeSkipEnum; import com.espertech.esper.epl.spec.MatchRecognizeSpec; import com.espertech.esper.schedule.ScheduleHandleCallback; import com.espertech.esper.schedule.ScheduleSlot; import com.espertech.esper.util.ExecutionPathDebugLog; import com.espertech.esper.util.StopCallback; import com.espertech.esper.view.ViewSupport; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import java.io.PrintWriter; import java.io.StringWriter; import java.util.*; /** * View for match recognize support. */ public class EventRowRegexNFAView extends ViewSupport implements StopCallback, EventRowRegexNFAViewService { private static final Log log = LogFactory.getLog(EventRowRegexNFAView.class); private static final boolean IS_DEBUG = false; private static final Iterator<EventBean> NULL_ITERATOR = new SingleEventIterator(null); private final MatchRecognizeSpec matchRecognizeSpec; private final boolean isUnbound; private final boolean isIterateOnly; private final boolean isSelectAsksMultimatches; private final EventType compositeEventType; private final EventType rowEventType; private final AgentInstanceContext agentInstanceContext; private final AggregationServiceMatchRecognize aggregationService; // for interval-handling private final ScheduleSlot scheduleSlot; private final EPStatementHandleCallback handle; private final TreeMap<Long, Object> schedule; private final ExprEvaluator[] columnEvaluators; private final String[] columnNames; private final RegexNFAState[] startStates; private final RegexNFAState[] allStates; private final String[] variablesArray; private final LinkedHashMap<String, Pair<Integer, Boolean>> variableStreams; private final Map<Integer, String> streamsVariables; private final Set<String> variablesSingle; // state private RegexPartitionStateRepo regexPartitionStateRepo; private LinkedHashSet<EventBean> windowMatchedEventset; // this is NOT per partition - some optimizations are done for batch-processing (minus is out-of-sequence in partition) private int eventSequenceNumber; /** * Ctor. * @param compositeEventType final event type * @param rowEventType event type for input rows * @param matchRecognizeSpec specification * @param variableStreams variables and their assigned stream number * @param streamsVariables stream number and the assigned variable * @param variablesSingle single variables * @param callbacksPerIndex for handling the 'prev' function * @param aggregationService handles aggregations * @param isUnbound true if unbound stream * @param isIterateOnly true for iterate-only * @param isSelectAsksMultimatches if asking for multimatches */ public EventRowRegexNFAView(EventType compositeEventType, EventType rowEventType, MatchRecognizeSpec matchRecognizeSpec, LinkedHashMap<String, Pair<Integer, Boolean>> variableStreams, Map<Integer, String> streamsVariables, Set<String> variablesSingle, AgentInstanceContext agentInstanceContext, TreeMap<Integer, List<ExprPreviousMatchRecognizeNode>> callbacksPerIndex, AggregationServiceMatchRecognize aggregationService, boolean isUnbound, boolean isIterateOnly, boolean isSelectAsksMultimatches) { this.matchRecognizeSpec = matchRecognizeSpec; this.compositeEventType = compositeEventType; this.rowEventType = rowEventType; this.variableStreams = variableStreams; this.variablesArray = variableStreams.keySet().toArray(new String[variableStreams.keySet().size()]); this.streamsVariables = streamsVariables; this.variablesSingle = variablesSingle; this.aggregationService = aggregationService; this.isUnbound = isUnbound; this.isIterateOnly = isIterateOnly; this.agentInstanceContext = agentInstanceContext; this.isSelectAsksMultimatches = isSelectAsksMultimatches; if (matchRecognizeSpec.getInterval() != null) { scheduleSlot = agentInstanceContext.getStatementContext().getScheduleBucket().allocateSlot(); ScheduleHandleCallback callback = new ScheduleHandleCallback() { public void scheduledTrigger(ExtensionServicesContext extensionServicesContext) { EventRowRegexNFAView.this.triggered(); } }; handle = new EPStatementHandleCallback(agentInstanceContext.getEpStatementAgentInstanceHandle(), callback); schedule = new TreeMap<Long, Object>(); agentInstanceContext.getTerminationCallbacks().add(this); } else { scheduleSlot = null; handle = null; schedule = null; } this.windowMatchedEventset = new LinkedHashSet<EventBean>(); // handle "previous" function nodes (performance-optimized for direct index access) RegexPartitionStateRandomAccessGetter randomAccessByIndexGetter; if (!callbacksPerIndex.isEmpty()) { // Build an array of indexes int[] randomAccessIndexesRequested = new int[callbacksPerIndex.size()]; int count = 0; for (Map.Entry<Integer, List<ExprPreviousMatchRecognizeNode>> entry : callbacksPerIndex.entrySet()) { randomAccessIndexesRequested[count] = entry.getKey(); count++; } randomAccessByIndexGetter = new RegexPartitionStateRandomAccessGetter(randomAccessIndexesRequested, isUnbound); // Since an expression such as "prior(2, price), prior(8, price)" translates into {2, 8} the relative index is {0, 1}. // Map the expression-supplied index to a relative index count = 0; for (Map.Entry<Integer, List<ExprPreviousMatchRecognizeNode>> entry : callbacksPerIndex.entrySet()) { for (ExprPreviousMatchRecognizeNode callback : entry.getValue()) { callback.setGetter(randomAccessByIndexGetter); callback.setAssignedIndex(count); } count++; } } else { randomAccessByIndexGetter = null; } Map<String, ExprNode> variableDefinitions = new LinkedHashMap<String, ExprNode>(); for (MatchRecognizeDefineItem defineItem : matchRecognizeSpec.getDefines()) { variableDefinitions.put(defineItem.getIdentifier(), defineItem.getExpression()); } // build states RegexNFAStrandResult strand = EventRowRegexHelper.recursiveBuildStartStates(matchRecognizeSpec.getPattern(), variableDefinitions, variableStreams); startStates = strand.getStartStates().toArray(new RegexNFAState[strand.getStartStates().size()]); allStates = strand.getAllStates().toArray(new RegexNFAState[strand.getAllStates().size()]); if (log.isDebugEnabled() || IS_DEBUG) { log.info("NFA tree:\n" + print(startStates)); } // create evaluators columnNames = new String[matchRecognizeSpec.getMeasures().size()]; columnEvaluators = new ExprEvaluator[matchRecognizeSpec.getMeasures().size()]; int count = 0; for (MatchRecognizeMeasureItem measureItem : matchRecognizeSpec.getMeasures()) { columnNames[count] = measureItem.getName(); columnEvaluators[count] = measureItem.getExpr().getExprEvaluator(); count++; } // create state repository if (this.matchRecognizeSpec.getPartitionByExpressions().isEmpty()) { regexPartitionStateRepo = new RegexPartitionStateRepoNoGroup(randomAccessByIndexGetter, matchRecognizeSpec.getInterval() != null); } else { regexPartitionStateRepo = new RegexPartitionStateRepoGroup(randomAccessByIndexGetter, ExprNodeUtility.getEvaluators(matchRecognizeSpec.getPartitionByExpressions()), matchRecognizeSpec.getInterval() != null, agentInstanceContext); } } public void stop() { if (handle != null) { agentInstanceContext.getStatementContext().getSchedulingService().remove(handle, scheduleSlot); } } public void init(EventBean[] newEvents) { updateInternal(newEvents, null, false); } public void update(EventBean[] newData, EventBean[] oldData) { updateInternal(newData, oldData, true); } private void updateInternal(EventBean[] newData, EventBean[] oldData, boolean postOutput) { if (isIterateOnly) { if (oldData != null) { regexPartitionStateRepo.removeOld(oldData, false, new boolean[oldData.length]); } if (newData != null) { for (EventBean newEvent : newData) { RegexPartitionState partitionState = regexPartitionStateRepo.getState(newEvent, true); if ((partitionState != null) && (partitionState.getRandomAccess() != null)) { partitionState.getRandomAccess().newEventPrepare(newEvent); } } } return; } if (oldData != null) { boolean isOutOfSequenceRemove = false; EventBean first = null; if (!windowMatchedEventset.isEmpty()) { first = windowMatchedEventset.iterator().next(); } // remove old data, if found in set boolean[] found = new boolean[oldData.length]; int count = 0; // detect out-of-sequence removes for (EventBean oldEvent : oldData) { boolean removed = windowMatchedEventset.remove(oldEvent); if (removed) { if ((oldEvent != first) && (first != null)) { isOutOfSequenceRemove = true; } found[count++] = true; if (!windowMatchedEventset.isEmpty()) { first = windowMatchedEventset.iterator().next(); } } } // remove old events from repository - and let the repository know there are no interesting events left regexPartitionStateRepo.removeOld(oldData, windowMatchedEventset.isEmpty(), found); // reset, rebuilding state if (isOutOfSequenceRemove) { regexPartitionStateRepo = regexPartitionStateRepo.copyForIterate(); windowMatchedEventset = new LinkedHashSet<EventBean>(); Iterator<EventBean> parentEvents = this.getParent().iterator(); EventRowRegexIteratorResult iteratorResult = processIterator(startStates, parentEvents, regexPartitionStateRepo); eventSequenceNumber = iteratorResult.getEventSequenceNum(); } } if (newData == null) { return; } List<RegexNFAStateEntry> endStates = new ArrayList<RegexNFAStateEntry>(); List<RegexNFAStateEntry> nextStates = new ArrayList<RegexNFAStateEntry>(); for (EventBean newEvent : newData) { eventSequenceNumber++; // get state holder for this event RegexPartitionState partitionState = regexPartitionStateRepo.getState(newEvent, true); List<RegexNFAStateEntry> currentStates = partitionState.getCurrentStates(); // add start states for each new event for (RegexNFAState startState : startStates) { long time = 0; if (matchRecognizeSpec.getInterval() != null) { time = agentInstanceContext.getStatementContext().getSchedulingService().getTime(); } currentStates.add(new RegexNFAStateEntry(eventSequenceNumber, time, startState, new EventBean[variableStreams.size()], new int[allStates.length], null, partitionState.getOptionalKeys())); } if (partitionState.getRandomAccess() != null) { partitionState.getRandomAccess().newEventPrepare(newEvent); } if ((ExecutionPathDebugLog.isDebugEnabled) && (log.isDebugEnabled()) || (IS_DEBUG)) { log.info("Evaluating event " + newEvent.getUnderlying() + "\n" + "current : " + printStates(currentStates)); } step(currentStates, newEvent, nextStates, endStates, !isUnbound, eventSequenceNumber, partitionState.getOptionalKeys()); if ((ExecutionPathDebugLog.isDebugEnabled) && (log.isDebugEnabled()) || (IS_DEBUG)) { log.info("Evaluated event " + newEvent.getUnderlying() + "\n" + "next : " + printStates(nextStates) + "\n" + "end : " + printStates(endStates)); } partitionState.setCurrentStates(nextStates); nextStates = currentStates; nextStates.clear(); } if (endStates.isEmpty()) { return; } // perform inter-ranking and elimination of duplicate matches if (!matchRecognizeSpec.isAllMatches()) { endStates = rankEndStatesMultiPartition(endStates); } // handle interval for the set of matches if (matchRecognizeSpec.getInterval() != null) { Iterator<RegexNFAStateEntry> it = endStates.iterator(); for (;it.hasNext();) { RegexNFAStateEntry endState = it.next(); RegexPartitionState partitionState = regexPartitionStateRepo.getState(endState.getPartitionKey()); if (partitionState == null) { log.warn("Null partition state encountered, skipping row"); continue; } long matchBeginTime = endState.getMatchBeginEventTime(); long current = agentInstanceContext.getStatementContext().getSchedulingService().getTime(); long deltaFromStart = current - matchBeginTime; long deltaUntil = matchRecognizeSpec.getInterval().getMSec() - deltaFromStart; if (schedule.containsKey(matchBeginTime)) { scheduleCallback(deltaUntil, endState); it.remove(); } else { if (deltaFromStart < deltaUntil) { scheduleCallback(deltaUntil, endState); it.remove(); } } } if (endStates.isEmpty()) { return; } } // handle skip for incremental mode else if (matchRecognizeSpec.getSkip().getSkip() == MatchRecognizeSkipEnum.PAST_LAST_ROW) { Iterator<RegexNFAStateEntry> endStateIter = endStates.iterator(); for (;endStateIter.hasNext();) { RegexNFAStateEntry endState = endStateIter.next(); RegexPartitionState partitionState = regexPartitionStateRepo.getState(endState.getPartitionKey()); if (partitionState == null) { log.warn("Null partition state encountered, skipping row"); continue; } Iterator<RegexNFAStateEntry> stateIter = partitionState.getCurrentStates().iterator(); for (;stateIter.hasNext();) { RegexNFAStateEntry currentState = stateIter.next(); if (currentState.getMatchBeginEventSeqNo() <= endState.getMatchEndEventSeqNo()) { stateIter.remove(); } } } } else if (matchRecognizeSpec.getSkip().getSkip() == MatchRecognizeSkipEnum.TO_NEXT_ROW) { Iterator<RegexNFAStateEntry> endStateIter = endStates.iterator(); for (;endStateIter.hasNext();) { RegexNFAStateEntry endState = endStateIter.next(); RegexPartitionState partitionState = regexPartitionStateRepo.getState(endState.getPartitionKey()); if (partitionState == null) { log.warn("Null partition state encountered, skipping row"); continue; } Iterator<RegexNFAStateEntry> stateIter = partitionState.getCurrentStates().iterator(); for (;stateIter.hasNext();) { RegexNFAStateEntry currentState = stateIter.next(); if (currentState.getMatchBeginEventSeqNo() <= endState.getMatchBeginEventSeqNo()) { stateIter.remove(); } } } } EventBean[] outBeans = new EventBean[endStates.size()]; int count = 0; for (RegexNFAStateEntry endState : endStates) { outBeans[count++] = generateOutputRow(endState); // check partition state - if empty delete (no states and no random access) if (endState.getPartitionKey() != null) { RegexPartitionState state = regexPartitionStateRepo.getState(endState.getPartitionKey()); if (state.getCurrentStates().isEmpty() && state.getRandomAccess() == null) { regexPartitionStateRepo.removeState(endState.getPartitionKey()); } } } if (postOutput) { updateChildren(outBeans, null); } } private RegexNFAStateEntry rankEndStates(List<RegexNFAStateEntry> endStates) { RegexNFAStateEntry found = null; int min = Integer.MAX_VALUE; boolean multipleMinimums = false; for (RegexNFAStateEntry state : endStates) { if (state.getMatchBeginEventSeqNo() < min) { found = state; min = state.getMatchBeginEventSeqNo(); } else if (state.getMatchBeginEventSeqNo() == min) { multipleMinimums = true; } } if (!multipleMinimums) { Collections.singletonList(found); } int[] best = null; found = null; for (RegexNFAStateEntry state : endStates) { if (state.getMatchBeginEventSeqNo() != min) { continue; } if (best == null) { best = state.getGreedycountPerState(); found = state; } else { int[] current = state.getGreedycountPerState(); if (compare(current, best)) { best = current; found = state; } } } return found; } private boolean compare(int[] current, int[] best) { for (RegexNFAState state : allStates) { if (state.isGreedy() == null) { continue; } if (state.isGreedy()) { if (current[state.getNodeNumFlat()] > best[state.getNodeNumFlat()]) { return true; } } else { if (current[state.getNodeNumFlat()] < best[state.getNodeNumFlat()]) { return true; } } } return false; } private EventRowRegexIteratorResult processIterator(RegexNFAState[] startStates, Iterator<EventBean> events, RegexPartitionStateRepo regexPartitionStateRepo) { List<RegexNFAStateEntry> endStates = new ArrayList<RegexNFAStateEntry>(); List<RegexNFAStateEntry> nextStates = new ArrayList<RegexNFAStateEntry>(); List<RegexNFAStateEntry> currentStates; int eventSequenceNumber = 0; EventBean theEvent; for (;events.hasNext();) { theEvent = events.next(); eventSequenceNumber++; RegexPartitionState partitionState = regexPartitionStateRepo.getState(theEvent, false); currentStates = partitionState.getCurrentStates(); // add start states for each new event for (RegexNFAState startState : startStates) { long time = 0; if (matchRecognizeSpec.getInterval() != null) { time = agentInstanceContext.getStatementContext().getSchedulingService().getTime(); } currentStates.add(new RegexNFAStateEntry(eventSequenceNumber, time, startState, new EventBean[variableStreams.size()], new int[allStates.length], null, partitionState.getOptionalKeys())); } if (partitionState.getRandomAccess() != null) { partitionState.getRandomAccess().existingEventPrepare(theEvent); } if ((ExecutionPathDebugLog.isDebugEnabled) && (log.isDebugEnabled()) || (IS_DEBUG)) { log.info("Evaluating event " + theEvent.getUnderlying() + "\n" + "current : " + printStates(currentStates)); } step(currentStates, theEvent, nextStates, endStates, false, eventSequenceNumber, partitionState.getOptionalKeys()); if ((ExecutionPathDebugLog.isDebugEnabled) && (log.isDebugEnabled()) || (IS_DEBUG)) { log.info("Evaluating event " + theEvent.getUnderlying() + "\n" + "next : " + printStates(nextStates) + "\n" + "end : " + printStates(endStates)); } partitionState.setCurrentStates(nextStates); nextStates = currentStates; nextStates.clear(); } return new EventRowRegexIteratorResult(endStates, eventSequenceNumber); } public EventType getEventType() { return rowEventType; } public Iterator<EventBean> iterator() { if (isUnbound) { return NULL_ITERATOR; } Iterator<EventBean> it = parent.iterator(); RegexPartitionStateRepo regexPartitionStateRepoNew = regexPartitionStateRepo.copyForIterate(); EventRowRegexIteratorResult iteratorResult = processIterator(startStates, it, regexPartitionStateRepoNew); List<RegexNFAStateEntry> endStates = iteratorResult.getEndStates(); if (endStates.isEmpty()) { return NULL_ITERATOR; } else { endStates = rankEndStatesMultiPartition(endStates); } List<EventBean> output = new ArrayList<EventBean>(); for (RegexNFAStateEntry endState : endStates) { output.add(generateOutputRow(endState)); } return output.iterator(); } private List<RegexNFAStateEntry> rankEndStatesMultiPartition(List<RegexNFAStateEntry> endStates) { if (endStates.isEmpty()) { return endStates; } if (endStates.size() == 1) { return endStates; } // unpartitioned case - if (matchRecognizeSpec.getPartitionByExpressions().isEmpty()) { return rankEndStatesWithinPartitionByStart(endStates); } // partitioned case - structure end states by partition Map<Object, Object> perPartition = new LinkedHashMap<Object, Object>(); for (RegexNFAStateEntry endState : endStates) { Object value = perPartition.get(endState.getPartitionKey()); if (value == null) { perPartition.put(endState.getPartitionKey(), endState); } else if (value instanceof List) { List<RegexNFAStateEntry> entries = (List<RegexNFAStateEntry>) value; entries.add(endState); } else { List<RegexNFAStateEntry> entries = new ArrayList<RegexNFAStateEntry>(); entries.add((RegexNFAStateEntry) value); entries.add(endState); perPartition.put(endState.getPartitionKey(), entries); } } List<RegexNFAStateEntry> finalEndStates = new ArrayList<RegexNFAStateEntry>(); for (Map.Entry<Object, Object> entry : perPartition.entrySet()) { if (entry.getValue() instanceof RegexNFAStateEntry) { finalEndStates.add((RegexNFAStateEntry) entry.getValue()); } else { List<RegexNFAStateEntry> entries = (List<RegexNFAStateEntry>) entry.getValue(); finalEndStates.addAll(rankEndStatesWithinPartitionByStart(entries)); } } return finalEndStates; } private List<RegexNFAStateEntry> rankEndStatesWithinPartitionByStart(List<RegexNFAStateEntry> endStates) { if (endStates.isEmpty()) { return endStates; } if (endStates.size() == 1) { return endStates; } TreeMap<Integer, Object> endStatesPerBeginEvent = new TreeMap<Integer, Object>(); for (RegexNFAStateEntry entry : endStates) { Integer endNum = entry.getMatchBeginEventSeqNo(); Object value = endStatesPerBeginEvent.get(endNum); if (value == null) { endStatesPerBeginEvent.put(endNum, entry); } else if (value instanceof List) { List<RegexNFAStateEntry> entries = (List<RegexNFAStateEntry>) value; entries.add(entry); } else { List<RegexNFAStateEntry> entries = new ArrayList<RegexNFAStateEntry>(); entries.add((RegexNFAStateEntry) value); entries.add(entry); endStatesPerBeginEvent.put(endNum, entries); } } if (endStatesPerBeginEvent.size() == 1) { List<RegexNFAStateEntry> endStatesUnranked = (List<RegexNFAStateEntry>) endStatesPerBeginEvent.values().iterator().next(); if (matchRecognizeSpec.isAllMatches()) { return endStatesUnranked; } RegexNFAStateEntry chosen = rankEndStates(endStatesUnranked); return Collections.singletonList(chosen); } List<RegexNFAStateEntry> endStatesRanked = new ArrayList<RegexNFAStateEntry>(); Set<Integer> keyset = endStatesPerBeginEvent.keySet(); Integer[] keys = keyset.toArray(new Integer[keyset.size()]); for (Integer key : keys) { Object value = endStatesPerBeginEvent.remove(key); if (value == null) { continue; } RegexNFAStateEntry entryTaken; if (value instanceof List) { List<RegexNFAStateEntry> endStatesUnranked = (List<RegexNFAStateEntry>) value; if (endStatesUnranked.isEmpty()) { continue; } entryTaken = rankEndStates(endStatesUnranked); if (matchRecognizeSpec.isAllMatches()) { endStatesRanked.addAll(endStatesUnranked); // we take all matches and don't rank except to determine skip-past } else { endStatesRanked.add(entryTaken); } } else { entryTaken = (RegexNFAStateEntry) value; endStatesRanked.add(entryTaken); } // could be null as removals take place if (entryTaken != null) { if (matchRecognizeSpec.getSkip().getSkip() == MatchRecognizeSkipEnum.PAST_LAST_ROW) { int skipPastRow = entryTaken.getMatchEndEventSeqNo(); removeSkippedEndStates(endStatesPerBeginEvent, skipPastRow); } else if (matchRecognizeSpec.getSkip().getSkip() == MatchRecognizeSkipEnum.TO_NEXT_ROW) { int skipPastRow = entryTaken.getMatchBeginEventSeqNo(); removeSkippedEndStates(endStatesPerBeginEvent, skipPastRow); } } } return endStatesRanked; } private void removeSkippedEndStates(TreeMap<Integer, Object> endStatesPerEndEvent, int skipPastRow) { for (Map.Entry<Integer, Object> entry : endStatesPerEndEvent.entrySet()) { Object value = entry.getValue(); if (value instanceof List) { List<RegexNFAStateEntry> endStatesUnranked = (List<RegexNFAStateEntry>) value; Iterator<RegexNFAStateEntry> it = endStatesUnranked.iterator(); for (;it.hasNext();) { RegexNFAStateEntry endState = it.next(); if (endState.getMatchBeginEventSeqNo() <= skipPastRow) { it.remove(); } } } else { RegexNFAStateEntry endState = (RegexNFAStateEntry) value; if (endState.getMatchBeginEventSeqNo() <= skipPastRow) { endStatesPerEndEvent.put(entry.getKey(), null); } } } } private void step(List<RegexNFAStateEntry> currentStates, EventBean theEvent, List<RegexNFAStateEntry> nextStates, List<RegexNFAStateEntry> endStates, boolean isRetainEventSet, int currentEventSequenceNumber, Object partitionKey) { for (RegexNFAStateEntry currentState : currentStates) { EventBean[] eventsPerStream = currentState.getEventsPerStream(); int currentStateStreamNum = currentState.getState().getStreamNum(); eventsPerStream[currentStateStreamNum] = theEvent; if (currentState.getState().matches(eventsPerStream, agentInstanceContext)) { if (isRetainEventSet) { this.windowMatchedEventset.add(theEvent); } List<RegexNFAState> nextStatesFromHere = currentState.getState().getNextStates(); // save state for each next state boolean copy = nextStatesFromHere.size() > 1; for (RegexNFAState next : nextStatesFromHere) { EventBean[] eventsForState = eventsPerStream; MultimatchState[] multimatches = currentState.getOptionalMultiMatches(); int[] greedyCounts = currentState.getGreedycountPerState(); if (copy) { eventsForState = new EventBean[eventsForState.length]; System.arraycopy(eventsPerStream, 0, eventsForState, 0, eventsForState.length); int[] greedyCountsCopy = new int[greedyCounts.length]; System.arraycopy(greedyCounts, 0, greedyCountsCopy, 0, greedyCounts.length); greedyCounts = greedyCountsCopy; if (isSelectAsksMultimatches) { multimatches = deepCopy(multimatches); } } if ((isSelectAsksMultimatches) && (currentState.getState().isMultiple())) { multimatches = addTag(currentState.getState().getStreamNum(), theEvent, multimatches); } if ((currentState.getState().isGreedy() != null) && (currentState.getState().isGreedy())) { greedyCounts[currentState.getState().getNodeNumFlat()]++; } RegexNFAStateEntry entry = new RegexNFAStateEntry(currentState.getMatchBeginEventSeqNo(), currentState.getMatchBeginEventTime(), next, eventsForState, greedyCounts, multimatches, partitionKey); if (next instanceof RegexNFAStateEnd) { entry.setMatchEndEventSeqNo(currentEventSequenceNumber); endStates.add(entry); } else { nextStates.add(entry); } } } } } private MultimatchState[] deepCopy(MultimatchState[] multimatchStates) { if (multimatchStates == null) { return null; } MultimatchState[] copy = new MultimatchState[multimatchStates.length]; for (int i = 0; i < copy.length; i++) { if (multimatchStates[i] != null) { copy[i] = new MultimatchState(multimatchStates[i]); } } return copy; } private MultimatchState[] addTag(int streamNum, EventBean theEvent, MultimatchState[] multimatches) { if (multimatches == null) { multimatches = new MultimatchState[variablesArray.length]; } MultimatchState state = multimatches[streamNum]; if (state == null) { multimatches[streamNum] = new MultimatchState(theEvent); return multimatches; } multimatches[streamNum].add(theEvent); return multimatches; } private String printStates(List<RegexNFAStateEntry> states) { StringBuilder buf = new StringBuilder(); String delimiter = ""; for (RegexNFAStateEntry state : states) { buf.append(delimiter); buf.append(state.getState().getNodeNumNested()); buf.append("{"); EventBean[] eventsPerStream = state.getEventsPerStream(); if (eventsPerStream == null) { buf.append("null"); } else { String eventDelimiter = ""; for (Map.Entry<Integer, String> streamVariable : streamsVariables.entrySet()) { buf.append(eventDelimiter); buf.append(streamVariable.getValue()); buf.append('='); if (variablesSingle.contains(streamVariable.getValue())) { if (eventsPerStream[streamVariable.getKey()] == null) { buf.append("null"); } else { buf.append(eventsPerStream[streamVariable.getKey()].getUnderlying()); } } else { int streamNum = state.getState().getStreamNum(); if (state.getOptionalMultiMatches() == null) { buf.append("null-mm"); } else if (state.getOptionalMultiMatches()[streamNum] == null) { buf.append("no-entry"); } else { buf.append("{"); String arrayEventDelimiter = ""; EventBean[] multiMatch = state.getOptionalMultiMatches()[streamNum].getBuffer(); int count = state.getOptionalMultiMatches()[streamNum].getCount(); for (int i = 0; i < count; i++) { buf.append(arrayEventDelimiter); buf.append(multiMatch[i].getUnderlying()); arrayEventDelimiter = ", "; } buf.append("}"); } } eventDelimiter = ", "; } } buf.append("}"); delimiter = ", "; } return buf.toString(); } private String print(RegexNFAState[] states) { StringWriter writer = new StringWriter(); PrintWriter buf = new PrintWriter(writer); Stack<RegexNFAState> currentStack = new Stack<RegexNFAState>(); print(Arrays.asList(states), buf, 0, currentStack); return writer.toString(); } private void print(List<RegexNFAState> states, PrintWriter writer, int indent, Stack<RegexNFAState> currentStack) { for (RegexNFAState state : states) { indent(writer, indent); if (currentStack.contains(state)) { writer.println("(self)"); } else { writer.println(printState(state)); currentStack.push(state); print(state.getNextStates(), writer, indent + 4, currentStack); currentStack.pop(); } } } private String printState(RegexNFAState state) { if (state instanceof RegexNFAStateEnd) { return "#" + state.getNodeNumNested(); } else { return "#" + state.getNodeNumNested() + " " + state.getVariableName() + " s" + state.getStreamNum() + " defined as " + state; } } private void indent(PrintWriter writer, int indent) { for (int i = 0; i < indent; i++) { writer.append(' '); } } private EventBean generateOutputRow(RegexNFAStateEntry entry) { // we first generate a raw row of <String, Object> for each variable name. Map<String, Object> rowDataRaw = new HashMap<String, Object>(); for (Map.Entry<String, Pair<Integer, Boolean>> variableDef : variableStreams.entrySet()) { if (!variableDef.getValue().getSecond()) { rowDataRaw.put(variableDef.getKey(), entry.getEventsPerStream()[variableDef.getValue().getFirst()]); } } if (aggregationService != null) { aggregationService.clearResults(); } if (entry.getOptionalMultiMatches() != null) { MultimatchState[] multimatchState = entry.getOptionalMultiMatches(); for (int i = 0; i < multimatchState.length; i++) { if (multimatchState[i] == null) { continue; } EventBean[] multimatchEvents = multimatchState[i].getEventArray(); rowDataRaw.put(variablesArray[i], multimatchEvents); if (aggregationService != null) { EventBean[] eventsPerStream = entry.getEventsPerStream(); for (EventBean multimatchEvent : multimatchEvents) { eventsPerStream[i] = multimatchEvent; aggregationService.applyEnter(eventsPerStream, i, agentInstanceContext); } } } } EventBean rowRaw = agentInstanceContext.getStatementContext().getEventAdapterService().adapterForTypedMap(rowDataRaw, compositeEventType); Map<String, Object> row = new HashMap<String, Object>(); int columnNum = 0; for (ExprEvaluator expression : columnEvaluators) { Object result = expression.evaluate(new EventBean[] {rowRaw}, true, agentInstanceContext); row.put(columnNames[columnNum], result); columnNum++; } return agentInstanceContext.getStatementContext().getEventAdapterService().adapterForTypedMap(row, rowEventType); } private void scheduleCallback(long msecAfterCurrentTime, RegexNFAStateEntry endState) { long matchBeginTime = endState.getMatchBeginEventTime(); if (schedule.isEmpty()) { schedule.put(matchBeginTime, endState); agentInstanceContext.getStatementContext().getSchedulingService().add(msecAfterCurrentTime, handle, scheduleSlot); } else { Object value = schedule.get(matchBeginTime); if (value == null) { long currentFirstKey = schedule.firstKey(); if (currentFirstKey > matchBeginTime) { agentInstanceContext.getStatementContext().getSchedulingService().remove(handle, scheduleSlot); agentInstanceContext.getStatementContext().getSchedulingService().add(msecAfterCurrentTime, handle, scheduleSlot); } schedule.put(matchBeginTime, endState); } else if (value instanceof RegexNFAStateEntry) { RegexNFAStateEntry valueEntry = (RegexNFAStateEntry) value; List<RegexNFAStateEntry> list = new ArrayList<RegexNFAStateEntry>(); list.add(valueEntry); list.add(endState); schedule.put(matchBeginTime, list); } else { List<RegexNFAStateEntry> list = (List<RegexNFAStateEntry>) value; list.add(endState); } } } private void triggered() { long currentTime = agentInstanceContext.getStatementContext().getSchedulingService().getTime(); if (schedule.isEmpty()) { return; } List<RegexNFAStateEntry> indicatables = new ArrayList<RegexNFAStateEntry>(); while (true) { long firstKey = schedule.firstKey(); long cutOffTime = currentTime - this.matchRecognizeSpec.getInterval().getMSec(); if (firstKey > cutOffTime) { break; } Object value = schedule.remove(firstKey); if (value instanceof RegexNFAStateEntry) { indicatables.add((RegexNFAStateEntry) value); } else { List<RegexNFAStateEntry> list = (List<RegexNFAStateEntry>) value; indicatables.addAll(list); } if (schedule.isEmpty()) { break; } } // schedule next if (!schedule.isEmpty()) { long msecAfterCurrentTime = schedule.firstKey() + this.matchRecognizeSpec.getInterval().getMSec() - agentInstanceContext.getStatementContext().getSchedulingService().getTime(); agentInstanceContext.getStatementContext().getSchedulingService().add(msecAfterCurrentTime, handle, scheduleSlot); } if (!matchRecognizeSpec.isAllMatches()) { indicatables = rankEndStatesMultiPartition(indicatables); } EventBean[] outBeans = new EventBean[indicatables.size()]; int count = 0; for (RegexNFAStateEntry endState : indicatables) { outBeans[count++] = generateOutputRow(endState); } updateChildren(outBeans, null); } }