Package com.espertech.esper.epl.parse

Source Code of com.espertech.esper.epl.parse.EPLTreeWalker

/**************************************************************************************
* 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.epl.parse;

import com.espertech.esper.antlr.ASTUtil;
import com.espertech.esper.client.ConfigurationInformation;
import com.espertech.esper.client.EPException;
import com.espertech.esper.client.hook.AggregationFunctionFactory;
import com.espertech.esper.collection.Pair;
import com.espertech.esper.collection.UniformPair;
import com.espertech.esper.core.context.mgr.ContextManagementService;
import com.espertech.esper.core.context.util.ContextDescriptor;
import com.espertech.esper.core.service.EPAdministratorHelper;
import com.espertech.esper.epl.agg.AggregationAccessType;
import com.espertech.esper.epl.agg.AggregationSupport;
import com.espertech.esper.epl.core.*;
import com.espertech.esper.epl.db.DatabasePollingViewableFactory;
import com.espertech.esper.epl.declexpr.ExprDeclaredNodeImpl;
import com.espertech.esper.epl.expression.*;
import com.espertech.esper.epl.generated.EsperEPL2Ast;
import com.espertech.esper.epl.script.ExprNodeScript;
import com.espertech.esper.epl.spec.*;
import com.espertech.esper.epl.variable.VariableService;
import com.espertech.esper.pattern.EvalFactoryNode;
import com.espertech.esper.pattern.PatternNodeFactory;
import com.espertech.esper.pattern.guard.GuardEnum;
import com.espertech.esper.rowregex.*;
import com.espertech.esper.schedule.SchedulingService;
import com.espertech.esper.schedule.TimeProvider;
import com.espertech.esper.type.*;
import com.espertech.esper.type.StringValue;
import com.espertech.esper.util.CollectionUtil;
import com.espertech.esper.util.PlaceholderParseException;
import com.espertech.esper.util.PlaceholderParser;
import org.antlr.runtime.CommonTokenStream;
import org.antlr.runtime.tree.Tree;
import org.antlr.runtime.tree.TreeNodeStream;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import java.util.*;

/**
* Called during the walks of a EPL expression AST tree as specified in the grammar file.
* Constructs filter and view specifications etc.
*/
public class EPLTreeWalker extends EsperEPL2Ast
{
    // private holding areas for accumulated info
    private Map<Tree, ExprNode> astExprNodeMap = new HashMap<Tree, ExprNode>();
    private final Stack<Map<Tree, ExprNode>> astExprNodeMapStack;

    private final Map<Tree, EvalFactoryNode> astPatternNodeMap = new HashMap<Tree, EvalFactoryNode>();

    private final Map<Tree, RowRegexExprNode> astRowRegexNodeMap = new HashMap<Tree, RowRegexExprNode>();

    private final Map<Tree, Object> astGOPNodeMap = new HashMap<Tree, Object>();

    private FilterSpecRaw filterSpec;
    private final List<ViewSpec> viewSpecs = new LinkedList<ViewSpec>();

    // AST Walk result
    private List<ExprSubstitutionNode> substitutionParamNodes = new ArrayList<ExprSubstitutionNode>();
    private StatementSpecRaw statementSpec;
    private final Stack<StatementSpecRaw> statementSpecStack;

    private List<SelectClauseElementRaw> propertySelectRaw;
    private PropertyEvalSpec propertyEvalSpec;
    private List<OnTriggerMergeMatched> mergeMatcheds;
    private List<OnTriggerMergeAction> mergeActions;
    private Map<EvalFactoryNode, String> evalNodeExpressions;
    private ContextDescriptor contextDescriptor;

    private final EngineImportService engineImportService;
    private final VariableService variableService;
    private final TimeProvider timeProvider;
    private final ExprEvaluatorContext exprEvaluatorContext;
    private final SelectClauseStreamSelectorEnum defaultStreamSelector;
    private final String engineURI;
    private final ConfigurationInformation configurationInformation;
    private final SchedulingService schedulingService;
    private final PatternNodeFactory patternNodeFactory;
    private final ContextManagementService contextManagementService;
    private final CommonTokenStream tokenStream;
    private final List<String> scriptBodies;
    private final List<ExpressionScriptProvided> scriptExpressions;
    private final ExpressionDeclDesc expressionDeclarations;

    /**
     * Ctor.
     * @param engineImportService is required to resolve lib-calls into static methods or configured aggregation functions
     * @param variableService for variable access
     * @param input is the tree nodes to walk
     * @param defaultStreamSelector - the configuration for which insert or remove streams (or both) to produce
     * @param engineURI engine URI
     * @param configurationInformation configuration info
     */
    public EPLTreeWalker(TreeNodeStream input,
                         CommonTokenStream tokenStream,
                         EngineImportService engineImportService,
                         VariableService variableService,
                         SchedulingService schedulingService,
                         SelectClauseStreamSelectorEnum defaultStreamSelector,
                         String engineURI,
                         ConfigurationInformation configurationInformation,
                         PatternNodeFactory patternNodeFactory,
                         ContextManagementService contextManagementService,
                         List<String> scriptBodies)
    {
        super(input);
        this.tokenStream = tokenStream;
        this.engineImportService = engineImportService;
        this.variableService = variableService;
        this.defaultStreamSelector = defaultStreamSelector;
        this.timeProvider = schedulingService;
        this.patternNodeFactory = patternNodeFactory;
        this.exprEvaluatorContext = new ExprEvaluatorContextTimeOnly(timeProvider);
        this.engineURI = engineURI;
        this.configurationInformation = configurationInformation;
        this.schedulingService = schedulingService;
        this.contextManagementService = contextManagementService;
        this.scriptBodies = scriptBodies;

        if (defaultStreamSelector == null)
        {
            throw new IllegalArgumentException("Default stream selector is null");
        }

        statementSpec = new StatementSpecRaw(defaultStreamSelector);
        statementSpecStack = new Stack<StatementSpecRaw>();
        astExprNodeMapStack = new Stack<Map<Tree, ExprNode>>();
       
        // statement-global items
        expressionDeclarations = new ExpressionDeclDesc();
        statementSpec.setExpressionDeclDesc(expressionDeclarations);
        scriptExpressions = new ArrayList<ExpressionScriptProvided>();
        statementSpec.setScriptExpressions(scriptExpressions);
    }

    /**
     * Pushes a statement into the stack, creating a new empty statement to fill in.
     * The leave node method for lookup statements pops from the stack.
     * The leave node method for lookup statements pops from the stack.
     */
    protected void pushStmtContext() {
        if (log.isDebugEnabled())
        {
            log.debug(".pushStmtContext");
        }
        statementSpecStack.push(statementSpec);
        astExprNodeMapStack.push(astExprNodeMap);

        statementSpec = new StatementSpecRaw(defaultStreamSelector);
        astExprNodeMap = new HashMap<Tree, ExprNode>();
    }

    /**
     * Returns statement specification.
     * @return statement spec.
     */
    public StatementSpecRaw getStatementSpec()
    {
        return statementSpec;
    }

    /**
     * Leave AST node and process it's type and child nodes.
     * @param node is the node to complete
     * @throws ASTWalkException if the node tree walk operation failed
     */
    protected void leaveNode(Tree node) throws ASTWalkException
    {
        if (log.isDebugEnabled())
        {
            log.debug(".leaveNode " + node);
        }

        switch (node.getType())
        {
            case STREAM_EXPR:
                leaveStreamExpr(node);
                break;
            case EVENT_FILTER_EXPR:
                leaveStreamFilter(node);
                break;
            case PATTERN_FILTER_EXPR:
                leavePatternFilter(node);
                break;
            case PATTERN_INCL_EXPR:
                return;
            case VIEW_EXPR:
                leaveView(node);
                break;
            case SELECTION_EXPR:
                leaveSelectClause(node);
                break;
            case WILDCARD_SELECT:
              leaveWildcardSelect();
              break;
            case SELECTION_ELEMENT_EXPR:
                leaveSelectionElement(node);
                break;
            case SELECTION_STREAM:
                leaveSelectionStream(node);
                break;
            case PROPERTY_SELECTION_ELEMENT_EXPR:
                leavePropertySelectionElement(node);
                break;
            case PROPERTY_SELECTION_STREAM:
                leavePropertySelectionStream(node);
                break;
            case PROPERTY_WILDCARD_SELECT:
              leavePropertyWildcardSelect();
              break;
            case EVENT_FILTER_PROPERTY_EXPR_ATOM:
              leavePropertySelectAtom(node);
              break;
            case EVENT_PROP_EXPR:
                leaveEventPropertyExpr(node);
                break;
            case EVAL_AND_EXPR:
                leaveJoinAndExpr(node);
                break;
            case EVAL_OR_EXPR:
                leaveJoinOrExpr(node);
                break;
            case EVAL_EQUALS_EXPR:
            case EVAL_NOTEQUALS_EXPR:
            case EVAL_IS_EXPR:
            case EVAL_ISNOT_EXPR:
                leaveEqualsExpr(node);
                break;
            case EVAL_EQUALS_GROUP_EXPR:
            case EVAL_NOTEQUALS_GROUP_EXPR:
            case EVAL_IS_GROUP_EXPR:
            case EVAL_ISNOT_GROUP_EXPR:
                leaveEqualsGroupExpr(node);
                break;
            case WHERE_EXPR:
                leaveWhereClause();
                break;
            case NUM_INT:
            case INT_TYPE:
            case LONG_TYPE:
            case BOOL_TYPE:
            case FLOAT_TYPE:
            case DOUBLE_TYPE:
            case STRING_TYPE:
            case NULL_TYPE:
                leaveConstant(node);
                break;
            case SUBSTITUTION:
                leaveSubstitution(node);
                break;
            case STAR:
            case MINUS:
            case PLUS:
            case DIV:
            case MOD:
                leaveMath(node);
                break;
            case BAND:
            case BOR:
            case BXOR:
              leaveBitWise(node);
              break;
             case LT:
            case GT:
            case LE:
            case GE:
                leaveRelationalOp(node);
                break;
            case COALESCE:
                leaveCoalesce(node);
                break;
            case NOT_EXPR:
                leaveExprNot(node);
                break;
            case PATTERN_NOT_EXPR:
                leavePatternNot(node);
                break;
            case SUM:
            case AVG:
            case COUNT:
            case MEDIAN:
            case STDDEV:
            case AVEDEV:
            case FIRST_AGGREG:
            case LAST_AGGREG:
            case WINDOW_AGGREG:
                leaveAggregate(node);
                break;
            case DOT_EXPR:
              leaveDotExpr(node);
                break;
            case LIB_FUNC_CHAIN:
              leaveLibFunctionChain(node);
                break;
            case LEFT_OUTERJOIN_EXPR:
            case RIGHT_OUTERJOIN_EXPR:
            case FULL_OUTERJOIN_EXPR:
            case INNERJOIN_EXPR:
                leaveOuterInnerJoin(node);
                break;
            case GROUP_BY_EXPR:
                leaveGroupBy(node);
                break;
            case HAVING_EXPR:
                leaveHavingClause();
                break;
            case ORDER_BY_EXPR:
              break;
            case ORDER_ELEMENT_EXPR:
              leaveOrderByElement(node);
              break;
            case EVENT_LIMIT_EXPR:
            case TIMEPERIOD_LIMIT_EXPR:
            case CRONTAB_LIMIT_EXPR:
            case WHEN_LIMIT_EXPR:
            case TERM_LIMIT_EXPR:
            case AFTER_LIMIT_EXPR:
              leaveOutputLimit(node);
              break;
            case ROW_LIMIT_EXPR:
              leaveRowLimit(node);
              break;
            case INSERTINTO_EXPR:
              leaveInsertInto(node);
              break;
            case CONCAT:
              leaveConcat(node);
              break;
            case CASE:
                leaveCaseNode(node, false);
                break;
            case CASE2:
                leaveCaseNode(node, true);
                break;
            case EVERY_EXPR:
                leaveEvery(node);
                break;
            case EVERY_DISTINCT_EXPR:
                leaveEveryDistinct(node);
                break;
            case FOLLOWED_BY_EXPR:
                leaveFollowedBy(node);
                break;
            case OR_EXPR:
                leaveOr(node);
                break;
            case AND_EXPR:
                leaveAnd(node);
                break;
            case GUARD_EXPR:
                leaveGuard(node);
                break;
            case OBSERVER_EXPR:
                leaveObserver(node);
                break;
            case MATCH_UNTIL_EXPR:
                leaveMatch(node);
                break;
            case IN_SET:
            case NOT_IN_SET:
                leaveInSet(node);
                break;
            case IN_RANGE:
            case NOT_IN_RANGE:
                leaveInRange(node);
                break;
            case BETWEEN:
            case NOT_BETWEEN:
                leaveBetween(node);
                break;
            case LIKE:
            case NOT_LIKE:
                leaveLike(node);
                break;
            case REGEXP:
            case NOT_REGEXP:
                leaveRegexp(node);
                break;
            case PREVIOUS:
            case PREVIOUSTAIL:
            case PREVIOUSWINDOW:
            case PREVIOUSCOUNT:
                leavePrevious(node);
                break;
            case PRIOR:
                leavePrior(node);
                break;
            case ARRAY_EXPR:
                leaveArray(node);
                break;
            case SUBSELECT_EXPR:
                leaveSubselectRow(node);
                break;
            case EXISTS_SUBSELECT_EXPR:
                leaveSubselectExists(node);
                break;
            case IN_SUBSELECT_EXPR:
            case NOT_IN_SUBSELECT_EXPR:
                leaveSubselectIn(node);
                break;
            case IN_SUBSELECT_QUERY_EXPR:
                leaveSubselectQueryIn(node);
                break;
            case INSTANCEOF:
                leaveInstanceOf(node);
                break;
            case TYPEOF:
                leaveTypeOf(node);
                break;
            case EXISTS:
                leaveExists(node);
                break;
            case CAST:
                leaveCast(node);
                break;
            case CURRENT_TIMESTAMP:
                leaveTimestamp(node);
                break;
            case CREATE_WINDOW_EXPR:
                leaveCreateWindow(node);
                break;
            case CREATE_INDEX_EXPR:
                leaveCreateIndex(node);
                break;
            case CREATE_SCHEMA_EXPR:
                leaveCreateSchema(node);
                break;
            case CREATE_WINDOW_SELECT_EXPR:
                leaveCreateWindowSelect();
                break;
            case CREATE_VARIABLE_EXPR:
                leaveCreateVariable(node);
                break;
            case ON_EXPR:
                leaveOnExpr(node);
                break;
            case UPDATE_EXPR:
                leaveUpdateExpr(node);
                break;
            case TIME_PERIOD:
                leaveTimePeriod(node);
                break;
            case NUMBERSETSTAR:
                leaveNumberSetStar(node);
                break;
            case NUMERIC_PARAM_FREQUENCY:
                leaveNumberSetFrequency(node);
                break;
            case NUMERIC_PARAM_RANGE:
                leaveNumberSetRange(node);
                break;
            case NUMERIC_PARAM_LIST:
                leaveNumberSetList(node);
                break;
            case LAST_OPERATOR:
            case LAST:
                leaveLastNumberSetOperator(node);
                break;
            case LW:
                leaveLastWeekdayNumberSetOperator(node);
                break;
            case WEEKDAY_OPERATOR:
                leaveWeekdayNumberSetOperator(node);
                break;
            case OBJECT_PARAM_ORDERED_EXPR:
                leaveObjectParamOrderedExpression(node);
                break;
            case ANNOTATION:
                leaveAnnotation(node);
                break;
            case MATCHREC_MEASURE_ITEM:
                leaveMatchRecognizeMeasureItem(node);
                break;
            case MATCHREC_PATTERN:
                leaveMatchRecognizePattern(node);
                break;
            case MATCHREC_PATTERN_NESTED:
                leaveMatchRecognizePatternNested(node);
                break;
            case MATCHREC_PATTERN_CONCAT:
                leaveMatchRecognizePatternConcat(node);
                break;
            case MATCHREC_PATTERN_ALTER:
                leaveMatchRecognizePatternAlter(node);
                break;
            case MATCHREC_PATTERN_ATOM:
                leaveMatchRecognizePatternAtom(node);
                break;
            case MATCHREC_DEFINE_ITEM:
                leaveMatchRecognizeDefineItem(node);
                break;
            case PARTITIONITEM:
                leaveMatchRecognizePartition(node);
                break;
            case MATCH_RECOGNIZE:
                leaveMatchRecognize(node);
                break;
            case ON_SELECT_EXPR:
                leaveOnSelect(node);
                break;
            case ON_STREAM:
                leaveOnStream(node);
                break;
            case FOR:
                leaveForClause(node);
                break;
            case MERGE_MAT:
            case MERGE_UNM:
                leaveMergeMatchedUnmatched(node);
                break;
            case MERGE_DEL:
                leaveMergeDelClause(node);
                break;
            case MERGE_UPD:
                leaveMergeUpdClause(node);
                break;
            case MERGE_INS:
                leaveMergeInsClause(node);
                break;
            case EXPRESSIONDECL:
                leaveExpressionDecl(node);
                break;
            case NEWKW:
                leaveNewKeyword(node);
                break;
            case CONTEXT:
                leaveContext(node);
                break;
            case CREATE_CTX:
                leaveCreateContext(node);
                break;
            case CREATE_DATAFLOW:
                leaveCreateDataflow(node);
                break;
            case GOPCFGITM:
            case GOPCFGEXP:
            case GOPCFGEPL:
                leaveGraphDetail(node);
                break;
            case JSON_ARRAY:
            case JSON_OBJECT:
                leaveJsonConstant(node);
                break;
            default:
                throw new ASTWalkException("Unhandled node type encountered, type '" + node.getType() +
                        "' with text '" + node.getText() + '\'');
        }

        // For each AST child node of this AST node that generated an ExprNode add the child node to the expression node.
        // This is for automatic expression tree building.
        if (!astExprNodeMap.isEmpty())
        {
            mapChildASTToChildExprNode(node);
        }

        // For each AST child node of this AST node that generated an EvalNode add the EvalNode as a child
        if (!astPatternNodeMap.isEmpty())
        {
            EvalFactoryNode thisPatternNode = astPatternNodeMap.get(node);
            for (int i = 0; i < node.getChildCount(); i++)
            {
                Tree childNode = node.getChild(i);
                EvalFactoryNode childEvalNode = astPatternNodeMap.get(childNode);
                if (childEvalNode != null)
                {
                    thisPatternNode.addChildNode(childEvalNode);
                    astPatternNodeMap.remove(childNode);
                }
            }
        }

        // For each AST child node of this AST node that generated an RowRegexExprNode add the RowRegexExprNode as a child
        if (!astRowRegexNodeMap.isEmpty())
        {
            RowRegexExprNode thisRegexNode = astRowRegexNodeMap.get(node);
            for (int i = 0; i < node.getChildCount(); i++)
            {
                Tree childNode = node.getChild(i);
                RowRegexExprNode childEvalNode = astRowRegexNodeMap.get(childNode);
                if (childEvalNode != null)
                {
                    thisRegexNode.addChildNode(childEvalNode);
                    astRowRegexNodeMap.remove(childNode);
                }
            }
        }

        switch (node.getType())
        {
            case SUM:
            case AVG:
            case COUNT:
            case MEDIAN:
            case STDDEV:
            case AVEDEV:
            case FIRST_AGGREG:
            case LAST_AGGREG:
            case WINDOW_AGGREG:
                postLeaveAggregate(node);
                break;
            default:
                break;
        }
    }

