/**************************************************************************************
* 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.pattern;
import com.espertech.esper.client.EventBean;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import java.util.ArrayList;
import java.util.Arrays;
/**
* 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;
}
@Override
public EvalNode getFactoryNode() {
return evalMatchUntilNode;
}
public final void start(MatchedEventMap 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);
}
initBounds();
if (stateMatcher != null) {
stateMatcher.start(beginState);
}
}
public final void evaluateTrue(MatchedEventMap matchEvent, EvalStateNode fromNode, boolean isQuitted)
{
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))
{
quit();
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
{
quit();
// 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);
}
else
{
this.getParentEvaluator().evaluateTrue(consolidated, this, true);
}
}
}
private 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 isMatcher = false;
if (fromNode == stateMatcher)
{
isMatcher = true;
}
if (isMatcher)
{
stateMatcher.quit();
stateMatcher = null;
}
else
{
stateUntil.quit();
stateUntil = null;
this.getParentEvaluator().evaluateFalse(this);
}
}
public final void quit()
{
if (stateMatcher != null)
{
stateMatcher.quit();
stateMatcher = null;
}
if (stateUntil != null)
{
stateUntil.quit();
stateUntil = null;
}
}
public final Object accept(EvalStateNodeVisitor visitor, Object data)
{
return visitor.visit(this, data);
}
public final Object childrenAccept(EvalStateNodeVisitor visitor, Object data)
{
if (stateMatcher != null) {
stateMatcher.accept(visitor, data);
}
if (stateUntil != null) {
stateUntil.accept(visitor, data);
}
return data;
}
public final String toString()
{
return "EvalMatchUntilStateNode";
}
public boolean isNotOperator() {
return false;
}
public boolean isFilterStateNode() {
return false;
}
public boolean isFilterChildNonQuitting() {
return true;
}
private boolean isTightlyBound() {
return lowerbounds != null && upperbounds != null && upperbounds.equals(lowerbounds);
}
private boolean isBounded() {
return lowerbounds != null || upperbounds != null;
}
protected void initBounds()
{
EventBean[] eventsPerStream = evalMatchUntilNode.getFactoryNode().getConvertor().convert(beginState);
if (evalMatchUntilNode.getFactoryNode().getLowerBounds() != null) {
lowerbounds = (Integer) evalMatchUntilNode.getFactoryNode().getLowerBounds().getExprEvaluator().evaluate(eventsPerStream, true, evalMatchUntilNode.getContext().getAgentInstanceContext());
}
if (evalMatchUntilNode.getFactoryNode().getUpperBounds() != null) {
upperbounds = (Integer) evalMatchUntilNode.getFactoryNode().getUpperBounds().getExprEvaluator().evaluate(eventsPerStream, true, evalMatchUntilNode.getContext().getAgentInstanceContext());
}
if (upperbounds != null && lowerbounds != null) {
if (upperbounds < lowerbounds) {
Integer lbounds = lowerbounds;
lowerbounds = upperbounds;
upperbounds = lbounds;
}
}
}
private static final Log log = LogFactory.getLog(EvalMatchUntilStateNode.class);
}