/* *************************************************************************************** * 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.accessagg; import com.espertech.esper.client.EventBean; import com.espertech.esper.client.EventType; import com.espertech.esper.collection.Pair; import com.espertech.esper.core.service.StatementType; import com.espertech.esper.epl.agg.access.*; import com.espertech.esper.epl.agg.service.AggregationMethodFactory; import com.espertech.esper.epl.agg.service.AggregationStateKeyWStream; import com.espertech.esper.epl.agg.service.AggregationStateTypeWStream; import com.espertech.esper.epl.expression.baseagg.ExprAggregateNode; import com.espertech.esper.epl.expression.baseagg.ExprAggregateNodeBase; import com.espertech.esper.epl.expression.core.*; import com.espertech.esper.epl.table.mgmt.TableMetadata; import com.espertech.esper.epl.table.mgmt.TableMetadataColumnAggregation; import com.espertech.esper.event.EventAdapterService; import com.espertech.esper.util.JavaClassHelper; import java.io.StringWriter; import java.util.Collection; import java.util.Set; public class ExprAggMultiFunctionSortedMinMaxByNode extends ExprAggregateNodeBase implements ExprEvaluatorEnumeration, ExprAggregateAccessMultiValueNode { private static final long serialVersionUID = -8407756454712340265L; private final boolean max; private final boolean ever; private final boolean sortedwin; private transient EventType containedType; public ExprAggMultiFunctionSortedMinMaxByNode(boolean max, boolean ever, boolean sortedwin) { super(false); this.max = max; this.ever = ever; this.sortedwin = sortedwin; } public AggregationMethodFactory validateAggregationParamsWBinding(ExprValidationContext validationContext, TableMetadataColumnAggregation tableAccessColumn) throws ExprValidationException { return validateAggregationInternal(validationContext, tableAccessColumn); } public AggregationMethodFactory validateAggregationChild(ExprValidationContext validationContext) throws ExprValidationException { return validateAggregationInternal(validationContext, null); } private AggregationMethodFactory validateAggregationInternal(ExprValidationContext validationContext, TableMetadataColumnAggregation optionalBinding) throws ExprValidationException { ExprAggMultiFunctionSortedMinMaxByNodeFactory factory; // handle table-access expression (state provided, accessor needed) if (optionalBinding != null) { factory = handleTableAccess(optionalBinding); } else if (validationContext.getExprEvaluatorContext().getStatementType() == StatementType.CREATE_TABLE) { // handle create-table statements (state creator and default accessor, limited to certain options) factory = handleCreateTable(validationContext); } else if (validationContext.getIntoTableName() != null) { // handle into-table (state provided, accessor and agent needed, validation done by factory) factory = handleIntoTable(validationContext); } else { // handle standalone factory = handleNonTable(validationContext); } this.containedType = factory.getContainedEventType(); return factory; } private ExprAggMultiFunctionSortedMinMaxByNodeFactory handleNonTable(ExprValidationContext validationContext) throws ExprValidationException { if (positionalParams.length == 0) { throw new ExprValidationException("Missing the sort criteria expression"); } // validate that the streams referenced in the criteria are a single stream's Set<Integer> streams = ExprNodeUtility.getIdentStreamNumbers(positionalParams[0]); if (streams.size() > 1 || streams.isEmpty()) { throw new ExprValidationException(getErrorPrefix() + " requires that any parameter expressions evaluate properties of the same stream"); } int streamNum = streams.iterator().next(); // validate that there is a remove stream, use "ever" if not if (!ever && ExprAggMultiFunctionLinearAccessNode.getIstreamOnly(validationContext.getStreamTypeService(), streamNum)) { if (sortedwin) { throw new ExprValidationException(getErrorPrefix() + " requires that a data window is declared for the stream"); } } // determine typing and evaluation containedType = validationContext.getStreamTypeService().getEventTypes()[streamNum]; Class componentType = containedType.getUnderlyingType(); Class accessorResultType = componentType; AggregationAccessor accessor; TableMetadata tableMetadata = validationContext.getTableService().getTableMetadataFromEventType(containedType); if (!sortedwin) { if (tableMetadata != null) { accessor = new AggregationAccessorMinMaxByTable(max, tableMetadata); } else { accessor = new AggregationAccessorMinMaxByNonTable(max); } } else { if (tableMetadata != null) { accessor = new AggregationAccessorSortedTable(max, componentType, tableMetadata); } else { accessor = new AggregationAccessorSortedNonTable(max, componentType); } accessorResultType = JavaClassHelper.getArrayType(accessorResultType); } Pair<ExprNode[], boolean[]> criteriaExpressions = getCriteriaExpressions(); AggregationStateTypeWStream type; if (ever) { type = max ? AggregationStateTypeWStream.MAXEVER : AggregationStateTypeWStream.MINEVER; } else { type = AggregationStateTypeWStream.SORTED; } AggregationStateKeyWStream stateKey = new AggregationStateKeyWStream(streamNum, containedType, type, criteriaExpressions.getFirst(), optionalFilter); ExprEvaluator optionalFilterEval = optionalFilter == null ? null : optionalFilter.getExprEvaluator(); SortedAggregationStateFactoryFactory stateFactoryFactory = new SortedAggregationStateFactoryFactory(validationContext.getEngineImportService(), validationContext.getStatementExtensionSvcContext(), ExprNodeUtility.getEvaluators(criteriaExpressions.getFirst()), criteriaExpressions.getSecond(), ever, streamNum, this, optionalFilterEval); return new ExprAggMultiFunctionSortedMinMaxByNodeFactory(this, accessor, accessorResultType, containedType, stateKey, stateFactoryFactory, AggregationAgentDefault.INSTANCE); } private ExprAggMultiFunctionSortedMinMaxByNodeFactory handleIntoTable(ExprValidationContext validationContext) throws ExprValidationException { int streamNum; if (positionalParams.length == 0 || (positionalParams.length == 1 && positionalParams[0] instanceof ExprWildcard)) { ExprAggMultiFunctionUtil.validateWildcardStreamNumbers(validationContext.getStreamTypeService(), getAggregationFunctionName()); streamNum = 0; } else if (positionalParams.length == 1 && positionalParams[0] instanceof ExprStreamUnderlyingNode) { streamNum = ExprAggMultiFunctionUtil.validateStreamWildcardGetStreamNum(positionalParams[0]); } else if (positionalParams.length > 0) { throw new ExprValidationException("When specifying into-table a sort expression cannot be provided"); } else { streamNum = 0; } EventType containedType = validationContext.getStreamTypeService().getEventTypes()[streamNum]; Class componentType = containedType.getUnderlyingType(); Class accessorResultType = componentType; AggregationAccessor accessor; if (!sortedwin) { accessor = new AggregationAccessorMinMaxByNonTable(max); } else { accessor = new AggregationAccessorSortedNonTable(max, componentType); accessorResultType = JavaClassHelper.getArrayType(accessorResultType); } AggregationAgent agent = ExprAggAggregationAgentFactory.make(streamNum, optionalFilter); return new ExprAggMultiFunctionSortedMinMaxByNodeFactory(this, accessor, accessorResultType, containedType, null, null, agent); } private ExprAggMultiFunctionSortedMinMaxByNodeFactory handleCreateTable(ExprValidationContext validationContext) throws ExprValidationException { if (positionalParams.length == 0) { throw new ExprValidationException("Missing the sort criteria expression"); } String message = "For tables columns, the aggregation function requires the 'sorted(*)' declaration"; if (!sortedwin && !ever) { throw new ExprValidationException(message); } if (validationContext.getStreamTypeService().getStreamNames().length == 0) { throw new ExprValidationException("'Sorted' requires that the event type is provided"); } EventType containedType = validationContext.getStreamTypeService().getEventTypes()[0]; Class componentType = containedType.getUnderlyingType(); Pair<ExprNode[], boolean[]> criteriaExpressions = getCriteriaExpressions(); Class accessorResultType = componentType; AggregationAccessor accessor; if (!sortedwin) { accessor = new AggregationAccessorMinMaxByNonTable(max); } else { accessor = new AggregationAccessorSortedNonTable(max, componentType); accessorResultType = JavaClassHelper.getArrayType(accessorResultType); } SortedAggregationStateFactoryFactory stateFactoryFactory = new SortedAggregationStateFactoryFactory(validationContext.getEngineImportService(), validationContext.getStatementExtensionSvcContext(), ExprNodeUtility.getEvaluators(criteriaExpressions.getFirst()), criteriaExpressions.getSecond(), ever, 0, this, null); return new ExprAggMultiFunctionSortedMinMaxByNodeFactory(this, accessor, accessorResultType, containedType, null, stateFactoryFactory, null); } private Pair<ExprNode[], boolean[]> getCriteriaExpressions() { // determine ordering ascending/descending and build criteria expression without "asc" marker ExprNode[] criteriaExpressions = new ExprNode[this.positionalParams.length]; boolean[] sortDescending = new boolean[positionalParams.length]; for (int i = 0; i < positionalParams.length; i++) { ExprNode parameter = positionalParams[i]; criteriaExpressions[i] = parameter; if (parameter instanceof ExprOrderedExpr) { ExprOrderedExpr ordered = (ExprOrderedExpr) parameter; sortDescending[i] = ordered.isDescending(); if (!ordered.isDescending()) { criteriaExpressions[i] = ordered.getChildNodes()[0]; } } } return new Pair<ExprNode[], boolean[]>(criteriaExpressions, sortDescending); } private ExprAggMultiFunctionSortedMinMaxByNodeFactory handleTableAccess(TableMetadataColumnAggregation tableAccess) { ExprAggMultiFunctionSortedMinMaxByNodeFactory factory = (ExprAggMultiFunctionSortedMinMaxByNodeFactory) tableAccess.getFactory(); AggregationAccessor accessor; Class componentType = factory.getContainedEventType().getUnderlyingType(); Class accessorResultType = componentType; if (!sortedwin) { accessor = new AggregationAccessorMinMaxByNonTable(max); } else { accessor = new AggregationAccessorSortedNonTable(max, componentType); accessorResultType = JavaClassHelper.getArrayType(accessorResultType); } return new ExprAggMultiFunctionSortedMinMaxByNodeFactory(this, accessor, accessorResultType, factory.getContainedEventType(), null, null, null); } public String getAggregationFunctionName() { if (sortedwin) { return "sorted"; } if (ever) { return max ? "maxbyever" : "minbyever"; } return max ? "maxby" : "minby"; } @Override public void toPrecedenceFreeEPL(StringWriter writer) { writer.append(getAggregationFunctionName()); ExprNodeUtility.toExpressionStringParams(writer, this.positionalParams); } public Collection<EventBean> evaluateGetROCollectionEvents(EventBean[] eventsPerStream, boolean isNewData, ExprEvaluatorContext context) { return super.aggregationResultFuture.getCollectionOfEvents(column, eventsPerStream, isNewData, context); } public Collection evaluateGetROCollectionScalar(EventBean[] eventsPerStream, boolean isNewData, ExprEvaluatorContext context) { return null; } public EventType getEventTypeCollection(EventAdapterService eventAdapterService, int statementId) { if (!sortedwin) { return null; } return containedType; } public Class getComponentTypeCollection() throws ExprValidationException { return null; } public EventType getEventTypeSingle(EventAdapterService eventAdapterService, int statementId) throws ExprValidationException { if (sortedwin) { return null; } return containedType; } public EventBean evaluateGetEventBean(EventBean[] eventsPerStream, boolean isNewData, ExprEvaluatorContext context) { return super.aggregationResultFuture.getEventBean(column, eventsPerStream, isNewData, context); } public boolean isMax() { return max; } protected boolean isFilterExpressionAsLastParameter() { return false; } @Override protected boolean equalsNodeAggregateMethodOnly(ExprAggregateNode node) { return false; } private String getErrorPrefix() { return "The '" + getAggregationFunctionName() + "' aggregation function"; } }