    private void mapChildASTToChildExprNode(Tree node)
    {
        ExprNode thisEvalNode = astExprNodeMap.get(node);
        for (int i = 0; i < node.getChildCount(); i++)
        {
            Tree childNode = node.getChild(i);

            ExprNode childEvalNode = astExprNodeMap.get(childNode);
            // If there was an expression node generated for the child node, and there is a current expression node,
            // add it to the current expression node (thisEvalNode)
            if ((childEvalNode != null) && (thisEvalNode != null))
            {
                thisEvalNode.addChildNode(childEvalNode);
                astExprNodeMap.remove(childNode);
            }
        }
    }

    private void leaveCreateWindow(Tree node)
    {
        log.debug(".leaveCreateWindow");

        String windowName = node.getChild(0).getText();

        String eventName = null;
        Tree eventNameNode = ASTUtil.findFirstNode(node, CLASS_IDENT);
        if (eventNameNode != null)
        {
            eventName = eventNameNode.getText();
        }
        if (eventName == null)
        {
            eventName = "java.lang.Object";
        }

        boolean isRetainUnion = false;
        boolean isRetainIntersection = false;
        for (int i = 0; i < node.getChildCount(); i++)
        {
            if (node.getChild(i).getType() == RETAINUNION)
            {
                isRetainUnion = true;
                break;
            }
            if (node.getChild(i).getType() == RETAININTERSECTION)
            {
                isRetainIntersection = true;
                break;
            }
        }
        StreamSpecOptions streamSpecOptions = new StreamSpecOptions(false,isRetainUnion,isRetainIntersection);

        // handle table-create clause, i.e. (col1 type, col2 type)
        List<ColumnDesc> colums = ASTCreateSchemaHelper.getColTypeList(node);

        boolean isInsert = false;
        ExprNode insertWhereExpr = null;
        Tree insertNode = ASTUtil.findFirstNode(node, INSERT);
        if (insertNode != null)
        {
            isInsert = true;
            if (insertNode.getChildCount() > 0)
            {
                insertWhereExpr = ASTExprHelper.getRemoveExpr(insertNode.getChild(0)this.astExprNodeMap);
            }
        }

        CreateWindowDesc desc = new CreateWindowDesc(windowName, viewSpecs, streamSpecOptions, isInsert, insertWhereExpr, colums, eventName);
        statementSpec.setCreateWindowDesc(desc);

        // this is good for indicating what is being selected from
        FilterSpecRaw rawFilterSpec = new FilterSpecRaw(eventName, new LinkedList<ExprNode>(), null);
        FilterStreamSpecRaw streamSpec = new FilterStreamSpecRaw(rawFilterSpec, new LinkedList<ViewSpec>(), null, streamSpecOptions);
        statementSpec.getStreamSpecs().add(streamSpec);
    }

    private void leaveCreateIndex(Tree node)
    {
        log.debug(".leaveCreateIndex");

        String indexName = node.getChild(0).getText();
        String windowName = node.getChild(1).getText();

        Tree nodeExpr = node.getChild(2);
        List<CreateIndexItem> columns = new ArrayList<CreateIndexItem>();

        for (int i = 0; i < nodeExpr.getChildCount(); i++)
        {
            CreateIndexType type = CreateIndexType.HASH;
            Tree child = nodeExpr.getChild(i);
            if (child.getType() == INDEXCOL)
            {
                String columnName = child.getChild(0).getText();
                if (child.getChildCount() == 2) {
                    String typeName = child.getChild(1).getText();
                    try {
                        type = CreateIndexType.valueOf(typeName.toUpperCase());
                    }
                    catch (RuntimeException ex) {
                        throw new ASTWalkException("Invalid column index type '" + typeName + "' encountered, please use any of the following index type names " + Arrays.asList(CreateIndexType.values()));
                    }
                }
                columns.add(new CreateIndexItem(columnName, type));
            }
        }

        statementSpec.setCreateIndexDesc(new CreateIndexDesc(indexName, windowName, columns));
    }

    private void leaveCreateSchema(Tree node)
    {
        log.debug(".leaveCreateSchema");
        CreateSchemaDesc createSchema = ASTCreateSchemaHelper.walkCreateSchema(node);
        statementSpec.getStreamSpecs().add(new FilterStreamSpecRaw(new FilterSpecRaw(Object.class.getName(), Collections.<ExprNode>emptyList(), null), Collections.<ViewSpec>emptyList(), null, new StreamSpecOptions()));
        statementSpec.setCreateSchemaDesc(createSchema);
    }

    private void leaveCreateVariable(Tree node)
    {
        log.debug(".leaveCreateVariable");

        Tree child = node.getChild(0);
        String variableType = child.getText();
        child = node.getChild(1);
        String variableName = child.getText();

        int start = 2;
        boolean constant = false;
        if (node.getChildCount() > start && node.getChild(start).getType() == IDENT) {
            String text = node.getChild(start).getText().toLowerCase().trim();
            if (text.equals("constant") || text.equals("const")) {
                constant = true;
            }
            else {
                throw new EPException("Expected 'constant' or 'const' keyword after create for create-variable syntax but encountered '" + text + "'");
            }
            start++;
        }

        ExprNode assignment = null;
        if (node.getChildCount() > start) {
            child = node.getChild(start);
            assignment = astExprNodeMap.remove(child);
        }

        CreateVariableDesc desc = new CreateVariableDesc(variableType, variableName, assignment, constant);
        statementSpec.setCreateVariableDesc(desc);
    }

    private void leaveCreateWindowSelect()
    {
        log.debug(".leaveCreateWindowSelect");
    }

    private void leaveOnExpr(Tree node)
    {
        log.debug(".leaveOnExpr");

        // determine on-delete or on-select
        boolean isOnDelete = false;
        Tree typeChildNode = null;

        for (int i = 0; i < node.getChildCount(); i++)
        {
          Tree childNode = node.getChild(i);

            if (childNode.getType() == ON_DELETE_EXPR)
            {
                typeChildNode = childNode;
                isOnDelete = true;
            }
            else if (childNode.getType() == ON_SELECT_EXPR)
            {
                typeChildNode = childNode;
            }
            else if (childNode.getType() == ON_UPDATE_EXPR)
            {
                typeChildNode = childNode;
            }
            else if (childNode.getType() == ON_SET_EXPR)
            {
                typeChildNode = childNode;
            }
            else if (childNode.getType() == ON_MERGE_EXPR)
            {
                typeChildNode = childNode;
            }
        }
        if (typeChildNode == null)
        {
            throw new IllegalStateException("Could not determine on-expr type");
        }

        if (typeChildNode.getType() == ON_MERGE_EXPR) {
            String windowName = typeChildNode.getChild(0).getText();
            String asName = null;
            if (typeChildNode.getChild(1).getType() == IDENT) {
                asName = typeChildNode.getChild(1).getText();
            }

            OnTriggerMergeDesc desc = new OnTriggerMergeDesc(windowName, asName, mergeMatcheds == null ? Collections.<OnTriggerMergeMatched>emptyList() : mergeMatcheds);
            statementSpec.setOnTriggerDesc(desc);
        }
        else if (typeChildNode.getType() != ON_SET_EXPR)
        {
            // The ON_EXPR_FROM contains the window name
            UniformPair<String> windowName = getWindowName(typeChildNode);
            if (windowName == null)
            {
                // on the statement spec, the deepest spec is the outermost
                List<OnTriggerSplitStream> splitStreams = new ArrayList<OnTriggerSplitStream>();
                for (int i = 1; i <= statementSpecStack.size() - 1; i++)
                {
                    StatementSpecRaw raw = statementSpecStack.get(i);
                    splitStreams.add(new OnTriggerSplitStream(raw.getInsertIntoDesc(), raw.getSelectClauseSpec(), raw.getFilterExprRootNode()));
                }
                splitStreams.add(new OnTriggerSplitStream(statementSpec.getInsertIntoDesc(), statementSpec.getSelectClauseSpec(), statementSpec.getFilterExprRootNode()));
                if (!statementSpecStack.isEmpty())
                {
                    statementSpec = statementSpecStack.get(0);
                }
                boolean isFirst = isSelectInsertFirst(node);
                statementSpec.setOnTriggerDesc(new OnTriggerSplitStreamDesc(OnTriggerType.ON_SPLITSTREAM, isFirst, splitStreams));
                statementSpecStack.clear();
            }
            else if (typeChildNode.getType() == ON_UPDATE_EXPR) {
                List<OnTriggerSetAssignment> assignments = ASTExprHelper.getOnTriggerSetAssignments(typeChildNode, astExprNodeMap);
                statementSpec.setOnTriggerDesc(new OnTriggerWindowUpdateDesc(windowName.getFirst(), windowName.getSecond(), assignments));
                statementSpec.setFilterExprRootNode(getRemoveFirstByType(typeChildNode, WHERE_EXPR));
            }
            else
            {
                statementSpec.setOnTriggerDesc(new OnTriggerWindowDesc(windowName.getFirst(), windowName.getSecond(), isOnDelete ? OnTriggerType.ON_DELETE : OnTriggerType.ON_SELECT));
            }
        }
        else
        {
            List<OnTriggerSetAssignment> assignments = ASTExprHelper.getOnTriggerSetAssignments(typeChildNode, astExprNodeMap);
            statementSpec.setOnTriggerDesc(new OnTriggerSetDesc(assignments));
        }
    }

