/* *************************************************************************************** * 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.expression.baseagg; import com.espertech.esper.epl.declexpr.ExprDeclaredNode; import com.espertech.esper.epl.expression.core.ExprNamedParameterNode; import com.espertech.esper.epl.expression.core.ExprNode; import com.espertech.esper.epl.expression.core.ExprNodeInnerNodeProvider; import com.espertech.esper.epl.expression.core.ExprValidationException; import com.espertech.esper.util.JavaClassHelper; import java.util.*; public class ExprAggregateNodeUtil { public static ExprAggregateNodeParamDesc getValidatePositionalParams(ExprNode[] childNodes, boolean builtinAggregationFunc) throws ExprValidationException { ExprAggregateLocalGroupByDesc optionalLocalGroupBy = null; ExprNode optionalFilter = null; int count = 0; for (ExprNode node : childNodes) { if (!isNonPositionalParameter(node)) { count++; } else { ExprNamedParameterNode namedParameterNode = (ExprNamedParameterNode) node; String paramNameLower = namedParameterNode.getParameterName().toLowerCase(Locale.ENGLISH); if (paramNameLower.equals("group_by")) { optionalLocalGroupBy = new ExprAggregateLocalGroupByDesc(namedParameterNode.getChildNodes()); } else if (paramNameLower.equals("filter")) { if (namedParameterNode.getChildNodes().length != 1 | JavaClassHelper.getBoxedType(namedParameterNode.getChildNodes()[0].getExprEvaluator().getType()) != Boolean.class) { throw new ExprValidationException("Filter named parameter requires a single expression returning a boolean-typed value"); } optionalFilter = namedParameterNode.getChildNodes()[0]; } else if (builtinAggregationFunc) { throw new ExprValidationException("Invalid named parameter '" + namedParameterNode.getParameterName() + "' (did you mean 'group_by' or 'filter'?)"); } } } ExprNode[] positionals = new ExprNode[count]; count = 0; for (ExprNode node : childNodes) { if (!isNonPositionalParameter(node)) { positionals[count++] = node; } } return new ExprAggregateNodeParamDesc(positionals, optionalLocalGroupBy, optionalFilter); } public static boolean isNonPositionalParameter(ExprNode node) { return node instanceof ExprNamedParameterNode; } public static void getAggregatesBottomUp(ExprNode[][] nodes, List<ExprAggregateNode> aggregateNodes) { if (nodes == null) { return; } for (ExprNode[] node : nodes) { getAggregatesBottomUp(node, aggregateNodes); } } public static void getAggregatesBottomUp(ExprNode[] nodes, List<ExprAggregateNode> aggregateNodes) { if (nodes == null) { return; } for (ExprNode node : nodes) { getAggregatesBottomUp(node, aggregateNodes); } } /** * Populates into the supplied list all aggregation functions within this expression, if any. * <p>Populates by going bottom-up such that nested aggregates appear first. * <p>I.e. sum(volume * sum(price)) would put first A then B into the list with A=sum(price) and B=sum(volume * A) * * @param topNode is the expression node to deep inspect * @param aggregateNodes is a list of node to populate into */ public static void getAggregatesBottomUp(ExprNode topNode, List<ExprAggregateNode> aggregateNodes) { // Map to hold per level of the node (1 to N depth) of expression node a list of aggregation expr nodes, if any // exist at that level TreeMap<Integer, List<ExprAggregateNode>> aggregateExprPerLevel = new TreeMap<Integer, List<ExprAggregateNode>>(); recursiveAggregateHandleSpecial(topNode, aggregateExprPerLevel, 1); // Recursively enter all aggregate functions and their level into map recursiveAggregateEnter(topNode, aggregateExprPerLevel, 1); // Done if none found if (aggregateExprPerLevel.isEmpty()) { return; } // From the deepest (highest) level to the lowest, add aggregates to list int deepLevel = aggregateExprPerLevel.lastKey(); for (int i = deepLevel; i >= 1; i--) { List<ExprAggregateNode> list = aggregateExprPerLevel.get(i); if (list == null) { continue; } aggregateNodes.addAll(list); } } private static void recursiveAggregateHandleSpecial(ExprNode topNode, Map<Integer, List<ExprAggregateNode>> aggregateExprPerLevel, int level) { if (topNode instanceof ExprNodeInnerNodeProvider) { ExprNodeInnerNodeProvider parameterized = (ExprNodeInnerNodeProvider) topNode; List<ExprNode> additionalNodes = parameterized.getAdditionalNodes(); for (ExprNode additionalNode : additionalNodes) { recursiveAggregateEnter(additionalNode, aggregateExprPerLevel, level); } } if (topNode instanceof ExprDeclaredNode) { ExprDeclaredNode declared = (ExprDeclaredNode) topNode; recursiveAggregateEnter(declared.getBody(), aggregateExprPerLevel, level); } } private static void recursiveAggregateEnter(ExprNode currentNode, Map<Integer, List<ExprAggregateNode>> aggregateExprPerLevel, int currentLevel) { // ask all child nodes to enter themselves for (ExprNode node : currentNode.getChildNodes()) { recursiveAggregateHandleSpecial(node, aggregateExprPerLevel, currentLevel + 1); recursiveAggregateEnter(node, aggregateExprPerLevel, currentLevel + 1); } if (!(currentNode instanceof ExprAggregateNode)) { return; } // Add myself to list, I'm an aggregate function List<ExprAggregateNode> aggregates = aggregateExprPerLevel.get(currentLevel); if (aggregates == null) { aggregates = new LinkedList<ExprAggregateNode>(); aggregateExprPerLevel.put(currentLevel, aggregates); } aggregates.add((ExprAggregateNode) currentNode); } public static int countPositionalArgs(List<ExprNode> args) { int count = 0; for (ExprNode expr : args) { if (!isNonPositionalParameter(expr)) { count++; } } return count; } }