/*
***************************************************************************************
* 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 org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
/**
* Contains the state collected by an "every" operator. The state includes handles to any sub-listeners
* started by the operator.
*/
public class EvalEveryDistinctStateExpireKeyNode extends EvalStateNode implements Evaluator {
protected final EvalEveryDistinctNode everyNode;
protected final Map<EvalStateNode, LinkedHashMap<Object, Long>> spawnedNodes;
protected MatchedEventMap beginState;
/**
* Constructor.
*
* @param parentNode is the parent evaluator to call to indicate truth value
* @param everyNode is the factory node associated to the state
*/
public EvalEveryDistinctStateExpireKeyNode(Evaluator parentNode,
EvalEveryDistinctNode everyNode) {
super(parentNode);
this.everyNode = everyNode;
this.spawnedNodes = new LinkedHashMap<EvalStateNode, LinkedHashMap<Object, Long>>();
}
public void removeMatch(Set<EventBean> matchEvent) {
if (PatternConsumptionUtil.containsEvent(matchEvent, beginState)) {
quit();
this.getParentEvaluator().evaluateFalse(this, true);
} else {
PatternConsumptionUtil.childNodeRemoveMatches(matchEvent, spawnedNodes.keySet());
}
}
@Override
public EvalNode getFactoryNode() {
return everyNode;
}
public final void start(MatchedEventMap beginState) {
if (InstrumentationHelper.ENABLED) {
InstrumentationHelper.get().qPatternEveryDistinctStart(everyNode, beginState);
}
this.beginState = beginState.shallowCopy();
EvalStateNode childState = everyNode.getChildNode().newState(this, null, 0L);
spawnedNodes.put(childState, new LinkedHashMap<Object, Long>());
// During the start of the child we need to use the temporary evaluator to catch any event created during a start.
// Events created during the start would likely come from the "not" operator.
// Quit the new child again if
EvalEveryStateSpawnEvaluator spawnEvaluator = new EvalEveryStateSpawnEvaluator(everyNode.getContext().getPatternContext().getStatementName());
childState.setParentEvaluator(spawnEvaluator);
childState.start(beginState);
// If the spawned expression turned true already, just quit it
if (spawnEvaluator.isEvaluatedTrue()) {
childState.quit();
} else {
childState.setParentEvaluator(this);
}
if (InstrumentationHelper.ENABLED) {
InstrumentationHelper.get().aPatternEveryDistinctStart();
}
}
public final void evaluateFalse(EvalStateNode fromNode, boolean restartable) {
if (InstrumentationHelper.ENABLED) {
InstrumentationHelper.get().qPatternEveryDistinctEvalFalse(everyNode);
}
fromNode.quit();
spawnedNodes.remove(fromNode);
// Spawn all nodes below this EVERY node
// During the start of a child we need to use the temporary evaluator to catch any event created during a start
// Such events can be raised when the "not" operator is used.
EvalEveryStateSpawnEvaluator spawnEvaluator = new EvalEveryStateSpawnEvaluator(everyNode.getContext().getPatternContext().getStatementName());
EvalStateNode spawned = everyNode.getChildNode().newState(spawnEvaluator, null, 0L);
spawned.start(beginState);
// If the whole spawned expression already turned true, quit it again
if (spawnEvaluator.isEvaluatedTrue()) {
spawned.quit();
} else {
spawnedNodes.put(spawned, new LinkedHashMap<Object, Long>());
spawned.setParentEvaluator(this);
}
if (InstrumentationHelper.ENABLED) {
InstrumentationHelper.get().aPatternEveryDistinctEvalFalse();
}
}
public final void evaluateTrue(MatchedEventMap matchEvent, EvalStateNode fromNode, boolean isQuitted) {
if (InstrumentationHelper.ENABLED) {
InstrumentationHelper.get().qPatternEveryDistinctEvaluateTrue(everyNode, matchEvent);
}
// determine if this evaluation has been seen before from the same node
Object matchEventKey = PatternExpressionUtil.getKeys(matchEvent, everyNode.getFactoryNode().getConvertor(), everyNode.getFactoryNode().getDistinctExpressionsArray(), everyNode.getContext().getAgentInstanceContext());
boolean haveSeenThis = false;
LinkedHashMap<Object, Long> keysFromNode = spawnedNodes.get(fromNode);
if (keysFromNode != null) {
// Clean out old keys
Iterator<Map.Entry<Object, Long>> it = keysFromNode.entrySet().iterator();
long currentTime = everyNode.getContext().getPatternContext().getTimeProvider().getTime();
for (; it.hasNext(); ) {
Map.Entry<Object, Long> entry = it.next();
if (currentTime >= entry.getValue()) {
it.remove();
} else {
break;
}
}
if (keysFromNode.containsKey(matchEventKey)) {
haveSeenThis = true;
} else {
long expiryTime = everyNode.getFactoryNode().absExpiry(everyNode.getContext());
keysFromNode.put(matchEventKey, expiryTime);
}
}
if (isQuitted) {
spawnedNodes.remove(fromNode);
}
// See explanation in EvalFilterStateNode for the type check
if (fromNode.isFilterStateNode()) {
// We do not need to newState new listeners here, since the filter state node below this node did not quit
} else {
// Spawn all nodes below this EVERY node
// During the start of a child we need to use the temporary evaluator to catch any event created during a start
// Such events can be raised when the "not" operator is used.
EvalEveryStateSpawnEvaluator spawnEvaluator = new EvalEveryStateSpawnEvaluator(everyNode.getContext().getPatternContext().getStatementName());
EvalStateNode spawned = everyNode.getChildNode().newState(spawnEvaluator, null, 0L);
spawned.start(beginState);
// If the whole spawned expression already turned true, quit it again
if (spawnEvaluator.isEvaluatedTrue()) {
spawned.quit();
} else {
LinkedHashMap<Object, Long> keyset = new LinkedHashMap<Object, Long>();
if (keysFromNode != null) {
keyset.putAll(keysFromNode);
}
spawnedNodes.put(spawned, keyset);
spawned.setParentEvaluator(this);
}
}
if (!haveSeenThis) {
this.getParentEvaluator().evaluateTrue(matchEvent, this, false);
}
if (InstrumentationHelper.ENABLED) {
InstrumentationHelper.get().aPatternEveryDistinctEvaluateTrue(null, keysFromNode, matchEventKey, haveSeenThis);
}
}
public final void quit() {
if (InstrumentationHelper.ENABLED) {
InstrumentationHelper.get().qPatternEveryDistinctQuit(everyNode);
}
// Stop all child nodes
for (EvalStateNode child : spawnedNodes.keySet()) {
child.quit();
}
if (InstrumentationHelper.ENABLED) {
InstrumentationHelper.get().aPatternEveryDistinctQuit();
}
}
public final void accept(EvalStateNodeVisitor visitor) {
visitor.visitEveryDistinct(everyNode.getFactoryNode(), this, beginState, spawnedNodes.values());
for (EvalStateNode spawnedNode : spawnedNodes.keySet()) {
spawnedNode.accept(visitor);
}
}
public boolean isNotOperator() {
return false;
}
public boolean isFilterStateNode() {
return false;
}
public boolean isFilterChildNonQuitting() {
return true;
}
public boolean isObserverStateNodeNonRestarting() {
return false;
}
public final String toString() {
return "EvalEveryStateNode spawnedChildren=" + spawnedNodes.size();
}
private static final Logger log = LoggerFactory.getLogger(EvalEveryStateNode.class);
}