    private void leaveOnStream(Tree node)
    {
        log.debug(".leaveOnStream");

        // get optional filter stream as-name
        Tree childNode = node.getChild(1);
        String streamAsName = null;
        if ((childNode != null) && (childNode.getType() == IDENT))
        {
            streamAsName = childNode.getText();
        }

        // get stream to use (pattern or filter)
        StreamSpecRaw streamSpec;
        if (node.getChild(0).getType() == EVENT_FILTER_EXPR)
        {
            streamSpec = new FilterStreamSpecRaw(filterSpec, new ArrayList<ViewSpec>(), streamAsName, new StreamSpecOptions());
        }
        else if (node.getChild(0).getType() == PATTERN_INCL_EXPR)
        {
            if ((astPatternNodeMap.size() > 1) || ((astPatternNodeMap.isEmpty())))
            {
                throw new ASTWalkException("Unexpected AST tree contains zero or more then 1 child elements for root");
            }
            // Get expression node sub-tree from the AST nodes placed so far
            EvalFactoryNode evalNode = astPatternNodeMap.values().iterator().next();
            streamSpec = new PatternStreamSpecRaw(evalNode, evalNodeExpressions, viewSpecs, streamAsName, new StreamSpecOptions());
            if (evalNodeExpressions != null) {
                evalNodeExpressions = new HashMap<EvalFactoryNode, String>();
            }
            astPatternNodeMap.clear();
        }
        else
        {
            throw new IllegalStateException("Invalid AST type node, cannot map to stream specification");
        }

        statementSpec.getStreamSpecs().add(streamSpec);
    }

    private void leaveForClause(Tree node)
    {
        log.debug(".leaveForClause");

        if (statementSpec.getForClauseSpec() == null) {
            statementSpec.setForClauseSpec(new ForClauseSpec());
        }
        String ident = node.getChild(0).getText();
        List<ExprNode> expressions = ASTExprHelper.getExprNodes(node, 1, astExprNodeMap);
        statementSpec.getForClauseSpec().getClauses().add(new ForClauseItemSpec(ident, expressions));
    }

    private void leaveMergeMatchedUnmatched(Tree node)
    {
        log.debug(".leaveMergeMatchedUnmatched");

        boolean matched = node.getType() == MERGE_MAT;
        if (mergeMatcheds == null) {
            mergeMatcheds = new ArrayList<OnTriggerMergeMatched>();
        }
        ExprNode filterSpec = null;
        if (node.getChildCount() > 0) {
            filterSpec = ASTExprHelper.getRemoveExpr(node.getChild(node.getChildCount() - 1), astExprNodeMap);
        }
        mergeMatcheds.add(new OnTriggerMergeMatched(matched, filterSpec, mergeActions));
        mergeActions = null;
    }

    private void leaveMergeDelClause(Tree node)
    {
        log.debug(".leaveMergeDelClause");

        if (mergeActions == null) {
            mergeActions = new ArrayList<OnTriggerMergeAction>();
        }
        Tree whereCondNode = ASTUtil.findFirstNode(node, WHERE_EXPR);
        ExprNode whereCond = whereCondNode != null ? ASTExprHelper.getRemoveExpr(whereCondNode.getChild(0), astExprNodeMap) : null;
        mergeActions.add(new OnTriggerMergeActionDelete(whereCond));
    }

    private void leaveMergeUpdClause(Tree node)
    {
        log.debug(".leaveMergeUpdClause");

        if (mergeActions == null) {
            mergeActions = new ArrayList<OnTriggerMergeAction>();
        }
        Tree whereCondNode = ASTUtil.findFirstNode(node, WHERE_EXPR);
        ExprNode whereCond = whereCondNode != null ? ASTExprHelper.getRemoveExpr(whereCondNode.getChild(0), astExprNodeMap) : null;

        List<OnTriggerSetAssignment> sets = ASTExprHelper.getOnTriggerSetAssignments(node, astExprNodeMap);
        mergeActions.add(new OnTriggerMergeActionUpdate(whereCond, sets));
    }

    private void leaveMergeInsClause(Tree node)
    {
        log.debug(".leaveMergeInsClause");

        Tree whereCondNode = ASTUtil.findFirstNode(node, WHERE_EXPR);
        ExprNode whereCond = whereCondNode != null ? ASTExprHelper.getRemoveExpr(whereCondNode.getChild(0), astExprNodeMap) : null;

        List<SelectClauseElementRaw> expressions = new ArrayList<SelectClauseElementRaw>(statementSpec.getSelectClauseSpec().getSelectExprList());
        statementSpec.getSelectClauseSpec().getSelectExprList().clear();

        Tree optInsertNameNode = ASTUtil.findFirstNode(node, CLASS_IDENT);
        String optionalInsertName = optInsertNameNode != null ? optInsertNameNode.getText() : null;

        List<String> columsList = Collections.emptyList();
        for (int i = 0; i < node.getChildCount(); i++) {
            if (node.getChild(i).getType() == EXPRCOL) {
                columsList = ASTLibHelper.getIdentList(node.getChild(i));
            }
        }

        if (mergeActions == null) {
            mergeActions = new ArrayList<OnTriggerMergeAction>();
        }
        mergeActions.add(new OnTriggerMergeActionInsert(whereCond, optionalInsertName, columsList, expressions));
    }

    private void leaveUpdateExpr(Tree node)
    {
        log.debug(".leaveUpdateExpr");

        String eventTypeName = node.getChild(0).getText();
        FilterStreamSpecRaw streamSpec = new FilterStreamSpecRaw(new FilterSpecRaw(eventTypeName, Collections.<ExprNode>emptyList(), null), new ArrayList<ViewSpec>(), eventTypeName, new StreamSpecOptions());
        statementSpec.getStreamSpecs().add(streamSpec);

        String optionalStreamName = null;
        if ((node.getChildCount() > 1) && (node.getChild(1).getType() == IDENT))
        {
            optionalStreamName = node.getChild(1).getText();
        }

        List<OnTriggerSetAssignment> assignments = ASTExprHelper.getOnTriggerSetAssignments(node, astExprNodeMap);
        ExprNode whereClause = this.getRemoveFirstByType(node, WHERE_EXPR);
        statementSpec.setUpdateDesc(new UpdateDesc(optionalStreamName, assignments, whereClause));
    }

    private UniformPair<String> getWindowName(Tree typeChildNode)
    {
        String windowName = null;
        String windowStreamName = null;

        for (int i = 0; i < typeChildNode.getChildCount(); i++)
        {
          Tree child = typeChildNode.getChild(i);
            if (child.getType() == ON_EXPR_FROM)
            {
                windowName = child.getChild(0).getText();
                if (child.getChildCount() > 1)
                {
                    windowStreamName = child.getChild(1).getText();
                }
                break;
            }
        }
        if (windowName == null)
        {
            return null;
        }
        return new UniformPair<String>(windowName, windowStreamName);
    }


    private void leavePrevious(Tree node)
    {
        log.debug(".leavePrevious");

        PreviousType previousType;
        if (node.getType() == PREVIOUS) {
            previousType = PreviousType.PREV;
        }
        else if (node.getType() == PREVIOUSTAIL) {
            previousType = PreviousType.PREVTAIL;
        }
        else if (node.getType() == PREVIOUSWINDOW) {
            previousType = PreviousType.PREVWINDOW;
        }
        else if (node.getType() == PREVIOUSCOUNT) {
            previousType = PreviousType.PREVCOUNT;
        }
        else {
            throw new IllegalStateException("Failed to handle type '" + node.getType() + "'");
        }

        ExprPreviousNode previousNode = new ExprPreviousNode(previousType);
        astExprNodeMap.put(node, previousNode);
    }

    private void leavePrior(Tree node)
    {
        log.debug(".leavePrior");

        ExprPriorNode priorNode = new ExprPriorNode();
        astExprNodeMap.put(node, priorNode);
    }

    private void leaveInstanceOf(Tree node)
    {
        log.debug(".leaveInstanceOf");

        // get class identifiers
        List<String> classes = new ArrayList<String>();
        for (int i = 1; i < node.getChildCount(); i++)
        {
            Tree classIdent = node.getChild(i);
            classes.add(classIdent.getText());
        }

        String idents[] = classes.toArray(new String[classes.size()]);
        ExprInstanceofNode instanceofNode = new ExprInstanceofNode(idents);
        astExprNodeMap.put(node, instanceofNode);
    }

    private void leaveTypeOf(Tree node)
    {
        log.debug(".leaveTypeOf");
        ExprTypeofNode typeofNode = new ExprTypeofNode();
        astExprNodeMap.put(node, typeofNode);
    }

    private void leaveExists(Tree node)
    {
        log.debug(".leaveExists");

        ExprPropertyExistsNode instanceofNode = new ExprPropertyExistsNode();
        astExprNodeMap.put(node, instanceofNode);
    }

    private void leaveCast(Tree node)
    {
        log.debug(".leaveCast");

        String classIdent = node.getChild(1).getText();
        ExprCastNode castNode = new ExprCastNode(classIdent);
        astExprNodeMap.put(node, castNode);
    }

    private void leaveTimestamp(Tree node)
    {
        log.debug(".leaveTimestamp");

        ExprTimestampNode timeNode = new ExprTimestampNode();
        astExprNodeMap.put(node, timeNode);
    }

    private void leaveTimePeriod(Tree node)
    {
        log.debug(".leaveTimePeriod");

        ExprTimePeriod timeNode = ASTExprHelper.getTimePeriodExpr(node, astExprNodeMap);
        astExprNodeMap.put(node, timeNode);
    }

    private void leaveNumberSetStar(Tree node)
    {
        log.debug(".leaveNumberSetStar");
        ExprNumberSetWildcard exprNode = new ExprNumberSetWildcard();
        astExprNodeMap.put(node, exprNode);
    }

    private void leaveNumberSetFrequency(Tree node)
    {
        log.debug(".leaveNumberSetFrequency");
        ExprNumberSetFrequency exprNode = new ExprNumberSetFrequency();
        astExprNodeMap.put(node, exprNode);
    }

    private void leaveNumberSetRange(Tree node)
    {
        log.debug(".leaveNumberSetRange");
        ExprNumberSetRange exprNode = new ExprNumberSetRange();
        astExprNodeMap.put(node, exprNode);
    }

    private void leaveNumberSetList(Tree node)
    {
        log.debug(".leaveNumberSetList");
        ExprNumberSetList exprNode = new ExprNumberSetList();
        astExprNodeMap.put(node, exprNode);
    }

    private void leaveLastNumberSetOperator(Tree node)
    {
        log.debug(".leaveLastNumberSetOperator");
        ExprNumberSetCronParam exprNode = new ExprNumberSetCronParam(CronOperatorEnum.LASTDAY);
        astExprNodeMap.put(node, exprNode);
    }

    private void leaveLastWeekdayNumberSetOperator(Tree node)
    {
        log.debug(".leaveLastWeekdayNumberSetOperator");
        ExprNumberSetCronParam exprNode = new ExprNumberSetCronParam(CronOperatorEnum.LASTWEEKDAY);
        astExprNodeMap.put(node, exprNode);
    }

    private void leaveWeekdayNumberSetOperator(Tree node)
    {
        log.debug(".leaveWeekdayNumberSetOperator");
        ExprNumberSetCronParam exprNode = new ExprNumberSetCronParam(CronOperatorEnum.WEEKDAY);
        astExprNodeMap.put(node, exprNode);
    }

    private void leaveObjectParamOrderedExpression(Tree node)
    {
        log.debug(".leaveObjectParamOrderedExpression");

        boolean isDescending = false;
        if ((node.getChildCount() > 1) && (node.getChild(1).getText().toUpperCase().equals("DESC")))
        {
            isDescending = true;
        }
        ExprOrderedExpr exprNode = new ExprOrderedExpr(isDescending);
        astExprNodeMap.put(node, exprNode);
    }

    private void leaveAnnotation(Tree node)
    {
        log.debug(".leaveAnnotation");
        statementSpec.getAnnotations().add(ASTAnnotationHelper.walk(node, this.engineImportService));
    }

    private void leaveArray(Tree node)
    {
        log.debug(".leaveArray");

        ExprArrayNode arrayNode = new ExprArrayNode();
        astExprNodeMap.put(node, arrayNode);
    }

    private void leaveSubselectRow(Tree node)
    {
        log.debug(".leaveSubselectRow");

        StatementSpecRaw currentSpec = popStacks();
        ExprSubselectRowNode subselectNode = new ExprSubselectRowNode(currentSpec);
        astExprNodeMap.put(node, subselectNode);
    }

    private void leaveSubselectExists(Tree node)
    {
        log.debug(".leaveSubselectExists");

        StatementSpecRaw currentSpec = popStacks();
        ExprSubselectNode subselectNode = new ExprSubselectExistsNode(currentSpec);
        astExprNodeMap.put(node, subselectNode);
    }

