/* *************************************************************************************** * Copyright (C) 2006 EsperTech, Inc. All rights reserved. * * http://www.espertech.com/esper * * http://www.espertech.com * * ---------------------------------------------------------------------------------- * * The software in this package is published under the terms of the GPL license * * a copy of which has been included with this distribution in the license.txt file. * *************************************************************************************** */ package com.espertech.esper.epl.parse; import com.espertech.esper.epl.expression.core.ExprChainedSpec; import com.espertech.esper.epl.expression.core.ExprNode; import com.espertech.esper.epl.expression.time.ExprTimePeriod; import com.espertech.esper.epl.generated.EsperEPL2GrammarParser; import com.espertech.esper.epl.spec.*; import com.espertech.esper.pattern.EvalFactoryNode; import com.espertech.esper.util.JavaClassHelper; import org.antlr.v4.runtime.Token; import org.antlr.v4.runtime.tree.Tree; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.ArrayList; import java.util.List; import java.util.Locale; import java.util.Map; public class ASTContextHelper { private static final Logger log = LoggerFactory.getLogger(ASTContextHelper.class); public static CreateContextDesc walkCreateContext(EsperEPL2GrammarParser.CreateContextExprContext ctx, Map<Tree, ExprNode> astExprNodeMap, Map<Tree, EvalFactoryNode> astPatternNodeMap, PropertyEvalSpec propertyEvalSpec, FilterSpecRaw filterSpec) { String contextName = ctx.name.getText(); ContextDetail contextDetail; EsperEPL2GrammarParser.CreateContextChoiceContext choice = ctx.createContextDetail().createContextChoice(); if (choice != null) { contextDetail = walkChoice(choice, astExprNodeMap, astPatternNodeMap, propertyEvalSpec, filterSpec); } else { contextDetail = walkNested(ctx.createContextDetail().contextContextNested(), astExprNodeMap, astPatternNodeMap, propertyEvalSpec, filterSpec); } return new CreateContextDesc(contextName, contextDetail); } private static ContextDetail walkNested(List<EsperEPL2GrammarParser.ContextContextNestedContext> nestedContexts, Map<Tree, ExprNode> astExprNodeMap, Map<Tree, EvalFactoryNode> astPatternNodeMap, PropertyEvalSpec propertyEvalSpec, FilterSpecRaw filterSpec) { List<CreateContextDesc> contexts = new ArrayList<CreateContextDesc>(nestedContexts.size()); for (EsperEPL2GrammarParser.ContextContextNestedContext nestedctx : nestedContexts) { ContextDetail contextDetail = walkChoice(nestedctx.createContextChoice(), astExprNodeMap, astPatternNodeMap, propertyEvalSpec, filterSpec); CreateContextDesc desc = new CreateContextDesc(nestedctx.name.getText(), contextDetail); contexts.add(desc); } return new ContextDetailNested(contexts); } private static ContextDetail walkChoice(EsperEPL2GrammarParser.CreateContextChoiceContext ctx, Map<Tree, ExprNode> astExprNodeMap, Map<Tree, EvalFactoryNode> astPatternNodeMap, PropertyEvalSpec propertyEvalSpec, FilterSpecRaw filterSpec) { // temporal fixed (start+end) and overlapping (initiated/terminated) if (ctx.START() != null || ctx.INITIATED() != null) { ExprNode[] distinctExpressions = null; if (ctx.createContextDistinct() != null) { if (ctx.createContextDistinct().expressionList() == null) { distinctExpressions = new ExprNode[0]; } else { distinctExpressions = ASTExprHelper.exprCollectSubNodesPerNode(ctx.createContextDistinct().expressionList().expression(), astExprNodeMap); } } ContextDetailCondition startEndpoint; if (ctx.START() != null) { boolean immediate = checkNow(ctx.i); if (immediate) { startEndpoint = ContextDetailConditionImmediate.INSTANCE; } else { startEndpoint = getContextCondition(ctx.r1, astExprNodeMap, astPatternNodeMap, propertyEvalSpec, false); } } else { boolean immediate = checkNow(ctx.i); startEndpoint = getContextCondition(ctx.r1, astExprNodeMap, astPatternNodeMap, propertyEvalSpec, immediate); } boolean overlapping = ctx.INITIATED() != null; ContextDetailCondition endEndpoint = getContextCondition(ctx.r2, astExprNodeMap, astPatternNodeMap, propertyEvalSpec, false); return new ContextDetailInitiatedTerminated(startEndpoint, endEndpoint, overlapping, distinctExpressions); } // partitioned if (ctx.PARTITION() != null) { List<EsperEPL2GrammarParser.CreateContextPartitionItemContext> partitions = ctx.createContextPartitionItem(); List<ContextDetailPartitionItem> rawSpecs = new ArrayList<ContextDetailPartitionItem>(); for (EsperEPL2GrammarParser.CreateContextPartitionItemContext partition : partitions) { filterSpec = ASTFilterSpecHelper.walkFilterSpec(partition.eventFilterExpression(), propertyEvalSpec, astExprNodeMap); propertyEvalSpec = null; List<String> propertyNames = new ArrayList<String>(); List<EsperEPL2GrammarParser.EventPropertyContext> properties = partition.eventProperty(); for (EsperEPL2GrammarParser.EventPropertyContext property : properties) { String propertyName = ASTUtil.getPropertyName(property, 0); propertyNames.add(propertyName); } ASTExprHelper.exprCollectSubNodes(partition, 0, astExprNodeMap); // remove expressions rawSpecs.add(new ContextDetailPartitionItem(filterSpec, propertyNames)); } return new ContextDetailPartitioned(rawSpecs); } else if (ctx.COALESCE() != null) { // hash List<EsperEPL2GrammarParser.CreateContextCoalesceItemContext> coalesces = ctx.createContextCoalesceItem(); List<ContextDetailHashItem> rawSpecs = new ArrayList<ContextDetailHashItem>(coalesces.size()); for (EsperEPL2GrammarParser.CreateContextCoalesceItemContext coalesce : coalesces) { ExprChainedSpec func = ASTLibFunctionHelper.getLibFunctionChainSpec(coalesce.libFunctionNoClass(), astExprNodeMap); filterSpec = ASTFilterSpecHelper.walkFilterSpec(coalesce.eventFilterExpression(), propertyEvalSpec, astExprNodeMap); propertyEvalSpec = null; rawSpecs.add(new ContextDetailHashItem(func, filterSpec)); } String granularity = ctx.g.getText(); if (!granularity.toLowerCase(Locale.ENGLISH).equals("granularity")) { throw ASTWalkException.from("Expected 'granularity' keyword after list of coalesce items, found '" + granularity + "' instead"); } Number num = (Number) ASTConstantHelper.parse(ctx.number()); String preallocateStr = ctx.p != null ? ctx.p.getText() : null; if (preallocateStr != null && !preallocateStr.toLowerCase(Locale.ENGLISH).equals("preallocate")) { throw ASTWalkException.from("Expected 'preallocate' keyword after list of coalesce items, found '" + preallocateStr + "' instead"); } if (!JavaClassHelper.isNumericNonFP(num.getClass()) || JavaClassHelper.getBoxedType(num.getClass()) == Long.class) { throw ASTWalkException.from("Granularity provided must be an int-type number, received " + num.getClass() + " instead"); } return new ContextDetailHash(rawSpecs, num.intValue(), preallocateStr != null); } // categorized if (ctx.createContextGroupItem() != null) { List<EsperEPL2GrammarParser.CreateContextGroupItemContext> grps = ctx.createContextGroupItem(); List<ContextDetailCategoryItem> items = new ArrayList<ContextDetailCategoryItem>(); for (EsperEPL2GrammarParser.CreateContextGroupItemContext grp : grps) { ExprNode exprNode = ASTExprHelper.exprCollectSubNodes(grp, 0, astExprNodeMap).get(0); String name = grp.i.getText(); items.add(new ContextDetailCategoryItem(exprNode, name)); } filterSpec = ASTFilterSpecHelper.walkFilterSpec(ctx.eventFilterExpression(), propertyEvalSpec, astExprNodeMap); return new ContextDetailCategory(items, filterSpec); } throw new IllegalStateException("Unrecognized context detail type"); } private static ContextDetailCondition getContextCondition(EsperEPL2GrammarParser.CreateContextRangePointContext ctx, Map<Tree, ExprNode> astExprNodeMap, Map<Tree, EvalFactoryNode> astPatternNodeMap, PropertyEvalSpec propertyEvalSpec, boolean immediate) { if (ctx == null) { return ContextDetailConditionNever.INSTANCE; } if (ctx.crontabLimitParameterSet() != null) { List<ExprNode> crontab = ASTExprHelper.exprCollectSubNodes(ctx.crontabLimitParameterSet(), 0, astExprNodeMap); return new ContextDetailConditionCrontab(crontab, immediate); } else if (ctx.patternInclusionExpression() != null) { EvalFactoryNode evalNode = ASTExprHelper.patternGetRemoveTopNode(ctx.patternInclusionExpression(), astPatternNodeMap); boolean inclusive = false; if (ctx.i != null) { String ident = ctx.i.getText(); if (ident != null && !ident.toLowerCase(Locale.ENGLISH).equals("inclusive")) { throw ASTWalkException.from("Expected 'inclusive' keyword after '@', found '" + ident + "' instead"); } inclusive = true; } return new ContextDetailConditionPattern(evalNode, inclusive, immediate); } else if (ctx.createContextFilter() != null) { FilterSpecRaw filterSpecRaw = ASTFilterSpecHelper.walkFilterSpec(ctx.createContextFilter().eventFilterExpression(), propertyEvalSpec, astExprNodeMap); String asName = ctx.createContextFilter().i != null ? ctx.createContextFilter().i.getText() : null; if (immediate) { throw ASTWalkException.from("Invalid use of 'now' with initiated-by stream, this combination is not supported"); } return new ContextDetailConditionFilter(filterSpecRaw, asName); } else if (ctx.AFTER() != null) { ExprTimePeriod timePeriod = (ExprTimePeriod) ASTExprHelper.exprCollectSubNodes(ctx.timePeriod(), 0, astExprNodeMap).get(0); return new ContextDetailConditionTimePeriod(timePeriod, immediate); } else { throw new IllegalStateException("Unrecognized child type"); } } private static boolean checkNow(Token i) { if (i == null) { return false; } String ident = i.getText(); if (!ident.toLowerCase(Locale.ENGLISH).equals("now")) { throw ASTWalkException.from("Expected 'now' keyword after '@', found '" + ident + "' instead"); } return true; } }