    private void leaveSubselectIn(Tree node)
    {
        log.debug(".leaveSubselectIn");

        Tree nodeSubquery = node.getChild(1);

        boolean isNot = false;
        if (node.getType() == NOT_IN_SUBSELECT_EXPR)
        {
            isNot = true;
        }

        ExprSubselectInNode subqueryNode = (ExprSubselectInNode) astExprNodeMap.remove(nodeSubquery);
        subqueryNode.setNotIn(isNot);

        astExprNodeMap.put(node, subqueryNode);
    }

    private void leaveSubselectQueryIn(Tree node)
    {
        log.debug(".leaveSubselectQueryIn");

        StatementSpecRaw currentSpec = popStacks();
        ExprSubselectNode subselectNode = new ExprSubselectInNode(currentSpec);
        astExprNodeMap.put(node, subselectNode);
    }

    private StatementSpecRaw popStacks()
    {
        log.debug(".popStacks");

        StatementSpecRaw currentSpec = statementSpec;
        statementSpec = statementSpecStack.pop();

        if (currentSpec.isHasVariables())
        {
            statementSpec.setHasVariables(true);
        }
        if (currentSpec.getReferencedVariables() != null) {
            for (String var : currentSpec.getReferencedVariables()) {
                addVariable(statementSpec, var);
            }
        }

        astExprNodeMap = astExprNodeMapStack.pop();

        return currentSpec;
    }

    /**
     * End processing of the AST tree for stand-alone pattern expressions.
     * @throws ASTWalkException is the walk failed
     */
    protected void endPattern() throws ASTWalkException
    {
        log.debug(".endPattern");

        if ((astPatternNodeMap.size() > 1) || ((astPatternNodeMap.isEmpty())))
        {
            throw new ASTWalkException("Unexpected AST tree contains zero or more then 1 child elements for root");
        }

        // Get expression node sub-tree from the AST nodes placed so far
        EvalFactoryNode evalNode = astPatternNodeMap.values().iterator().next();

        PatternStreamSpecRaw streamSpec = new PatternStreamSpecRaw(evalNode, evalNodeExpressions, new LinkedList<ViewSpec>(), null, new StreamSpecOptions());
        if (evalNodeExpressions != null) {
            evalNodeExpressions = new HashMap<EvalFactoryNode, String>();
        }
        statementSpec.getStreamSpecs().add(streamSpec);
        statementSpec.setSubstitutionParameters(substitutionParamNodes);

        astPatternNodeMap.clear();
    }

    /**
     * End processing of the AST tree, check that expression nodes found their homes.
     * @throws ASTWalkException is the walk failed
     */
    protected void end() throws ASTWalkException
    {
        log.debug(".end");

        if (astExprNodeMap.size() > 1)
        {
            throw new ASTWalkException("Unexpected AST tree contains left over child elements," +
                    " not all expression nodes have been removed from AST-to-expression nodes map");
        }
        if (astPatternNodeMap.size() > 1)
        {
            throw new ASTWalkException("Unexpected AST tree contains left over child elements," +
                    " not all pattern nodes have been removed from AST-to-pattern nodes map");
        }

        statementSpec.setSubstitutionParameters(substitutionParamNodes);
    }

    private void leaveSelectionElement(Tree node) throws ASTWalkException
    {
        log.debug(".leaveSelectionElement");

        if ((astExprNodeMap.size() > 1) || ((astExprNodeMap.isEmpty())))
        {
            throw new ASTWalkException("Unexpected AST tree contains zero or more then 1 child element for root");
        }

        // Get expression node sub-tree from the AST nodes placed so far
        ExprNode exprNode = astExprNodeMap.values().iterator().next();
        astExprNodeMap.clear();

        // Get list element name
        String optionalName = null;
        if (node.getChildCount() > 1)
        {
            optionalName = node.getChild(1).getText();
        }

        // Add as selection element
        statementSpec.getSelectClauseSpec().add(new SelectClauseExprRawSpec(exprNode, optionalName));
    }

    private void leavePropertySelectionElement(Tree node) throws ASTWalkException
    {
        log.debug(".leavePropertySelectionElement");

        if ((astExprNodeMap.size() > 1) || ((astExprNodeMap.isEmpty())))
        {
            throw new ASTWalkException("Unexpected AST tree contains zero or more then 1 child element for root");
        }

        // Get expression node sub-tree from the AST nodes placed so far
        ExprNode exprNode = astExprNodeMap.values().iterator().next();
        astExprNodeMap.clear();

        // Get list element name
        String optionalName = null;
        if (node.getChildCount() > 1)
        {
            optionalName = node.getChild(1).getText();
        }

        // Add as selection element
        if (propertySelectRaw == null)
        {
            propertySelectRaw = new ArrayList<SelectClauseElementRaw>();
        }
        this.propertySelectRaw.add(new SelectClauseExprRawSpec(exprNode, optionalName));
    }

    private void leavePropertySelectionStream(Tree node) throws ASTWalkException
    {
        log.debug(".leavePropertySelectionStream");

        String streamName = node.getChild(0).getText();

        // Get element name
        String optionalName = null;
        if (node.getChildCount() > 1)
        {
            optionalName = node.getChild(1).getText();
        }

        // Add as selection element
        if (propertySelectRaw == null)
        {
            propertySelectRaw = new ArrayList<SelectClauseElementRaw>();
        }
        this.propertySelectRaw.add(new SelectClauseStreamRawSpec(streamName, optionalName));
    }

    private void leaveSelectionStream(Tree node) throws ASTWalkException
    {
        log.debug(".leaveSelectionStream");

        String streamName = node.getChild(0).getText();

        // Get element name
        String optionalName = null;
        if (node.getChildCount() > 1)
        {
            optionalName = node.getChild(1).getText();
        }

        // Add as selection element
        statementSpec.getSelectClauseSpec().add(new SelectClauseStreamRawSpec(streamName, optionalName));
    }

    private void leaveWildcardSelect()
    {
      log.debug(".leaveWildcardSelect");
        statementSpec.getSelectClauseSpec().add(new SelectClauseElementWildcard());
    }

    private void leavePropertyWildcardSelect()
    {
      log.debug(".leavePropertyWildcardSelect");
        if (propertySelectRaw == null)
        {
            propertySelectRaw = new ArrayList<SelectClauseElementRaw>();
        }
        this.propertySelectRaw.add(new SelectClauseElementWildcard());
    }

    private void leavePropertySelectAtom(Tree node)
    {
      log.debug(".leavePropertySelectAtom");

        // initialize if not set
        if (propertyEvalSpec == null)
        {
            propertyEvalSpec = new PropertyEvalSpec();
        }

        // get select clause
        SelectClauseSpecRaw optionalSelectClause = new SelectClauseSpecRaw();
        if (propertySelectRaw != null)
        {
            optionalSelectClause.getSelectExprList().addAll(propertySelectRaw);
            propertySelectRaw = null;
        }

        // get the splitter expression
        ExprNode splitterExpression;
        if (node.getChild(0).getType() == SELECT) {
            splitterExpression = astExprNodeMap.remove(node.getChild(1));
        }
        else {
            splitterExpression = astExprNodeMap.remove(node.getChild(0));
        }

        // get where-clause, if any
        Tree optionalWhereClauseTree = ASTUtil.findFirstNode(node, WHERE_EXPR);
        ExprNode optionalWhereClause = optionalWhereClauseTree == null ? null : astExprNodeMap.remove(optionalWhereClauseTree.getChild(0));

        Tree propertyAsNameTree = ASTUtil.findFirstNode(node, IDENT);
        String optionalAsName = propertyAsNameTree == null ? null : propertyAsNameTree.getText();

        String splitterEventTypeName = null;
        Tree splitterEventTypeNameNode = ASTUtil.findFirstNode(node, ATCHAR);
        if (splitterEventTypeNameNode != null && splitterEventTypeNameNode.getChild(0).getText().equals("type")) {
            splitterEventTypeName = splitterEventTypeNameNode.getChild(1).getText();
        }

        PropertyEvalAtom atom = new PropertyEvalAtom(splitterExpression, splitterEventTypeName, optionalAsName, optionalSelectClause, optionalWhereClause);
        propertyEvalSpec.add(atom);
    }

    private void leaveView(Tree node) throws ASTWalkException
    {
        log.debug(".leaveView");
        String objectNamespace = node.getChild(0).getText();
        String objectName = node.getChild(1).getText();
        List<ExprNode> viewParameters = ASTExprHelper.getExprNodes(node, 2, astExprNodeMap);
        viewSpecs.add(new ViewSpec(objectNamespace, objectName, viewParameters));
    }

    private void leaveMatchRecognizeMeasureItem(Tree node) throws ASTWalkException
    {
        log.debug(".leaveMatchRecognizeMeasureItem");

        if (statementSpec.getMatchRecognizeSpec() == null)
        {
            statementSpec.setMatchRecognizeSpec(new MatchRecognizeSpec());
        }

        Tree currentNode = node.getChild(0);
        ExprNode exprNode = astExprNodeMap.get(currentNode);
        if (exprNode == null)
        {
            throw new IllegalStateException("Expression node for AST node not found for type " + currentNode.getType() + " and text " + currentNode.getText());
        }
        astExprNodeMap.remove(currentNode);

        String name = null;
        if (node.getChildCount() > 1)
        {
            name = node.getChild(1).getText();
        }
        statementSpec.getMatchRecognizeSpec().addMeasureItem(new MatchRecognizeMeasureItem(exprNode, name));
    }

    private void leaveMatchRecognizePatternAtom(Tree node) throws ASTWalkException
    {
        log.debug(".leaveMatchRecognizePatternAtom");

        String first = node.getChild(0).getText();
        RegexNFATypeEnum type = RegexNFATypeEnum.SINGLE;
        if (node.getChildCount() > 2)
        {
            type = RegexNFATypeEnum.fromString(node.getChild(1).getText(), node.getChild(2).getText());
        }
        else if (node.getChildCount() > 1)
        {
            type = RegexNFATypeEnum.fromString(node.getChild(1).getText(), null);
        }

        RowRegexExprNodeAtom item = new RowRegexExprNodeAtom(first, type);
        astRowRegexNodeMap.put(node, item);
    }

    private void leaveMatchRecognizePatternAlter(Tree node) throws ASTWalkException
    {
        log.debug(".leaveMatchRecognizePatternAlter");

        RowRegexExprNodeAlteration alterNode = new RowRegexExprNodeAlteration();
        astRowRegexNodeMap.put(node, alterNode);
    }

    private void leaveMatchRecognizePatternConcat(Tree node) throws ASTWalkException
    {
        RowRegexExprNodeConcatenation concatNode = new RowRegexExprNodeConcatenation();
        astRowRegexNodeMap.put(node, concatNode);
    }

    private void leaveMatchRecognizePatternNested(Tree node) throws ASTWalkException
    {
        RegexNFATypeEnum type = RegexNFATypeEnum.SINGLE;
        if (node.getChildCount() > 2)
        {
            type = RegexNFATypeEnum.fromString(node.getChild(1).getText(), node.getChild(2).getText());
        }
        else if (node.getChildCount() > 1)
        {
            type = RegexNFATypeEnum.fromString(node.getChild(1).getText(), null);
        }
        RowRegexExprNodeNested nestedNode = new RowRegexExprNodeNested(type);
        astRowRegexNodeMap.put(node, nestedNode);
    }

    private void leaveMatchRecognizePattern(Tree node) throws ASTWalkException
    {
        Tree currentNode = node.getChild(0);
        RowRegexExprNode exprNode = this.astRowRegexNodeMap.get(currentNode);
        if (exprNode == null)
        {
            throw new IllegalStateException("Expression node for AST node not found for type " + currentNode.getType() + " and text " + currentNode.getText());
        }
        astRowRegexNodeMap.remove(currentNode);
        statementSpec.getMatchRecognizeSpec().setPattern(exprNode);
    }

    private void leaveMatchRecognizeDefineItem(Tree node) throws ASTWalkException
    {
        log.debug(".leaveMatchRecognizeDefineItem");
        String first = node.getChild(0).getText();

        Tree currentNode = node.getChild(1);
        ExprNode exprNode = astExprNodeMap.get(currentNode);
        if (exprNode == null)
        {
            throw new IllegalStateException("Expression node for AST node not found for type " + currentNode.getType() + " and text " + currentNode.getText());
        }
        astExprNodeMap.remove(currentNode);
        statementSpec.getMatchRecognizeSpec().getDefines().add(new MatchRecognizeDefineItem(first, exprNode));
    }

    private void leaveMatchRecognize(Tree node) throws ASTWalkException
    {
        log.debug(".leaveMatchRecognize");

        boolean allMatches = false;
        for (int i = 0; i < node.getChildCount(); i++)
        {
            if (node.getChild(i).getType() == ALL)
            {
                allMatches = true;
            }
        }

        MatchRecognizeSkipEnum skip;
        for (int i = 0; i < node.getChildCount(); i++)
        {
            if (node.getChild(i).getType() == MATCHREC_AFTER_SKIP)
            {
                skip = ASTMatchRecognizeHelper.parseSkip(node.getChild(i));
                statementSpec.getMatchRecognizeSpec().getSkip().setSkip(skip);
            }
        }

        for (int i = 0; i < node.getChildCount(); i++)
        {
            if (node.getChild(i).getType() == MATCHREC_INTERVAL)
            {
                Tree intervalParent = node.getChild(i);
                if (!intervalParent.getChild(0).getText().toLowerCase().equals("interval"))
                {
                    throw new ASTWalkException("Invalid interval-clause within match-recognize, expecting keyword INTERVAL");
                }
                ExprNode expression = astExprNodeMap.remove(intervalParent.getChild(1));
                ExprTimePeriod timePeriodExpr;
                try {
                    ExprValidationContext validationContext = new ExprValidationContext(new StreamTypeServiceImpl(engineURI, false), null, null, timeProvider, variableService, exprEvaluatorContext, null, null, null, null, null);
                    timePeriodExpr = (ExprTimePeriod) ExprNodeUtility.getValidatedSubtree(expression, validationContext);
                }
                catch (ExprValidationException ex)
                {
                    throw new ASTWalkException("Invalid interval-clause within match-recognize: " + ex.getMessage(), ex);
                }
                statementSpec.getMatchRecognizeSpec().setInterval(new MatchRecognizeInterval(timePeriodExpr));
            }
        }

        statementSpec.getMatchRecognizeSpec().setAllMatches(allMatches);
    }

    private void leaveOnSelect(Tree node) throws ASTWalkException
    {
        log.debug(".leaveOnSelect");

        for (int i = 0; i < node.getChildCount(); i++)
        {
            if (node.getChild(i).getType() == DISTINCT)
            {
                statementSpec.getSelectClauseSpec().setDistinct(true);
            }
        }
    }

    private void leaveMatchRecognizePartition(Tree node) throws ASTWalkException
    {
        log.debug(".leaveMatchRecognizePartition");
        if (statementSpec.getMatchRecognizeSpec() == null)
        {
            statementSpec.setMatchRecognizeSpec(new MatchRecognizeSpec());
        }
        statementSpec.getMatchRecognizeSpec().getPartitionByExpressions().addAll(ASTExprHelper.getExprNodes(node, 0, astExprNodeMap));
    }

    private void leaveStreamExpr(Tree node)
    {
        log.debug(".leaveStreamExpr");

        // Determine the optional stream name
        // Search for identifier node that carries the stream name in an "from Class.win:time().std:getEnumerationSource() as StreamName"
        Tree streamNameNode = null;
        for (int i = 1; i < node.getChildCount(); i++)
        {
            Tree child = node.getChild(i);
            if (child.getType() == IDENT)
            {
                streamNameNode = child;
                break;
            }
        }
        String streamName = null;
        if (streamNameNode != null)
        {
            streamName = streamNameNode.getText();
        }

        // The first child node may be a "stream" keyword
        boolean isUnidirectional = false;
        boolean isRetainUnion = false;
        boolean isRetainIntersection = false;
        for (int i = 0; i < node.getChildCount(); i++)
        {
            if (node.getChild(i).getType() == UNIDIRECTIONAL)
            {
                isUnidirectional = true;
                break;
            }
            if (node.getChild(i).getType() == RETAINUNION)
            {
                isRetainUnion = true;
                break;
            }
            if (node.getChild(i).getType() == RETAININTERSECTION)
            {
                isRetainIntersection = true;
                break;
            }
        }

        // Convert to a stream specification instance
        StreamSpecRaw streamSpec;
        StreamSpecOptions options = new StreamSpecOptions(isUnidirectional, isRetainUnion, isRetainIntersection);

        // If the first subnode is a filter node, we have a filter stream specification
        if (node.getChild(0).getType() == EVENT_FILTER_EXPR)
        {
            streamSpec = new FilterStreamSpecRaw(filterSpec, viewSpecs, streamName, options);
        }
        else if (node.getChild(0).getType() == PATTERN_INCL_EXPR)
        {
            if ((astPatternNodeMap.size() > 1) || ((astPatternNodeMap.isEmpty())))
            {
                throw new ASTWalkException("Unexpected AST tree contains zero or more then 1 child elements for root");
            }

            // Get expression node sub-tree from the AST nodes placed so far
            EvalFactoryNode evalNode = astPatternNodeMap.values().iterator().next();

            streamSpec = new PatternStreamSpecRaw(evalNode, evalNodeExpressions, viewSpecs, streamName, options);
            if (evalNodeExpressions != null) {
                evalNodeExpressions = new HashMap<EvalFactoryNode, String>();
            }
            astPatternNodeMap.clear();
        }
        else if (node.getChild(0).getType() == DATABASE_JOIN_EXPR)
        {
            Tree dbrootNode = node.getChild(0);
            String dbName = dbrootNode.getChild(0).getText();
            String sqlWithParams = StringValue.parseString(dbrootNode.getChild(1).getText().trim());

            // determine if there is variables used
            List<PlaceholderParser.Fragment> sqlFragments;
            try
            {
                sqlFragments = PlaceholderParser.parsePlaceholder(sqlWithParams);
                for (PlaceholderParser.Fragment fragment : sqlFragments)
                {
                    if (!(fragment instanceof PlaceholderParser.ParameterFragment)) {
                        continue;
                    }

                    // Parse expression, store for substitution parameters
                    String expression = fragment.getValue();
                    if (expression.toUpperCase().equals(DatabasePollingViewableFactory.SAMPLE_WHERECLAUSE_PLACEHOLDER)) {
                        continue;
                    }

                    if (expression.trim().length() == 0) {
                        throw new ASTWalkException("Missing expression within ${...} in SQL statement");
                    }
                    String toCompile = "select * from java.lang.Object where " + expression;
                    StatementSpecRaw raw = EPAdministratorHelper.compileEPL(toCompile, expression, false, null, SelectClauseStreamSelectorEnum.ISTREAM_ONLY,
                            engineImportService, variableService, schedulingService, engineURI, configurationInformation, patternNodeFactory, contextManagementService);

                    if ((raw.getSubstitutionParameters() != null) && (raw.getSubstitutionParameters().size() > 0)) {
                        throw new ASTWalkException("EPL substitution parameters are not allowed in SQL ${...} expressions, consider using a variable instead");
                    }

                    if (raw.isHasVariables()) {
                        statementSpec.setHasVariables(true);
                    }

                    // add expression
                    if (statementSpec.getSqlParameters() == null) {
                        statementSpec.setSqlParameters(new HashMap<Integer, List<ExprNode>>());
                    }
                    List<ExprNode> listExp = statementSpec.getSqlParameters().get(statementSpec.getStreamSpecs().size());
                    if (listExp == null) {
                        listExp = new ArrayList<ExprNode>();
                        statementSpec.getSqlParameters().put(statementSpec.getStreamSpecs().size(), listExp);
                    }
                    listExp.add(raw.getFilterRootNode());
                }
            }
            catch (PlaceholderParseException ex)
            {
                log.warn("Failed to parse SQL text '" + sqlWithParams + "' :" + ex.getMessage());
                // Let the view construction handle the validation
            }

            String sampleSQL = null;
            if (dbrootNode.getChildCount() > 2)
            {
                sampleSQL = dbrootNode.getChild(2).getText();
                sampleSQL = StringValue.parseString(sampleSQL.trim());
            }

            streamSpec = new DBStatementStreamSpec(streamName, viewSpecs, dbName, sqlWithParams, sampleSQL);
        }
        else if (node.getChild(0).getType() == METHOD_JOIN_EXPR)
        {
            Tree methodRootNode = node.getChild(0);
            String prefixIdent = methodRootNode.getChild(0).getText();
            String className = methodRootNode.getChild(1).getText();

            int indexDot = className.lastIndexOf('.');
            String classNamePart;
            String methodNamePart;
            if (indexDot == -1)
            {
                classNamePart = className;
                methodNamePart = null;
            }
            else
            {
                classNamePart = className.substring(0, indexDot);
                methodNamePart = className.substring(indexDot + 1);
            }
            List<ExprNode> exprNodes = ASTExprHelper.getExprNodes(methodRootNode, 2, astExprNodeMap);

            streamSpec = new MethodStreamSpec(streamName, viewSpecs, prefixIdent, classNamePart, methodNamePart, exprNodes);
        }
        else
        {
            throw new ASTWalkException("Unexpected AST child node to stream expression, type=" + node.getChild(0).getType());
        }
        viewSpecs.clear();
        statementSpec.getStreamSpecs().add(streamSpec);
    }

    private void leaveEventPropertyExpr(Tree node)
    {
        log.debug(".leaveEventPropertyExpr");

        if (node.getChildCount() == 0)
        {
            throw new IllegalStateException("Empty event property expression encountered");
        }

        ExprNode exprNode;
        String propertyName;

        // The stream name may precede the event property name, but cannot be told apart from the property name:
        //      s0.p1 could be a nested property, or could be stream 's0' and property 'p1'

        // A single entry means this must be the property name.
        // And a non-simple property means that it cannot be a stream name.
        if ((node.getChildCount() == 1) || (node.getChild(0).getType() != EVENT_PROP_SIMPLE))
        {
            propertyName = ASTFilterSpecHelper.getPropertyName(node, 0);
            exprNode = new ExprIdentNodeImpl(propertyName);

            Pair<String, String> mappedPropertyPair = ASTFilterSpecHelper.getMappedPropertyPair(node);
            if (mappedPropertyPair != null) {
                ExpressionScriptProvided script = findScript(mappedPropertyPair.getFirst(), 1);
                if (script != null) {
                    exprNode = new ExprNodeScript(configurationInformation.getEngineDefaults().getScripts().getDefaultDialect(), script,
                            Collections.<ExprNode>singletonList(new ExprConstantNodeImpl(mappedPropertyPair.getSecond())));
                }
            }
        }
        // --> this is more then one child node, and the first child node is a simple property
        // we may have a stream name in the first simple property, or a nested property
        // i.e. 's0.p0' could mean that the event has a nested property to 's0' of name 'p0', or 's0' is the stream name
        else
        {
            String leadingIdentifier = node.getChild(0).getChild(0).getText();
            String streamOrNestedPropertyName = ASTFilterSpecHelper.escapeDot(leadingIdentifier);
            propertyName = ASTFilterSpecHelper.getPropertyName(node, 1);

            if (variableService.getReader(leadingIdentifier) != null)
            {
                exprNode = new ExprVariableNodeImpl(leadingIdentifier + "." + propertyName, variableService);
                statementSpec.setHasVariables(true);
                addVariable(statementSpec, propertyName);
            }
            else if (contextDescriptor != null && contextDescriptor.getContextPropertyRegistry().isContextPropertyPrefix(streamOrNestedPropertyName)) {
                exprNode = new ExprContextPropertyNode(propertyName);
            }
            else {
                exprNode = new ExprIdentNodeImpl(propertyName, streamOrNestedPropertyName);
            }
        }

        if (variableService.getReader(propertyName) != null)
        {
            exprNode = new ExprVariableNodeImpl(propertyName, variableService);
            statementSpec.setHasVariables(true);
            addVariable(statementSpec, propertyName);
        }

        astExprNodeMap.put(node, exprNode);
    }

    private void addVariable(StatementSpecRaw statementSpec, String propertyName) {
        if (statementSpec.getReferencedVariables() == null) {
            statementSpec.setReferencedVariables(new HashSet<String>());
        }
        statementSpec.getReferencedVariables().add(propertyName);
    }

    private void leaveLibFunctionOld(Tree parent, Tree node)
    {
      log.debug(".leaveLibFunctionOld");

        String childNodeText = node.getChild(0).getText();
        if (node.getChild(0).getType() == CLASS_IDENT)
        {
            String className = node.getChild(0).getText();
            List<ExprChainedSpec> chained = getLibFuncChain(parent);
            chained.add(0, new ExprChainedSpec(className, Collections.<ExprNode>emptyList(), true));
            astExprNodeMap.put(node, new ExprDotNode(chained, configurationInformation.getEngineDefaults().getExpression().isDuckTyping(), configurationInformation.getEngineDefaults().getExpression().isUdfCache()));
            return;
        }

        boolean isDistinct = false;
        if ((node.getChild(1) != null) && (node.getChild(1).getType() == DISTINCT))
        {
            isDistinct = true;
        }

        // try plug-in single-row function
        try
        {
            Pair<Class, EngineImportSingleRowDesc> classMethodPair = engineImportService.resolveSingleRow(childNodeText);
            List<ExprChainedSpec> spec = new ArrayList<ExprChainedSpec>();
            List<ExprNode> childExpressions = ASTLibHelper.getExprNodesLibFunc(0, node, astExprNodeMap);
            spec.add(new ExprChainedSpec(classMethodPair.getSecond().getMethodName(), childExpressions, true));
            astExprNodeMap.put(node, new ExprPlugInSingleRowNode(childNodeText, classMethodPair.getFirst(), spec, classMethodPair.getSecond()));
            return;
        }
        catch (EngineImportUndefinedException e)
        {
            // Not an single-row function
        }
        catch (EngineImportException e)
        {
            throw new IllegalStateException("Error resolving single-row function: " + e.getMessage(), e);
        }

        // try plug-in aggregation function
        try
        {
            AggregationFunctionFactory aggregationFactory = engineImportService.resolveAggregationFactory(childNodeText);
            astExprNodeMap.put(node, new ExprPlugInAggFunctionFactoryNode(isDistinct, aggregationFactory, childNodeText));
            return;
        }
        catch (EngineImportUndefinedException e)
        {
            // Not an aggretaion function
        }
        catch (EngineImportException e)
        {
            throw new IllegalStateException("Error resolving aggregation: " + e.getMessage(), e);
        }

        // try plug-in aggregation function (AggregationSupport, deprecated)
        try
        {
            AggregationSupport aggregation = engineImportService.resolveAggregation(childNodeText);
            astExprNodeMap.put(node, new ExprPlugInAggFunctionNode(isDistinct, aggregation, childNodeText));
            return;
        }
        catch (EngineImportUndefinedException e)
        {
            // Not an aggretaion function
        }
        catch (EngineImportException e)
        {
            throw new IllegalStateException("Error resolving aggregation: " + e.getMessage(), e);
        }

        // special case for min,max
        if ((childNodeText.toLowerCase().equals("max")) || (childNodeText.toLowerCase().equals("min")) ||
            (childNodeText.toLowerCase().equals("fmax")) || (childNodeText.toLowerCase().equals("fmin")))
        {
            handleMinMax(node);
            return;
        }

        // try built-in expanded set of aggregation functions
        ExprNode extentedBuiltIn = engineImportService.resolveAggExtendedBuiltin(childNodeText, isDistinct);
        if (extentedBuiltIn != null)
        {
            astExprNodeMap.put(node, extentedBuiltIn);
            return;
        }

        // try expression declaration local statement
        if (!expressionDeclarations.getExpressions().isEmpty()) {
            String name = node.getChild(0).getText();
            List<ExprChainedSpec> chained = getLibFuncChain(parent);
            for (ExpressionDeclItem declNode : expressionDeclarations.getExpressions()) {
                if (declNode.getName().equals(name)) {
                    astExprNodeMap.put(node, new ExprDeclaredNodeImpl(declNode, chained.get(0).getParameters()));
                    return;
                }
            }
        }

        // try scripting expression
        if (!scriptExpressions.isEmpty()) {
            String name = node.getChild(0).getText();
            List<ExprChainedSpec> chained = getLibFuncChain(parent);
            ExpressionScriptProvided script = findScript(name, chained.get(0).getParameters().size());
            if (script != null) {
                astExprNodeMap.put(node, new ExprNodeScript(configurationInformation.getEngineDefaults().getScripts().getDefaultDialect(), script, chained.get(0).getParameters()));
                return;
            }
        }

        // Could be a mapped property with an expression-parameter "mapped(expr)" or array property with an expression-parameter "array(expr)".
        List<ExprChainedSpec> spec = new ArrayList<ExprChainedSpec>();
        List<ExprNode> childExpressions = ASTLibHelper.getExprNodesLibFunc(0, node, astExprNodeMap);
        spec.add(new ExprChainedSpec(childNodeText, childExpressions, false));
        astExprNodeMap.put(node, new ExprDotNode(spec, false, false));
    }

    private ExpressionScriptProvided findScript(String name, int parameterCount) {
        if (scriptExpressions.isEmpty()) {
            return null;
        }
        ExpressionScriptProvided nameMatchedScript = null;
        for (ExpressionScriptProvided script : scriptExpressions) {
            if (script.getName().equals(name) && script.getParameterNames().size() == parameterCount) {
                return script;
            }
            if (script.getName().equals(name)) {
                nameMatchedScript = script;
            }
        }
        return nameMatchedScript;
    }

    private void leaveDotExpr(Tree node)
    {
      log.debug(".leaveDotExpr");
        List<ExprChainedSpec> chainSpec = getLibFuncChain(node);
        astExprNodeMap.put(node, new ExprDotNode(chainSpec, configurationInformation.getEngineDefaults().getExpression().isDuckTyping(),
                configurationInformation.getEngineDefaults().getExpression().isUdfCache()));
    }

    private void leaveLibFunctionChain(Tree node)
    {
      log.debug(".leaveLibFunctionChain");

        // Single chain can include a class name or property name.
        // As the current node does not generate any expression for this 1-element chain, forward expression to this node.
        if (node.getChildCount() == 1) {
            leaveLibFunctionOld(node, node.getChild(0));
            mapChildASTToChildExprNode(node.getChild(0));
            ExprNode generated = astExprNodeMap.remove(node.getChild(0));
            astExprNodeMap.put(node, generated);
            return;
        }

        String className = node.getChild(0).getChild(0).getText();
        List<ExprChainedSpec> chained = this.getLibFuncChain(node);

        // try plug-in single-row function
        try
        {
            Pair<Class, EngineImportSingleRowDesc> classMethodPair = engineImportService.resolveSingleRow(className);
            chained.get(0).setName(classMethodPair.getSecond().getMethodName());
            astExprNodeMap.put(node, new ExprPlugInSingleRowNode(className, classMethodPair.getFirst(), chained, classMethodPair.getSecond()));
            return;
        }
        catch (EngineImportUndefinedException e)
        {
            // Not an single-row function
        }
        catch (EngineImportException e)
        {
            throw new IllegalStateException("Error resolving single-row function: " + e.getMessage(), e);
        }

        // if the class name is the first
        boolean duckType = configurationInformation.getEngineDefaults().getExpression().isDuckTyping();
        boolean udfCache = configurationInformation.getEngineDefaults().getExpression().isUdfCache();
        if (!className.equals(chained.get(0).getName())) {
            chained.add(0, new ExprChainedSpec(className, Collections.<ExprNode>emptyList(), true));
        }

        ExprDotNode dotNode = new ExprDotNode(chained, duckType, udfCache);

        // try expression declaration local statement
        if (!expressionDeclarations.getExpressions().isEmpty()) {
            String name = chained.get(0).getName();
            for (ExpressionDeclItem declNode : expressionDeclarations.getExpressions()) {
                if (declNode.getName().equals(name)) {
                    dotNode.addChildNode(new ExprDeclaredNodeImpl(declNode, chained.get(0).getParameters()));
                    chained.remove(0);
                    break;
                }
            }
        }

        // try scripting expression
        if (!scriptExpressions.isEmpty()) {
            String name = chained.get(0).getName();
            ExpressionScriptProvided script = findScript(name, chained.get(0).getParameters().size());
            if (script != null) {
                dotNode.addChildNode(new ExprNodeScript(configurationInformation.getEngineDefaults().getScripts().getDefaultDialect(), script, chained.get(0).getParameters()));
                chained.remove(0);
            }
        }

        astExprNodeMap.put(node, dotNode);
    }

    private void leaveEqualsExpr(Tree node)
    {
        log.debug(".leaveEqualsExpr");

        boolean isNot = false;
        if (node.getType() == EVAL_NOTEQUALS_EXPR || node.getType() == EVAL_ISNOT_EXPR)
        {
            isNot = true;
        }

        boolean isIs = false;
        if (node.getType() == EVAL_IS_EXPR || node.getType() == EVAL_ISNOT_EXPR)
        {
            isIs = true;
        }

        ExprEqualsNode identNode = new ExprEqualsNodeImpl(isNot, isIs);
        astExprNodeMap.put(node, identNode);
    }

    private void leaveEqualsGroupExpr(Tree node)
    {
        log.debug(".leaveEqualsGroupExpr");

        boolean isNot = false;
        if (node.getType() == EVAL_NOTEQUALS_GROUP_EXPR)
        {
            isNot = true;
        }

        boolean isAll = false;
        if (node.getChild(1).getType() == ALL)
        {
            isAll = true;
        }

        if ((node.getChildCount() > 2) && (node.getChild(2).getType() == SUBSELECT_GROUP_EXPR))
        {
            StatementSpecRaw currentSpec = popStacks();
            ExprSubselectAllSomeAnyNode subselectNode = new ExprSubselectAllSomeAnyNode(currentSpec, isNot, isAll, null);
            astExprNodeMap.put(node, subselectNode);
        }
        else
        {
            ExprEqualsAllAnyNode groupNode = new ExprEqualsAllAnyNode(isNot, isAll);
            astExprNodeMap.put(node, groupNode);
        }
    }

    private void leaveJoinAndExpr(Tree node)
    {
        log.debug(".leaveJoinAndExpr");
        ExprAndNode identNode = new ExprAndNodeImpl();
        astExprNodeMap.put(node, identNode);
    }

    private void leaveJoinOrExpr(Tree node)
    {
        log.debug(".leaveJoinOrExpr");
        ExprOrNode identNode = new ExprOrNode();
        astExprNodeMap.put(node, identNode);
    }

    private void leaveConstant(Tree node)
    {
        log.debug(".leaveConstant value '" + node.getText() + "'");
        ExprConstantNode constantNode = new ExprConstantNodeImpl(ASTConstantHelper.parse(node));
        astExprNodeMap.put(node, constantNode);
    }

    private void leaveJsonConstant(Tree node)
    {
        log.debug(".leaveJsonConstant value '" + node.getText() + "'");
        ExprConstantNode constantNode = new ExprConstantNodeImpl(ASTJsonHelper.walk(node));
        astExprNodeMap.put(node, constantNode);
    }

    private void leaveSubstitution(Tree node)
    {
        log.debug(".leaveSubstitution");

        // Add the substitution parameter node, for later replacement
        int currentSize = this.substitutionParamNodes.size();
        ExprSubstitutionNode substitutionNode = new ExprSubstitutionNode(currentSize + 1);
        substitutionParamNodes.add(substitutionNode);

        astExprNodeMap.put(node, substitutionNode);
    }

    private void leaveMath(Tree node)
    {
        log.debug(".leaveMath");

        MathArithTypeEnum mathArithTypeEnum;

        switch (node.getType())
        {
            case DIV :
                mathArithTypeEnum = MathArithTypeEnum.DIVIDE;
                break;
            case STAR :
                mathArithTypeEnum = MathArithTypeEnum.MULTIPLY;
                break;
            case PLUS :
                mathArithTypeEnum = MathArithTypeEnum.ADD;
                break;
            case MINUS :
                mathArithTypeEnum = MathArithTypeEnum.SUBTRACT;
                break;
            case MOD :
                mathArithTypeEnum = MathArithTypeEnum.MODULO;
                break;
            default :
                throw new IllegalArgumentException("Node type " + node.getType() + " not a recognized math node type");
        }

        ExprMathNode mathNode = new ExprMathNode(mathArithTypeEnum,
                configurationInformation.getEngineDefaults().getExpression().isIntegerDivision(),
                configurationInformation.getEngineDefaults().getExpression().isDivisionByZeroReturnsNull());
        astExprNodeMap.put(node, mathNode);
    }

    // Min/Max nodes can be either an aggregate or a per-row function depending on the number or arguments
    private void handleMinMax(Tree libNode)
    {
        log.debug(".handleMinMax");

        // Determine min or max
        Tree childNode = libNode.getChild(0);
        String childNodeText = childNode.getText().toLowerCase();
        MinMaxTypeEnum minMaxTypeEnum;
        boolean filtered = childNodeText.startsWith("f");
        if (childNodeText.equals("min") || childNodeText.equals("fmin"))
        {
            minMaxTypeEnum = MinMaxTypeEnum.MIN;
        }
        else if (childNodeText.equals("max") || childNodeText.equals("fmax"))
        {
            minMaxTypeEnum = MinMaxTypeEnum.MAX;
        }
        else
        {
            throw new IllegalArgumentException("Node type " + childNode.getType() + ' ' + childNode.getText() + " not a recognized min max node");
        }

        // Determine distinct or not
        Tree nextNode = libNode.getChild(1);
        boolean isDistinct = false;
        if (nextNode.getType() == DISTINCT)
        {
            isDistinct = true;
        }

        // Error if more then 3 nodes with distinct since it's an aggregate function
        if ((libNode.getChildCount() > 4) && (isDistinct) && !filtered)
        {
            throw new ASTWalkException("The distinct keyword is not valid in per-row min and max " +
                    "functions with multiple sub-expressions");
        }

        ExprNode minMaxNode;
        if ((!isDistinct) && (libNode.getChildCount() > 3) && !filtered)
        {
            // use the row function
            minMaxNode = new ExprMinMaxRowNode(minMaxTypeEnum);
        }
        else
        {
            // use the aggregation function
            minMaxNode = new ExprMinMaxAggrNode(isDistinct, minMaxTypeEnum, filtered);
        }
        astExprNodeMap.put(libNode, minMaxNode);
    }

    private void leaveCoalesce(Tree node)
    {
        log.debug(".leaveCoalesce");

        ExprNode coalesceNode = new ExprCoalesceNode();
        astExprNodeMap.put(node, coalesceNode);
    }

    private void leaveAggregate(Tree node)
    {
        log.debug(".leaveAggregate");

        boolean isDistinct = false;
        if ((node.getChild(0) != null) && (node.getChild(0).getType() == DISTINCT))
        {
            isDistinct = true;
        }

        // NOTE: Also see "postLeaveAggregate" below which appends the filter expression
        boolean hasFilter = ASTUtil.findFirstNode(node, AGG_FILTER_EXPR) != null;

        ExprAggregateNode aggregateNode;
        ExprNode childNode = null;

        switch (node.getType())
        {
            case AVG:
                aggregateNode = new ExprAvgNode(isDistinct, hasFilter);
                break;
            case SUM:
                aggregateNode = new ExprSumNode(isDistinct, hasFilter);
                break;
            case COUNT:
                aggregateNode = new ExprCountNode(isDistinct, hasFilter);
                break;
            case MEDIAN:
                aggregateNode = new ExprMedianNode(isDistinct, hasFilter);
                break;
            case STDDEV:
                aggregateNode = new ExprStddevNode(isDistinct, hasFilter);
                break;
            case AVEDEV:
                aggregateNode = new ExprAvedevNode(isDistinct, hasFilter);
                break;
            case FIRST_AGGREG:
            case WINDOW_AGGREG:
            case LAST_AGGREG:
                boolean isWildcard = false;
                String streamWildcard = null;
                if (node.getChildCount() > 0 && node.getChild(0).getType() == ACCESS_AGG) {
                    Tree wildcardNode = ASTUtil.findFirstNode(node.getChild(0), PROPERTY_WILDCARD_SELECT);
                    Tree streamWCNode = ASTUtil.findFirstNode(node.getChild(0), PROPERTY_SELECTION_STREAM);
                    if (wildcardNode != null) {
                        isWildcard = true;
                    }
                    else if (streamWCNode != null) {
                        streamWildcard = streamWCNode.getChild(0).getText();
                    }
                    else {
                        childNode = astExprNodeMap.remove(node.getChild(0).getChild(0));
                    }
                }
                else // no parameter case
                    isWildcard = true;
                }

                if (node.getType() == FIRST_AGGREG) {
                    aggregateNode = new ExprAccessAggNode(AggregationAccessType.FIRST, isWildcard, streamWildcard);
                }
                else if (node.getType() == WINDOW_AGGREG) {
                    aggregateNode = new ExprAccessAggNode(AggregationAccessType.WINDOW, isWildcard, streamWildcard);
                }
                else {
                    aggregateNode = new ExprAccessAggNode(AggregationAccessType.LAST, isWildcard, streamWildcard);
                }
                break;
            default:
                throw new IllegalArgumentException("Node type " + node.getType() + " not a recognized aggregate node type");
        }

        if (childNode != null) {
            aggregateNode.addChildNode(childNode);
        }
        astExprNodeMap.put(node, aggregateNode);
    }

    private void postLeaveAggregate(Tree node)
    {
        Tree optionalFilterNode = ASTUtil.findFirstNode(node, AGG_FILTER_EXPR);
        if (optionalFilterNode == null) {
            return;
        }
        ExprNode currentAggNode = astExprNodeMap.get(node);
        ExprNode filter = astExprNodeMap.remove(optionalFilterNode.getChild(0));
        currentAggNode.addChildNode(filter);
    }

    private void leaveRelationalOp(Tree node)
    {
        log.debug(".leaveRelationalOp");

        RelationalOpEnum relationalOpEnum;

        switch (node.getType())
        {
            case LT :
                relationalOpEnum = RelationalOpEnum.LT;
                break;
            case GT :
                relationalOpEnum = RelationalOpEnum.GT;
                break;
            case LE :
                relationalOpEnum = RelationalOpEnum.LE;
                break;
            case GE :
                relationalOpEnum = RelationalOpEnum.GE;
                break;
            default :
                throw new IllegalArgumentException("Node type " + node.getType() + " not a recognized relational op node type");
        }

        boolean isAll = false;
        boolean isAny = false;
        if (node.getChild(1).getType() == ALL)
        {
            isAll = true;
        }
        if ((node.getChild(1).getType() == ANY) || (node.getChild(1).getType() == SOME))
        {
            isAny = true;
        }

        ExprNode result;
        if (isAll || isAny)
        {
            if ((node.getChildCount() > 2) && (node.getChild(2).getType() == SUBSELECT_GROUP_EXPR))
            {
                StatementSpecRaw currentSpec = popStacks();
                result = new ExprSubselectAllSomeAnyNode(currentSpec, false, isAll, relationalOpEnum);
            }
            else
            {
                result = new ExprRelationalOpAllAnyNode(relationalOpEnum, isAll);
            }
        }
        else
        {
            result = new ExprRelationalOpNodeImpl(relationalOpEnum);
        }

        astExprNodeMap.put(node, result);
    }

    private void leaveBitWise(Tree node)
    {
        log.debug(".leaveBitWise");

        BitWiseOpEnum bitWiseOpEnum;
        switch (node.getType())
        {
          case BAND :
            bitWiseOpEnum = BitWiseOpEnum.BAND;
              break;
          case BOR :
            bitWiseOpEnum = BitWiseOpEnum.BOR;
              break;
          case BXOR :
            bitWiseOpEnum = BitWiseOpEnum.BXOR;
              break;
          default :
              throw new IllegalArgumentException("Node type " + node.getType() + " not a recognized bit wise node type");
        }

      ExprBitWiseNode bwNode = new ExprBitWiseNode(bitWiseOpEnum);
      astExprNodeMap.put(node, bwNode);
    }

    private void leaveWhereClause()
    {
        log.debug(".leaveWhereClause");

        if (astExprNodeMap.size() != 1)
        {
            throw new IllegalStateException("Where clause generated zero or more then one expression nodes");
        }

        // Just assign the single root ExprNode not consumed yet
        statementSpec.setFilterRootNode(astExprNodeMap.values().iterator().next());
        astExprNodeMap.clear();
    }

    private void leaveHavingClause()
    {
        log.debug(".leaveHavingClause");

        if (astExprNodeMap.size() != 1)
        {
            throw new IllegalStateException("Having clause generated zero or more then one expression nodes");
        }

        // Just assign the single root ExprNode not consumed yet
        statementSpec.setHavingExprRootNode(astExprNodeMap.values().iterator().next());
        astExprNodeMap.clear();
    }

    private void leaveOutputLimit(Tree node) throws ASTWalkException
    {
        log.debug(".leaveOutputLimit");

        OutputLimitSpec spec = ASTOutputLimitHelper.buildOutputLimitSpec(node, astExprNodeMap, variableService, engineURI, timeProvider, exprEvaluatorContext);
        statementSpec.setOutputLimitSpec(spec);

        if (spec.getVariableName() != null)
        {
            statementSpec.setHasVariables(true);
            addVariable(statementSpec, spec.getVariableName());
        }
    }

    private void leaveRowLimit(Tree node) throws ASTWalkException
    {
        log.debug(".leaveRowLimit");

        RowLimitSpec spec = ASTOutputLimitHelper.buildRowLimitSpec(node);
        statementSpec.setRowLimitSpec(spec);

        if ((spec.getNumRowsVariable() != null) || (spec.getOptionalOffsetVariable() != null))
        {
            statementSpec.setHasVariables(true);
            addVariable(statementSpec, spec.getOptionalOffsetVariable());
        }
    }

    private void leaveOuterInnerJoin(Tree node)
    {
        log.debug(".leaveOuterInnerJoin");

        OuterJoinType joinType;
        switch (node.getType())
        {
            case LEFT_OUTERJOIN_EXPR:
                joinType = OuterJoinType.LEFT;
                break;
            case RIGHT_OUTERJOIN_EXPR:
                joinType = OuterJoinType.RIGHT;
                break;
            case FULL_OUTERJOIN_EXPR:
                joinType = OuterJoinType.FULL;
                break;
            case INNERJOIN_EXPR:
                joinType = OuterJoinType.INNER;
                break;
            default:
                throw new IllegalArgumentException("Node type " + node.getType() + " not a recognized outer join node type");
        }

        // always starts with ON-token, so as to not produce an empty node
        ExprIdentNode left = null;
        ExprIdentNode right = null;
        ExprIdentNode[] addLeftArr = null;
        ExprIdentNode[] addRightArr = null;

        // get subnodes representing the on-expression, if provided
        if (node.getChildCount() > 1) {
            left = (ExprIdentNode) astExprNodeMap.get(node.getChild(1));
            right = (ExprIdentNode) astExprNodeMap.get(node.getChild(2));

            // remove from AST-to-expression node map
            astExprNodeMap.remove(node.getChild(1));
            astExprNodeMap.remove(node.getChild(2));

            // get optional additional
            if (node.getChildCount() > 3) {
                ArrayList<ExprIdentNode> addLeft = new ArrayList<ExprIdentNode>();
                ArrayList<ExprIdentNode> addRight = new ArrayList<ExprIdentNode>();
                for (int i = 3; i < node.getChildCount(); i+=2)
                {
                    Tree child = node.getChild(i);
                    addLeft.add((ExprIdentNode)astExprNodeMap.remove(child));
                    addRight.add((ExprIdentNode)astExprNodeMap.remove(node.getChild(i + 1)));
                }
                addLeftArr = addLeft.toArray(new ExprIdentNode[addLeft.size()]);
                addRightArr = addRight.toArray(new ExprIdentNode[addRight.size()]);
            }
        }

        OuterJoinDesc outerJoinDesc = new OuterJoinDesc(joinType, left, right, addLeftArr, addRightArr);
        statementSpec.getOuterJoinDescList().add(outerJoinDesc);
    }

    private void leaveGroupBy(Tree node)
    {
        log.debug(".leaveGroupBy");

        // there must be some expressions under the group by in our map
        if (astExprNodeMap.size() < 1)
        {
            throw new IllegalStateException("Group-by clause generated no expression nodes");
        }

        // For each child to the group-by AST node there must be a generated ExprNode
        for (int i = 0; i < node.getChildCount(); i++)
        {
          Tree child = node.getChild(i);
            // get top expression node for the child node
            ExprNode exprNode = astExprNodeMap.get(child);

            if (exprNode == null)
            {
                throw new IllegalStateException("Expression node as a result of group-by child node not found in collection");
            }

            statementSpec.getGroupByExpressions().add(exprNode);
        }

        // Clear the map - all expression node should be gone
        astExprNodeMap.clear();
    }

    private void leaveInsertInto(Tree node)
    {
        log.debug(".leaveInsertInto");

        int count = 0;
        Tree child = node.getChild(count);

        // istream or rstream
        boolean isIStream = true;
        if (child.getType() == RSTREAM)
        {
            isIStream = false;
            child = node.getChild(++count);
        }
        if (child.getType() == ISTREAM)
        {
            child = node.getChild(++count);
        }

        // type name
        String eventTypeName = child.getText();
        InsertIntoDesc insertIntoDesc = new InsertIntoDesc(isIStream, eventTypeName);

        // optional columns
        child = node.getChild(++count);
        if ((child != null) && (child.getType() == EXPRCOL))
        {
            // Each child to the insert-into AST node represents a column name
            for (int i = 0; i < child.getChildCount(); i++)
            {
                Tree childNode = child.getChild(i);
                insertIntoDesc.add(childNode.getText());
            }
        }

        statementSpec.setInsertIntoDesc(insertIntoDesc);
    }

    private void leaveOrderByElement(Tree node) throws ASTWalkException
    {
        log.debug(".leaveOrderByElement");
        if ((astExprNodeMap.size() > 1) || ((astExprNodeMap.isEmpty())))
        {
            throw new ASTWalkException("Unexpected AST tree contains zero or more then 1 child element for root");
        }

        // Get expression node sub-tree from the AST nodes placed so far
        ExprNode exprNode = astExprNodeMap.values().iterator().next();
        astExprNodeMap.clear();

        // Get optional ascending or descending qualifier
        boolean descending = false;
        if (node.getChildCount() > 1)
        {
            descending = node.getChild(1).getType() == DESC;
        }

        // Add as order-by element
        statementSpec.getOrderByList().add(new OrderByItem(exprNode, descending));
    }

    private void leaveConcat(Tree node)
    {
        ExprConcatNode concatNode = new ExprConcatNode();
        astExprNodeMap.put(node, concatNode);
    }

    private void leaveEvery(Tree node)
    {
        log.debug(".leaveEvery");
        EvalFactoryNode everyNode = this.patternNodeFactory.makeEveryNode();
        addEvalNodeExpression(everyNode, node);
    }

    private void leaveEveryDistinct(Tree node)
    {
        log.debug(".leaveEveryDistinct");
        List<ExprNode> exprNodes = ASTExprHelper.getExprNodes(node.getChild(0), 0, astExprNodeMap);
        EvalFactoryNode everyNode = this.patternNodeFactory.makeEveryDistinctNode(exprNodes);
        addEvalNodeExpression(everyNode, node);
    }

    private void leaveStreamFilter(Tree node)
    {
        log.debug(".leaveStreamFilter");

        // for event streams we keep the filter spec around for use when the stream definition is completed
        filterSpec = ASTExprHelper.walkFilterSpec(node, propertyEvalSpec, astExprNodeMap);

        // set property eval to null
        propertyEvalSpec = null;

        // clear the sub-nodes for the filter since the event property expressions have been processed
        // by building the spec
        astExprNodeMap.clear();
    }

    private void leavePatternFilter(Tree node)
    {
        log.debug(".leavePatternFilter");

        int count = 0;
        Tree startNode = node.getChild(0);
        String optionalPatternTagName = null;
        if (startNode.getType() == IDENT)
        {
            optionalPatternTagName = startNode.getText();
            startNode = node.getChild(++count);
        }

        // Determine event type
        String eventName = startNode.getText();
        count++;

        // get property expression if any
        if ((node.getChildCount() > count) && (node.getChild(count).getType() == EVENT_FILTER_PROPERTY_EXPR))
        {
            ++count;
        }

        Integer consumption = null;
        if ((node.getChildCount() > count) && (node.getChild(count).getType() == ATCHAR))
        {
            Tree filterConsumeAnno = node.getChild(count);
            String name = filterConsumeAnno.getChild(0).getText();
            if (!name.toUpperCase().equals("CONSUME")) {
                throw new EPException("Unexpected pattern filter @ annotation, expecting 'consume' but received '" + name + "'");
            }
            Object number = filterConsumeAnno.getChildCount() < 2 ? null : ASTConstantHelper.parse(filterConsumeAnno.getChild(1));
            if (number != null) {
                consumption = ((Number) number).intValue();
            }
            else {
                consumption = 1;
            }
            count++;
        }

        List<ExprNode> exprNodes = ASTExprHelper.getExprNodes(node, count, astExprNodeMap);

        FilterSpecRaw rawFilterSpec = new FilterSpecRaw(eventName, exprNodes, propertyEvalSpec);
        propertyEvalSpec = null;
        EvalFactoryNode filterNode = patternNodeFactory.makeFilterNode(rawFilterSpec, optionalPatternTagName, consumption);
        addEvalNodeExpression(filterNode, node);
    }

    private void leaveFollowedBy(Tree node)
    {
        log.debug(".leaveFollowedBy");
        ExprNode[] maxExpressions = new ExprNode[node.getChildCount() - 1];
        List<EvalFactoryNode> childNodes = new ArrayList<EvalFactoryNode>();
        for (int i = 0; i < node.getChildCount(); i++) {
            Tree child = node.getChild(i);
            if (child.getType() != FOLLOWED_BY_ITEM) {
                throw new ASTWalkException("Unexpected child node for followed-by item");
            }
            if (i == 0) {
                childNodes.add(astPatternNodeMap.remove(child.getChild(0)));    // first pattern sub-expression cannot have max
            }
            else {
                int current = 0;
                if (child.getChildCount() == 2) {
                    maxExpressions[i - 1] = astExprNodeMap.remove(child.getChild(current));
                    current++;
                }
                childNodes.add(astPatternNodeMap.remove(child.getChild(current)));
            }
        }
        List<ExprNode> expressions = Collections.emptyList();
        if (!CollectionUtil.isAllNullArray(maxExpressions)) {
            expressions = Arrays.asList(maxExpressions); // can contain null elements as max/no-max can be mixed
        }
        EvalFactoryNode fbNode = patternNodeFactory.makeFollowedByNode(expressions, configurationInformation.getEngineDefaults().getPatterns().getMaxSubexpressions() != null);
        fbNode.addChildNodes(childNodes);
        addEvalNodeExpression(fbNode, node);
    }

    private void addEvalNodeExpression(EvalFactoryNode evalNode, Tree node) {
        astPatternNodeMap.put(node, evalNode);
        if (evalNodeExpressions == null) {
            evalNodeExpressions = new HashMap<EvalFactoryNode, String>();
        }
        evalNodeExpressions.put(evalNode, ASTExprHelper.getExpressionText(this.tokenStream, node));
    }

    private void leaveAnd(Tree node)
    {
        log.debug(".leaveAnd");
        EvalFactoryNode andNode = patternNodeFactory.makeAndNode();
        addEvalNodeExpression(andNode, node);
    }

    private void leaveOr(Tree node)
    {
        log.debug(".leaveOr");
        EvalFactoryNode orNode = patternNodeFactory.makeOrNode();
        addEvalNodeExpression(orNode, node);
    }

    private void leaveInSet(Tree node)
    {
        log.debug(".leaveInSet");

        ExprInNode inNode = new ExprInNodeImpl(node.getType() == NOT_IN_SET);
        astExprNodeMap.put(node, inNode);
    }

    private void leaveInRange(Tree node)
    {
        log.debug(".leaveInRange");

        // The second node must be braces
        Tree bracesNode = node.getChild(1);
        if ((bracesNode.getType() != LBRACK) && ((bracesNode.getType() != LPAREN)))
        {
            throw new IllegalStateException("Invalid in-range syntax, no braces but type '" + bracesNode.getType() + "'");
        }
        boolean isLowInclude = bracesNode.getType() == LBRACK;

        // The fifth node must be braces
        bracesNode = node.getChild(4);
        if ((bracesNode.getType() != RBRACK) && ((bracesNode.getType() != RPAREN)))
        {
            throw new IllegalStateException("Invalid in-range syntax, no braces but type '" + bracesNode.getType() + "'");
        }
        boolean isHighInclude = bracesNode.getType() == RBRACK;

        ExprBetweenNode betweenNode = new ExprBetweenNodeImpl(isLowInclude, isHighInclude, node.getType() == NOT_IN_RANGE);
        astExprNodeMap.put(node, betweenNode);
    }

    private void leaveBetween(Tree node)
    {
        log.debug(".leaveBetween");

        ExprBetweenNode betweenNode = new ExprBetweenNodeImpl(true, true, node.getType() == NOT_BETWEEN);
        astExprNodeMap.put(node, betweenNode);
    }

    private void leaveLike(Tree node)
    {
        log.debug(".leaveLike");

        boolean isNot = node.getType() == NOT_LIKE;
        ExprLikeNode likeNode = new ExprLikeNode(isNot);
        astExprNodeMap.put(node, likeNode);
    }

    private void leaveRegexp(Tree node)
    {
        log.debug(".leaveRegexp");

        boolean isNot = node.getType() == NOT_REGEXP;
        ExprRegexpNode regExpNode = new ExprRegexpNode(isNot);
        astExprNodeMap.put(node, regExpNode);
    }

    private void leaveExprNot(Tree node)
    {
        log.debug(".leaveExprNot");
        ExprNotNode notNode = new ExprNotNode();
        astExprNodeMap.put(node, notNode);
    }

    private void leavePatternNot(Tree node)
    {
        log.debug(".leavePatternNot");
        EvalFactoryNode notNode = this.patternNodeFactory.makeNotNode();
        addEvalNodeExpression(notNode, node);
    }

    private void leaveGuard(Tree node) throws ASTWalkException
    {
        log.debug(".leaveGuard");
        String objectNamespace;
        String objectName;
        List<ExprNode> obsParameters;
        if (node.getChild(1).getType() == IDENT && node.getChild(2).getType() == IDENT) {
            objectNamespace = node.getChild(1).getText();
            objectName = node.getChild(2).getText();
            obsParameters = ASTExprHelper.getExprNodes(node, 3, astExprNodeMap);
        }
        else {
            objectNamespace = GuardEnum.WHILE_GUARD.getNamespace();
            objectName = GuardEnum.WHILE_GUARD.getName();
            obsParameters = ASTExprHelper.getExprNodes(node, 1, astExprNodeMap);
        }

        PatternGuardSpec guardSpec = new PatternGuardSpec(objectNamespace, objectName, obsParameters);
        EvalFactoryNode guardNode = patternNodeFactory.makeGuardNode(guardSpec);
        addEvalNodeExpression(guardNode, node);
    }

    private void leaveCaseNode(Tree node, boolean inCase2)
    {
        if (log.isDebugEnabled())
        {
            log.debug(".leaveCase2Node inCase2=" + inCase2);
        }

        if (astExprNodeMap.isEmpty())
        {
            throw new ASTWalkException("Unexpected AST tree contains zero child element for case node");
        }
        if (astExprNodeMap.size() == 1)
        {
            throw new ASTWalkException("AST tree doesn not contain at least when node for case node");
        }

        ExprCaseNode caseNode = new ExprCaseNode(inCase2);
        astExprNodeMap.put(node, caseNode);
    }

    private void leaveExpressionDecl(Tree node)
    {
        if (log.isDebugEnabled())
        {
            log.debug(".leaveExpressionEval");
        }

        String name = node.getChild(0).getText();
        if (node.getChild(1).getType() == EXPRESSIONDECL) {
            String expressionText = scriptBodies.remove(0);
            Tree parametersNode = ASTUtil.findFirstNode(node, EXPRCOL);
            List<String> parameters = Collections.emptyList();
            if (parametersNode != null) {
                parameters = ASTLibHelper.getIdentList(parametersNode);
            }
            Tree optionalReturnTypeNode = ASTUtil.findFirstNode(node, CLASS_IDENT);
            boolean optionalReturnTypeArray = ASTUtil.findFirstNode(node, LBRACK) != null;
            Tree optionalDialectNode = ASTUtil.findFirstNode(node, COLON);
            scriptExpressions.add(new ExpressionScriptProvided(name, expressionText, parameters,
                    optionalReturnTypeNode != null ? optionalReturnTypeNode.getText() : null,
                    optionalReturnTypeArray,
                    optionalDialectNode != null ? optionalDialectNode.getChild(0).getText() : null));
            return;
        }

        Tree parentGoes = node.getChild(1);
        ExprNode inner = ASTExprHelper.getRemoveExpr(parentGoes.getChild(0), this.astExprNodeMap);

        List<String> parametersNames = Collections.emptyList();
        if (parentGoes.getChildCount() > 1) {
            if (parentGoes.getChild(1).getType() == GOES) {
                Tree paramParent = parentGoes.getChild(1);
                if (paramParent.getChild(0).getType() == EXPRCOL) {
                    parametersNames = ASTLibHelper.getIdentList(paramParent.getChild(0));
                }
                else {
                    parametersNames = Collections.singletonList(paramParent.getChild(0).getText());
                }
            }
        }

        ExpressionDeclItem declNode = new ExpressionDeclItem(name, parametersNames, inner);
        expressionDeclarations.add(declNode);
    }

    private void leaveNewKeyword(Tree node)
    {
        if (log.isDebugEnabled())
        {
            log.debug(".leaveNewKeyword");
        }

        List<String> columnNames = new ArrayList<String>();
        List<ExprNode> expressions = new ArrayList<ExprNode>();
        for (int i = 0; i < node.getChildCount(); i++) {
            Tree child = node.getChild(i);
            if (child.getType() != NEW_ITEM) {
                throw new IllegalStateException("Expected new-item node not found");
            }
            String property = ASTFilterSpecHelper.getPropertyName(child.getChild(0), 0);
            columnNames.add(property);

            ExprNode expr;
            if (child.getChildCount() > 1) {
                expr = astExprNodeMap.remove(child.getChild(1));
            }
            else {
                expr = new ExprIdentNodeImpl(property);
            }
            expressions.add(expr);
        }
        String[] columns = columnNames.toArray(new String[columnNames.size()]);
        ExprNewNode newNode = new ExprNewNode(columns);
        newNode.getChildNodes().addAll(expressions);
        astExprNodeMap.put(node, newNode);
    }

    private void leaveContext(Tree node)
    {
        if (log.isDebugEnabled()) {
            log.debug(".leaveContext");
        }
        String contextName = node.getChild(0).getText();
        statementSpec.setOptionalContextName(contextName);
        contextDescriptor = contextManagementService.getContextDescriptor(contextName);
    }

    private void leaveCreateContext(Tree node)
    {
        if (log.isDebugEnabled()) {
            log.debug(".leaveCreateContext");
        }

        CreateContextDesc contextDesc = ASTContextHelper.walkCreateContext(node, astExprNodeMap, astPatternNodeMap, propertyEvalSpec, filterSpec);
        filterSpec = null;
        propertyEvalSpec = null;
        statementSpec.setCreateContextDesc(contextDesc);
    }

    private void leaveCreateDataflow(Tree node)
    {
        if (log.isDebugEnabled()) {
            log.debug(".leaveCreateDataflow");
        }

        CreateDataFlowDesc graphDesc = ASTGraphHelper.walkCreateDataFlow(node, astGOPNodeMap, engineImportService);
        statementSpec.setCreateDataFlowDesc(graphDesc);
    }

    private void leaveGraphDetail(Tree node)
    {
        if (log.isDebugEnabled()) {
            log.debug(".leaveGraphDetail");
        }

        Object value;
        if (node.getType() == GOPCFGITM) {
            value = astExprNodeMap.remove(node.getChild(1));
        }
        else if (node.getType() == GOPCFGEXP) {
            value = astExprNodeMap.remove(node.getChild(0));
        }
        else {
            StatementSpecRaw newSpec = new StatementSpecRaw(defaultStreamSelector);
            value = statementSpec;
            newSpec.getAnnotations().addAll(statementSpec.getAnnotations());
            statementSpec = newSpec;
        }
        astGOPNodeMap.put(node, value);
    }

    private void leaveObserver(Tree node) throws ASTWalkException
    {
        log.debug(".leaveObserver");

        // Get the object information from AST
        String objectNamespace = node.getChild(0).getText();
        String objectName = node.getChild(1).getText();
        List<ExprNode> obsParameters = ASTExprHelper.getExprNodes(node, 2, astExprNodeMap);

        PatternObserverSpec observerSpec = new PatternObserverSpec(objectNamespace, objectName, obsParameters);
        EvalFactoryNode observerNode = this.patternNodeFactory.makeObserverNode(observerSpec);
        addEvalNodeExpression(observerNode, node);
    }

    private void leaveMatch(Tree node) throws ASTWalkException
    {
        log.debug(".leaveMatch");

        boolean hasRange = true;
        int type = node.getChild(0).getType();
        ExprNode low = null;
        ExprNode high = null;
        boolean allowZeroLowerBounds = false;
        if (type == MATCH_UNTIL_RANGE_HALFOPEN) // [expr:]
        {
            low = astExprNodeMap.remove(node.getChild(0).getChild(0));
        }
        else if (type == MATCH_UNTIL_RANGE_HALFCLOSED) // [:expr]
        {
            high = astExprNodeMap.remove(node.getChild(0).getChild(0));
        }
        else if (type == MATCH_UNTIL_RANGE_BOUNDED) // [expr]
        {
            low = astExprNodeMap.remove(node.getChild(0).getChild(0));
            high = low;
        }
        else if (type == MATCH_UNTIL_RANGE_CLOSED) // [expr:expr]
        {
            low = astExprNodeMap.remove(node.getChild(0).getChild(0));
            high = astExprNodeMap.remove(node.getChild(0).getChild(1));
            allowZeroLowerBounds = true;
        }
        else
        {
            hasRange = false;
        }

        boolean tightlyBound = ASTMatchUntilHelper.validate(low, high, allowZeroLowerBounds);
        if ((node.getChildCount() == 2) && (hasRange) && (!tightlyBound))
        {
            throw new ASTWalkException("Variable bounds repeat operator requires an until-expression");
        }

        EvalFactoryNode fbNode = this.patternNodeFactory.makeMatchUntilNode(low, high);
        addEvalNodeExpression(fbNode, node);
    }

    private void leaveSelectClause(Tree node)
    {
        log.debug(".leaveSelectClause");

        int nodeType = node.getChild(0).getType();
        if (nodeType == RSTREAM)
        {
            statementSpec.setSelectStreamDirEnum(SelectClauseStreamSelectorEnum.RSTREAM_ONLY);
        }
        if (nodeType == ISTREAM)
        {
            statementSpec.setSelectStreamDirEnum(SelectClauseStreamSelectorEnum.ISTREAM_ONLY);
        }
        if (nodeType == IRSTREAM)
        {
            statementSpec.setSelectStreamDirEnum(SelectClauseStreamSelectorEnum.RSTREAM_ISTREAM_BOTH);
        }

        boolean isDistinct = false;
        for (int i = 0; i < node.getChildCount(); i++)
        {
            if (node.getChild(i).getType() == DISTINCT)
            {
                isDistinct = true;
            }
        }
        statementSpec.getSelectClauseSpec().setDistinct(isDistinct);
    }

    private ExprNode getRemoveFirstByType(Tree parent, int type) {
        ExprNode exprNode = null;
        for (int i = 0; i < parent.getChildCount(); i++)
        {
            if (parent.getChild(i).getType() == type)
            {
                exprNode = astExprNodeMap.get(parent.getChild(i).getChild(0));
                if (exprNode == null)
                {
                    throw new IllegalStateException("Expression node for AST node not found for type " + parent.getChild(i).getType() + " and text " + parent.getChild(i).getText());
                }
                astExprNodeMap.remove(parent.getChild(i));
            }
        }
        return exprNode;
    }

    private boolean isSelectInsertFirst(Tree child)
    {
        for (int i = 0; i < child.getChildCount(); i++)
        {
            if (child.getChild(i).getType() == ON_SELECT_INSERT_OUTPUT)
            {
                if (child.getChild(i).getChild(0).getType() == ALL)
                {
                    return false;
                }
            }
        }
        return true;
    }

    private List<ExprChainedSpec> getLibFuncChain(Tree parent) {

        List<ExprChainedSpec> chained = new ArrayList<ExprChainedSpec>();
        for (int i = 0; i < parent.getChildCount(); i++) {
            Tree chainElement = parent.getChild(i);
            if (chainElement.getType() != LIB_FUNCTION) {
                continue;
            }

            ExprChainedSpec chainSpec = ASTLibHelper.getLibFunctionChainSpec(chainElement, astExprNodeMap);
            chained.add(chainSpec);
        }
        return chained;
    }

    private static final Log log = LogFactory.getLog(EPLTreeWalker.class);
}
TOP

Related Classes of com.espertech.esper.epl.parse.EPLTreeWalker

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.