com.espertech.esper.epl.core.ResultSetProcessorRowPerGroupRollup.java Source code

Java tutorial

Introduction

Here is the source code for com.espertech.esper.epl.core.ResultSetProcessorRowPerGroupRollup.java

Source

/**************************************************************************************
 * Copyright (C) 2006-2015 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.core;

import com.espertech.esper.client.EPException;
import com.espertech.esper.client.EventBean;
import com.espertech.esper.client.EventType;
import com.espertech.esper.client.scopetest.EPAssertionUtil;
import com.espertech.esper.collection.ArrayEventIterator;
import com.espertech.esper.collection.MultiKey;
import com.espertech.esper.collection.MultiKeyUntyped;
import com.espertech.esper.collection.UniformPair;
import com.espertech.esper.core.context.util.AgentInstanceContext;
import com.espertech.esper.epl.agg.rollup.GroupByRollupKey;
import com.espertech.esper.epl.agg.service.AggregationGroupByRollupLevel;
import com.espertech.esper.epl.agg.service.AggregationRowRemovedCallback;
import com.espertech.esper.epl.agg.service.AggregationService;
import com.espertech.esper.epl.expression.core.ExprEvaluator;
import com.espertech.esper.epl.expression.core.ExprValidationException;
import com.espertech.esper.epl.spec.OutputLimitLimitType;
import com.espertech.esper.epl.view.OutputConditionPolled;
import com.espertech.esper.epl.view.OutputConditionPolledFactory;
import com.espertech.esper.metrics.instrumentation.InstrumentationHelper;
import com.espertech.esper.view.Viewable;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import java.util.*;

public class ResultSetProcessorRowPerGroupRollup implements ResultSetProcessor, AggregationRowRemovedCallback {

    private static final Log log = LogFactory.getLog(ResultSetProcessorRowPerGroupRollup.class);

    protected final ResultSetProcessorRowPerGroupRollupFactory prototype;
    protected final OrderByProcessor orderByProcessor;
    protected final AggregationService aggregationService;
    protected AgentInstanceContext agentInstanceContext;

    // For output rate limiting, as temporary buffer of to keep a representative event for each group
    protected final Map<Object, EventBean[]>[] outputLimitGroupRepsPerLevel;

    private final Map<Object, OutputConditionPolled>[] outputState;

    protected final Map<Object, EventBean>[] eventPerGroupBuf;
    private final Map<Object, EventBean[]>[] eventPerGroupJoinBuf;

    private final EventArrayAndSortKeyArray rstreamEventSortArrayPair;
    private final ResultSetProcessorRowPerGroupRollupOutputLastHelper outputLastHelper;
    private final ResultSetProcessorRowPerGroupRollupOutputAllHelper outputAllHelper;

    public ResultSetProcessorRowPerGroupRollup(ResultSetProcessorRowPerGroupRollupFactory prototype,
            OrderByProcessor orderByProcessor, AggregationService aggregationService,
            AgentInstanceContext agentInstanceContext) {
        this.prototype = prototype;
        this.orderByProcessor = orderByProcessor;
        this.aggregationService = aggregationService;
        this.agentInstanceContext = agentInstanceContext;
        aggregationService.setRemovedCallback(this);

        int levelCount = prototype.getGroupByRollupDesc().getLevels().length;

        if (prototype.isJoin()) {
            eventPerGroupJoinBuf = (LinkedHashMap<Object, EventBean[]>[]) new LinkedHashMap[levelCount];
            for (int i = 0; i < levelCount; i++) {
                eventPerGroupJoinBuf[i] = new LinkedHashMap<Object, EventBean[]>();
            }
            eventPerGroupBuf = null;
        } else {
            eventPerGroupBuf = (LinkedHashMap<Object, EventBean>[]) new LinkedHashMap[levelCount];
            for (int i = 0; i < levelCount; i++) {
                eventPerGroupBuf[i] = new LinkedHashMap<Object, EventBean>();
            }
            eventPerGroupJoinBuf = null;
        }

        if (prototype.getOutputLimitSpec() != null) {
            outputLimitGroupRepsPerLevel = (LinkedHashMap<Object, EventBean[]>[]) new LinkedHashMap[levelCount];
            for (int i = 0; i < levelCount; i++) {
                outputLimitGroupRepsPerLevel[i] = new LinkedHashMap<Object, EventBean[]>();
            }

            if (prototype.getOutputLimitSpec().getDisplayLimit() == OutputLimitLimitType.LAST) {
                outputLastHelper = new ResultSetProcessorRowPerGroupRollupOutputLastHelper(this,
                        outputLimitGroupRepsPerLevel.length);
                outputAllHelper = null;
            } else if (prototype.getOutputLimitSpec().getDisplayLimit() == OutputLimitLimitType.ALL) {
                outputAllHelper = new ResultSetProcessorRowPerGroupRollupOutputAllHelper(this,
                        outputLimitGroupRepsPerLevel.length);
                outputLastHelper = null;
            } else {
                outputLastHelper = null;
                outputAllHelper = null;
            }
        } else {
            outputLimitGroupRepsPerLevel = null;
            outputLastHelper = null;
            outputAllHelper = null;
        }

        // Allocate output state for output-first
        if (prototype.getOutputLimitSpec() != null
                && prototype.getOutputLimitSpec().getDisplayLimit() == OutputLimitLimitType.FIRST) {
            outputState = (Map<Object, OutputConditionPolled>[]) new Map[levelCount];
            for (int i = 0; i < levelCount; i++) {
                outputState[i] = new HashMap<Object, OutputConditionPolled>();
            }
        } else {
            outputState = null;
        }

        if (prototype.getOutputLimitSpec() != null && (prototype.isSelectRStream()
                || prototype.getOutputLimitSpec().getDisplayLimit() == OutputLimitLimitType.FIRST)) {
            List<EventBean>[] eventsPerLevel = (List<EventBean>[]) new List[prototype.getGroupByRollupDesc()
                    .getLevels().length];
            List<Object>[] sortKeyPerLevel = null;
            if (orderByProcessor != null) {
                sortKeyPerLevel = (List<Object>[]) new List[prototype.getGroupByRollupDesc().getLevels().length];
            }
            for (AggregationGroupByRollupLevel level : prototype.getGroupByRollupDesc().getLevels()) {
                eventsPerLevel[level.getLevelNumber()] = new ArrayList<EventBean>();
                if (orderByProcessor != null) {
                    sortKeyPerLevel[level.getLevelNumber()] = new ArrayList<Object>();
                }
            }
            rstreamEventSortArrayPair = new EventArrayAndSortKeyArray(eventsPerLevel, sortKeyPerLevel);
        } else {
            rstreamEventSortArrayPair = null;
        }
    }

    public void setAgentInstanceContext(AgentInstanceContext agentInstanceContext) {
        this.agentInstanceContext = agentInstanceContext;
    }

    public EventType getResultEventType() {
        return prototype.getResultEventType();
    }

    public UniformPair<EventBean[]> processJoinResult(Set<MultiKey<EventBean>> newEvents,
            Set<MultiKey<EventBean>> oldEvents, boolean isSynthesize) {
        if (InstrumentationHelper.ENABLED) {
            InstrumentationHelper.get().qResultSetProcessGroupedRowPerGroup();
        }

        if (prototype.isUnidirectional()) {
            this.clear();
        }

        resetEventPerGroupJoinBuf();
        Object[][] newDataMultiKey = generateGroupKeysJoin(newEvents, eventPerGroupJoinBuf, true);
        Object[][] oldDataMultiKey = generateGroupKeysJoin(oldEvents, eventPerGroupJoinBuf, false);

        EventBean[] selectOldEvents = null;
        if (prototype.isSelectRStream()) {
            selectOldEvents = generateOutputEventsJoin(eventPerGroupJoinBuf, false, isSynthesize);
        }

        // update aggregates
        if (newEvents != null) {
            int count = 0;
            for (MultiKey<EventBean> mk : newEvents) {
                aggregationService.applyEnter(mk.getArray(), newDataMultiKey[count++], agentInstanceContext);
            }
        }
        if (oldEvents != null) {
            int count = 0;
            for (MultiKey<EventBean> mk : oldEvents) {
                aggregationService.applyLeave(mk.getArray(), oldDataMultiKey[count++], agentInstanceContext);
            }
        }

        // generate new events using select expressions
        EventBean[] selectNewEvents = generateOutputEventsJoin(eventPerGroupJoinBuf, true, isSynthesize);

        if ((selectNewEvents != null) || (selectOldEvents != null)) {
            if (InstrumentationHelper.ENABLED) {
                InstrumentationHelper.get().aResultSetProcessGroupedRowPerGroup(selectNewEvents, selectOldEvents);
            }
            return new UniformPair<EventBean[]>(selectNewEvents, selectOldEvents);
        }
        if (InstrumentationHelper.ENABLED) {
            InstrumentationHelper.get().aResultSetProcessGroupedRowPerGroup(null, null);
        }
        return null;
    }

    public UniformPair<EventBean[]> processViewResult(EventBean[] newData, EventBean[] oldData,
            boolean isSynthesize) {
        if (InstrumentationHelper.ENABLED) {
            InstrumentationHelper.get().qResultSetProcessGroupedRowPerGroup();
        }

        resetEventPerGroupBuf();
        Object[][] newDataMultiKey = generateGroupKeysView(newData, eventPerGroupBuf, true);
        Object[][] oldDataMultiKey = generateGroupKeysView(oldData, eventPerGroupBuf, false);

        EventBean[] selectOldEvents = null;
        if (prototype.isSelectRStream()) {
            selectOldEvents = generateOutputEventsView(eventPerGroupBuf, false, isSynthesize);
        }

        // update aggregates
        EventBean[] eventsPerStream = new EventBean[1];
        if (newData != null) {
            for (int i = 0; i < newData.length; i++) {
                eventsPerStream[0] = newData[i];
                aggregationService.applyEnter(eventsPerStream, newDataMultiKey[i], agentInstanceContext);
            }
        }
        if (oldData != null) {
            for (int i = 0; i < oldData.length; i++) {
                eventsPerStream[0] = oldData[i];
                aggregationService.applyLeave(eventsPerStream, oldDataMultiKey[i], agentInstanceContext);
            }
        }

        // generate new events using select expressions
        EventBean[] selectNewEvents = generateOutputEventsView(eventPerGroupBuf, true, isSynthesize);

        if ((selectNewEvents != null) || (selectOldEvents != null)) {
            if (InstrumentationHelper.ENABLED) {
                InstrumentationHelper.get().aResultSetProcessGroupedRowPerGroup(selectNewEvents, selectOldEvents);
            }
            return new UniformPair<EventBean[]>(selectNewEvents, selectOldEvents);
        }
        if (InstrumentationHelper.ENABLED) {
            InstrumentationHelper.get().aResultSetProcessGroupedRowPerGroup(null, null);
        }
        return null;
    }

    protected EventBean[] generateOutputEventsView(Map<Object, EventBean>[] keysAndEvents, boolean isNewData,
            boolean isSynthesize) {
        EventBean[] eventsPerStream = new EventBean[1];
        ArrayList<EventBean> events = new ArrayList<EventBean>(1);
        List<GroupByRollupKey> currentGenerators = null;
        if (prototype.isSorting()) {
            currentGenerators = new ArrayList<GroupByRollupKey>(4);
        }

        AggregationGroupByRollupLevel[] levels = prototype.getGroupByRollupDesc().getLevels();
        SelectExprProcessor[] selectExprProcessors = prototype.getPerLevelExpression().getSelectExprProcessor();
        ExprEvaluator[] optionalHavingClauses = prototype.getPerLevelExpression().getOptionalHavingNodes();
        for (AggregationGroupByRollupLevel level : levels) {
            for (Map.Entry<Object, EventBean> entry : keysAndEvents[level.getLevelNumber()].entrySet()) {
                Object groupKey = entry.getKey();

                // Set the current row of aggregation states
                aggregationService.setCurrentAccess(groupKey, agentInstanceContext.getAgentInstanceId(), level);
                eventsPerStream[0] = entry.getValue();

                // Filter the having clause
                if (optionalHavingClauses != null) {
                    if (InstrumentationHelper.ENABLED) {
                        InstrumentationHelper.get().qHavingClauseNonJoin(entry.getValue());
                    }
                    Boolean result = (Boolean) optionalHavingClauses[level.getLevelNumber()]
                            .evaluate(eventsPerStream, isNewData, agentInstanceContext);
                    if (InstrumentationHelper.ENABLED) {
                        InstrumentationHelper.get().aHavingClauseNonJoin(result);
                    }
                    if ((result == null) || (!result)) {
                        continue;
                    }
                }
                events.add(selectExprProcessors[level.getLevelNumber()].process(eventsPerStream, isNewData,
                        isSynthesize, agentInstanceContext));

                if (prototype.isSorting()) {
                    EventBean[] currentEventsPerStream = new EventBean[] { entry.getValue() };
                    currentGenerators.add(new GroupByRollupKey(currentEventsPerStream, level, groupKey));
                }
            }
        }

        if (events.isEmpty()) {
            return null;
        }
        EventBean[] outgoing = events.toArray(new EventBean[events.size()]);
        if (outgoing.length > 1 && prototype.isSorting()) {
            return orderByProcessor.sort(outgoing, currentGenerators, isNewData, agentInstanceContext,
                    prototype.getPerLevelExpression().getOptionalOrderByElements());
        }
        return outgoing;
    }

    private EventBean[] generateOutputEventsJoin(Map<Object, EventBean[]>[] eventPairs, boolean isNewData,
            boolean synthesize) {
        ArrayList<EventBean> events = new ArrayList<EventBean>(1);
        List<GroupByRollupKey> currentGenerators = null;
        if (prototype.isSorting()) {
            currentGenerators = new ArrayList<GroupByRollupKey>(4);
        }

        AggregationGroupByRollupLevel[] levels = prototype.getGroupByRollupDesc().getLevels();
        SelectExprProcessor[] selectExprProcessors = prototype.getPerLevelExpression().getSelectExprProcessor();
        ExprEvaluator[] optionalHavingClauses = prototype.getPerLevelExpression().getOptionalHavingNodes();
        for (AggregationGroupByRollupLevel level : levels) {
            for (Map.Entry<Object, EventBean[]> entry : eventPairs[level.getLevelNumber()].entrySet()) {
                Object groupKey = entry.getKey();

                // Set the current row of aggregation states
                aggregationService.setCurrentAccess(groupKey, agentInstanceContext.getAgentInstanceId(), level);

                // Filter the having clause
                if (optionalHavingClauses != null) {
                    if (InstrumentationHelper.ENABLED) {
                        InstrumentationHelper.get().qHavingClauseJoin(entry.getValue());
                    }
                    Boolean result = (Boolean) optionalHavingClauses[level.getLevelNumber()]
                            .evaluate(entry.getValue(), isNewData, agentInstanceContext);
                    if (InstrumentationHelper.ENABLED) {
                        InstrumentationHelper.get().aHavingClauseJoin(result);
                    }
                    if ((result == null) || (!result)) {
                        continue;
                    }
                }
                events.add(selectExprProcessors[level.getLevelNumber()].process(entry.getValue(), isNewData,
                        synthesize, agentInstanceContext));

                if (prototype.isSorting()) {
                    currentGenerators.add(new GroupByRollupKey(entry.getValue(), level, groupKey));
                }
            }
        }

        if (events.isEmpty()) {
            return null;
        }
        EventBean[] outgoing = events.toArray(new EventBean[events.size()]);
        if (outgoing.length > 1 && prototype.isSorting()) {
            return orderByProcessor.sort(outgoing, currentGenerators, isNewData, agentInstanceContext,
                    prototype.getPerLevelExpression().getOptionalOrderByElements());
        }
        return outgoing;
    }

    public Iterator<EventBean> getIterator(Viewable parent) {
        if (!prototype.isHistoricalOnly()) {
            return obtainIterator(parent);
        }

        aggregationService.clearResults(agentInstanceContext);
        Iterator<EventBean> it = parent.iterator();
        EventBean[] eventsPerStream = new EventBean[1];
        Object[] groupKeys = new Object[prototype.getGroupByRollupDesc().getLevels().length];
        AggregationGroupByRollupLevel[] levels = prototype.getGroupByRollupDesc().getLevels();
        for (; it.hasNext();) {
            eventsPerStream[0] = it.next();
            Object groupKeyComplete = generateGroupKey(eventsPerStream, true);
            for (int j = 0; j < levels.length; j++) {
                Object subkey = levels[j].computeSubkey(groupKeyComplete);
                groupKeys[j] = subkey;
            }
            aggregationService.applyEnter(eventsPerStream, groupKeys, agentInstanceContext);
        }

        ArrayDeque<EventBean> deque = ResultSetProcessorUtil.iteratorToDeque(obtainIterator(parent));
        aggregationService.clearResults(agentInstanceContext);
        return deque.iterator();
    }

    private Iterator<EventBean> obtainIterator(Viewable parent) {
        resetEventPerGroupBuf();
        EventBean[] events = EPAssertionUtil.iteratorToArray(parent.iterator());
        generateGroupKeysView(events, eventPerGroupBuf, true);
        EventBean[] output = generateOutputEventsView(eventPerGroupBuf, true, true);
        return new ArrayEventIterator(output);
    }

    public Iterator<EventBean> getIterator(Set<MultiKey<EventBean>> joinSet) {
        resetEventPerGroupJoinBuf();
        generateGroupKeysJoin(joinSet, eventPerGroupJoinBuf, true);
        EventBean[] output = generateOutputEventsJoin(eventPerGroupJoinBuf, true, true);
        return new ArrayEventIterator(output);
    }

    public void clear() {
        aggregationService.clearResults(agentInstanceContext);
    }

    public UniformPair<EventBean[]> processOutputLimitedJoin(
            List<UniformPair<Set<MultiKey<EventBean>>>> joinEventsSet, boolean generateSynthetic,
            OutputLimitLimitType outputLimitLimitType) {
        if (outputLimitLimitType == OutputLimitLimitType.DEFAULT) {
            return handleOutputLimitDefaultJoin(joinEventsSet, generateSynthetic);
        } else if (outputLimitLimitType == OutputLimitLimitType.ALL) {
            return handleOutputLimitAllJoin(joinEventsSet, generateSynthetic);
        } else if (outputLimitLimitType == OutputLimitLimitType.FIRST) {
            return handleOutputLimitFirstJoin(joinEventsSet, generateSynthetic);
        }
        // (outputLimitLimitType == OutputLimitLimitType.LAST) {
        return handleOutputLimitLastJoin(joinEventsSet, generateSynthetic);
    }

    public UniformPair<EventBean[]> processOutputLimitedView(List<UniformPair<EventBean[]>> viewEventsList,
            boolean generateSynthetic, OutputLimitLimitType outputLimitLimitType) {
        if (outputLimitLimitType == OutputLimitLimitType.DEFAULT) {
            return handleOutputLimitDefaultView(viewEventsList, generateSynthetic);
        } else if (outputLimitLimitType == OutputLimitLimitType.ALL) {
            return handleOutputLimitAllView(viewEventsList, generateSynthetic);
        } else if (outputLimitLimitType == OutputLimitLimitType.FIRST) {
            return handleOutputLimitFirstView(viewEventsList, generateSynthetic);
        }
        // (outputLimitLimitType == OutputLimitLimitType.LAST) {
        return handleOutputLimitLastView(viewEventsList, generateSynthetic);
    }

    private UniformPair<EventBean[]> handleOutputLimitFirstView(List<UniformPair<EventBean[]>> viewEventsList,
            boolean generateSynthetic) {

        for (Map<Object, EventBean[]> aGroupRepsView : outputLimitGroupRepsPerLevel) {
            aGroupRepsView.clear();
        }

        rstreamEventSortArrayPair.reset();

        int oldEventCount;
        if (prototype.getPerLevelExpression().getOptionalHavingNodes() == null) {
            oldEventCount = handleOutputLimitFirstViewNoHaving(viewEventsList, generateSynthetic,
                    rstreamEventSortArrayPair.getEventsPerLevel(), rstreamEventSortArrayPair.getSortKeyPerLevel());
        } else {
            oldEventCount = handleOutputLimitFirstViewHaving(viewEventsList, generateSynthetic,
                    rstreamEventSortArrayPair.getEventsPerLevel(), rstreamEventSortArrayPair.getSortKeyPerLevel());
        }

        return generateAndSort(outputLimitGroupRepsPerLevel, generateSynthetic, oldEventCount);
    }

    private UniformPair<EventBean[]> handleOutputLimitFirstJoin(
            List<UniformPair<Set<MultiKey<EventBean>>>> joinEventsSet, boolean generateSynthetic) {

        for (Map<Object, EventBean[]> aGroupRepsView : outputLimitGroupRepsPerLevel) {
            aGroupRepsView.clear();
        }

        rstreamEventSortArrayPair.reset();

        int oldEventCount;
        if (prototype.getPerLevelExpression().getOptionalHavingNodes() == null) {
            oldEventCount = handleOutputLimitFirstJoinNoHaving(joinEventsSet, generateSynthetic,
                    rstreamEventSortArrayPair.getEventsPerLevel(), rstreamEventSortArrayPair.getSortKeyPerLevel());
        } else {
            oldEventCount = handleOutputLimitFirstJoinHaving(joinEventsSet, generateSynthetic,
                    rstreamEventSortArrayPair.getEventsPerLevel(), rstreamEventSortArrayPair.getSortKeyPerLevel());
        }

        return generateAndSort(outputLimitGroupRepsPerLevel, generateSynthetic, oldEventCount);
    }

    private int handleOutputLimitFirstViewHaving(List<UniformPair<EventBean[]>> viewEventsList,
            boolean generateSynthetic, List<EventBean>[] oldEventsPerLevel,
            List<Object>[] oldEventsSortKeyPerLevel) {
        int oldEventCount = 0;

        ExprEvaluator[] havingPerLevel = prototype.getPerLevelExpression().getOptionalHavingNodes();

        for (UniformPair<EventBean[]> pair : viewEventsList) {
            EventBean[] newData = pair.getFirst();
            EventBean[] oldData = pair.getSecond();

            // apply to aggregates
            Object[] groupKeysPerLevel = new Object[prototype.getGroupByRollupDesc().getLevels().length];
            EventBean[] eventsPerStream;
            if (newData != null) {
                for (EventBean aNewData : newData) {
                    eventsPerStream = new EventBean[] { aNewData };
                    Object groupKeyComplete = generateGroupKey(eventsPerStream, true);
                    for (AggregationGroupByRollupLevel level : prototype.getGroupByRollupDesc().getLevels()) {
                        Object groupKey = level.computeSubkey(groupKeyComplete);
                        groupKeysPerLevel[level.getLevelNumber()] = groupKey;
                    }
                    aggregationService.applyEnter(eventsPerStream, groupKeysPerLevel, agentInstanceContext);
                }
            }
            if (oldData != null) {
                for (EventBean anOldData : oldData) {
                    eventsPerStream = new EventBean[] { anOldData };
                    Object groupKeyComplete = generateGroupKey(eventsPerStream, false);
                    for (AggregationGroupByRollupLevel level : prototype.getGroupByRollupDesc().getLevels()) {
                        Object groupKey = level.computeSubkey(groupKeyComplete);
                        groupKeysPerLevel[level.getLevelNumber()] = groupKey;
                    }
                    aggregationService.applyLeave(eventsPerStream, groupKeysPerLevel, agentInstanceContext);
                }
            }

            if (newData != null) {
                for (EventBean aNewData : newData) {
                    eventsPerStream = new EventBean[] { aNewData };
                    Object groupKeyComplete = generateGroupKey(eventsPerStream, true);
                    for (AggregationGroupByRollupLevel level : prototype.getGroupByRollupDesc().getLevels()) {
                        Object groupKey = level.computeSubkey(groupKeyComplete);

                        aggregationService.setCurrentAccess(groupKey, agentInstanceContext.getAgentInstanceId(),
                                level);
                        if (InstrumentationHelper.ENABLED) {
                            InstrumentationHelper.get().qHavingClauseNonJoin(aNewData);
                        }
                        Boolean result = (Boolean) havingPerLevel[level.getLevelNumber()].evaluate(eventsPerStream,
                                true, agentInstanceContext);
                        if (InstrumentationHelper.ENABLED) {
                            InstrumentationHelper.get().aHavingClauseNonJoin(result);
                        }
                        if ((result == null) || (!result)) {
                            continue;
                        }

                        OutputConditionPolled outputStateGroup = outputState[level.getLevelNumber()].get(groupKey);
                        if (outputStateGroup == null) {
                            try {
                                outputStateGroup = OutputConditionPolledFactory
                                        .createCondition(prototype.getOutputLimitSpec(), agentInstanceContext);
                            } catch (ExprValidationException e) {
                                throw handleConditionValidationException(e);
                            }
                            outputState[level.getLevelNumber()].put(groupKey, outputStateGroup);
                        }
                        boolean pass = outputStateGroup.updateOutputCondition(1, 0);
                        if (pass) {
                            if (outputLimitGroupRepsPerLevel[level.getLevelNumber()].put(groupKey,
                                    eventsPerStream) == null) {
                                if (prototype.isSelectRStream()) {
                                    generateOutputBatched(false, groupKey, level, eventsPerStream, true,
                                            generateSynthetic, oldEventsPerLevel, oldEventsSortKeyPerLevel);
                                    oldEventCount++;
                                }
                            }
                        }
                    }
                }
            }
            if (oldData != null) {
                for (EventBean anOldData : oldData) {
                    eventsPerStream = new EventBean[] { anOldData };
                    Object groupKeyComplete = generateGroupKey(eventsPerStream, false);
                    for (AggregationGroupByRollupLevel level : prototype.getGroupByRollupDesc().getLevels()) {
                        Object groupKey = level.computeSubkey(groupKeyComplete);

                        aggregationService.setCurrentAccess(groupKey, agentInstanceContext.getAgentInstanceId(),
                                level);
                        if (InstrumentationHelper.ENABLED) {
                            InstrumentationHelper.get().qHavingClauseNonJoin(anOldData);
                        }
                        Boolean result = (Boolean) havingPerLevel[level.getLevelNumber()].evaluate(eventsPerStream,
                                false, agentInstanceContext);
                        if (InstrumentationHelper.ENABLED) {
                            InstrumentationHelper.get().aHavingClauseNonJoin(result);
                        }
                        if ((result == null) || (!result)) {
                            continue;
                        }

                        OutputConditionPolled outputStateGroup = outputState[level.getLevelNumber()].get(groupKey);
                        if (outputStateGroup == null) {
                            try {
                                outputStateGroup = OutputConditionPolledFactory
                                        .createCondition(prototype.getOutputLimitSpec(), agentInstanceContext);
                            } catch (ExprValidationException e) {
                                throw handleConditionValidationException(e);
                            }
                            outputState[level.getLevelNumber()].put(groupKey, outputStateGroup);
                        }
                        boolean pass = outputStateGroup.updateOutputCondition(1, 0);
                        if (pass) {
                            if (outputLimitGroupRepsPerLevel[level.getLevelNumber()].put(groupKey,
                                    eventsPerStream) == null) {
                                if (prototype.isSelectRStream()) {
                                    generateOutputBatched(false, groupKey, level, eventsPerStream, false,
                                            generateSynthetic, oldEventsPerLevel, oldEventsSortKeyPerLevel);
                                    oldEventCount++;
                                }
                            }
                        }
                    }
                }
            }
        }
        return oldEventCount;
    }

    private int handleOutputLimitFirstJoinNoHaving(List<UniformPair<Set<MultiKey<EventBean>>>> joinEventSet,
            boolean generateSynthetic, List<EventBean>[] oldEventsPerLevel,
            List<Object>[] oldEventsSortKeyPerLevel) {

        int oldEventCount = 0;

        // outer loop is the events
        for (UniformPair<Set<MultiKey<EventBean>>> pair : joinEventSet) {
            Set<MultiKey<EventBean>> newData = pair.getFirst();
            Set<MultiKey<EventBean>> oldData = pair.getSecond();

            // apply to aggregates
            Object[] groupKeysPerLevel = new Object[prototype.getGroupByRollupDesc().getLevels().length];
            EventBean[] eventsPerStream;
            if (newData != null) {
                for (MultiKey<EventBean> aNewData : newData) {
                    Object groupKeyComplete = generateGroupKey(aNewData.getArray(), true);
                    for (AggregationGroupByRollupLevel level : prototype.getGroupByRollupDesc().getLevels()) {
                        Object groupKey = level.computeSubkey(groupKeyComplete);
                        groupKeysPerLevel[level.getLevelNumber()] = groupKey;

                        OutputConditionPolled outputStateGroup = outputState[level.getLevelNumber()].get(groupKey);
                        if (outputStateGroup == null) {
                            try {
                                outputStateGroup = OutputConditionPolledFactory
                                        .createCondition(prototype.getOutputLimitSpec(), agentInstanceContext);
                            } catch (ExprValidationException e) {
                                throw handleConditionValidationException(e);
                            }
                            outputState[level.getLevelNumber()].put(groupKey, outputStateGroup);
                        }
                        boolean pass = outputStateGroup.updateOutputCondition(1, 0);
                        if (pass) {
                            if (outputLimitGroupRepsPerLevel[level.getLevelNumber()].put(groupKey,
                                    aNewData.getArray()) == null) {
                                if (prototype.isSelectRStream()) {
                                    generateOutputBatched(false, groupKey, level, aNewData.getArray(), true,
                                            generateSynthetic, oldEventsPerLevel, oldEventsSortKeyPerLevel);
                                    oldEventCount++;
                                }
                            }
                        }
                    }
                    aggregationService.applyEnter(aNewData.getArray(), groupKeysPerLevel, agentInstanceContext);
                }
            }
            if (oldData != null) {
                for (MultiKey<EventBean> anOldData : oldData) {
                    Object groupKeyComplete = generateGroupKey(anOldData.getArray(), false);
                    for (AggregationGroupByRollupLevel level : prototype.getGroupByRollupDesc().getLevels()) {
                        Object groupKey = level.computeSubkey(groupKeyComplete);
                        groupKeysPerLevel[level.getLevelNumber()] = groupKey;

                        OutputConditionPolled outputStateGroup = outputState[level.getLevelNumber()].get(groupKey);
                        if (outputStateGroup == null) {
                            try {
                                outputStateGroup = OutputConditionPolledFactory
                                        .createCondition(prototype.getOutputLimitSpec(), agentInstanceContext);
                            } catch (ExprValidationException e) {
                                throw handleConditionValidationException(e);
                            }
                            outputState[level.getLevelNumber()].put(groupKey, outputStateGroup);
                        }
                        boolean pass = outputStateGroup.updateOutputCondition(1, 0);
                        if (pass) {
                            if (outputLimitGroupRepsPerLevel[level.getLevelNumber()].put(groupKey,
                                    anOldData.getArray()) == null) {
                                if (prototype.isSelectRStream()) {
                                    generateOutputBatched(false, groupKey, level, anOldData.getArray(), false,
                                            generateSynthetic, oldEventsPerLevel, oldEventsSortKeyPerLevel);
                                    oldEventCount++;
                                }
                            }
                        }
                    }
                    aggregationService.applyLeave(anOldData.getArray(), groupKeysPerLevel, agentInstanceContext);
                }
            }
        }
        return oldEventCount;
    }

    private int handleOutputLimitFirstJoinHaving(List<UniformPair<Set<MultiKey<EventBean>>>> joinEventSet,
            boolean generateSynthetic, List<EventBean>[] oldEventsPerLevel,
            List<Object>[] oldEventsSortKeyPerLevel) {
        int oldEventCount = 0;

        ExprEvaluator[] havingPerLevel = prototype.getPerLevelExpression().getOptionalHavingNodes();

        for (UniformPair<Set<MultiKey<EventBean>>> pair : joinEventSet) {
            Set<MultiKey<EventBean>> newData = pair.getFirst();
            Set<MultiKey<EventBean>> oldData = pair.getSecond();

            // apply to aggregates
            Object[] groupKeysPerLevel = new Object[prototype.getGroupByRollupDesc().getLevels().length];
            EventBean[] eventsPerStream;
            if (newData != null) {
                for (MultiKey<EventBean> aNewData : newData) {
                    Object groupKeyComplete = generateGroupKey(aNewData.getArray(), true);
                    for (AggregationGroupByRollupLevel level : prototype.getGroupByRollupDesc().getLevels()) {
                        Object groupKey = level.computeSubkey(groupKeyComplete);
                        groupKeysPerLevel[level.getLevelNumber()] = groupKey;
                    }
                    aggregationService.applyEnter(aNewData.getArray(), groupKeysPerLevel, agentInstanceContext);
                }
            }
            if (oldData != null) {
                for (MultiKey<EventBean> anOldData : oldData) {
                    Object groupKeyComplete = generateGroupKey(anOldData.getArray(), false);
                    for (AggregationGroupByRollupLevel level : prototype.getGroupByRollupDesc().getLevels()) {
                        Object groupKey = level.computeSubkey(groupKeyComplete);
                        groupKeysPerLevel[level.getLevelNumber()] = groupKey;
                    }
                    aggregationService.applyLeave(anOldData.getArray(), groupKeysPerLevel, agentInstanceContext);
                }
            }

            if (newData != null) {
                for (MultiKey<EventBean> aNewData : newData) {
                    Object groupKeyComplete = generateGroupKey(aNewData.getArray(), true);
                    for (AggregationGroupByRollupLevel level : prototype.getGroupByRollupDesc().getLevels()) {
                        Object groupKey = level.computeSubkey(groupKeyComplete);

                        aggregationService.setCurrentAccess(groupKey, agentInstanceContext.getAgentInstanceId(),
                                level);
                        if (InstrumentationHelper.ENABLED) {
                            InstrumentationHelper.get().qHavingClauseJoin(aNewData.getArray());
                        }
                        Boolean result = (Boolean) havingPerLevel[level.getLevelNumber()]
                                .evaluate(aNewData.getArray(), true, agentInstanceContext);
                        if (InstrumentationHelper.ENABLED) {
                            InstrumentationHelper.get().aHavingClauseJoin(result);
                        }
                        if ((result == null) || (!result)) {
                            continue;
                        }

                        OutputConditionPolled outputStateGroup = outputState[level.getLevelNumber()].get(groupKey);
                        if (outputStateGroup == null) {
                            try {
                                outputStateGroup = OutputConditionPolledFactory
                                        .createCondition(prototype.getOutputLimitSpec(), agentInstanceContext);
                            } catch (ExprValidationException e) {
                                throw handleConditionValidationException(e);
                            }
                            outputState[level.getLevelNumber()].put(groupKey, outputStateGroup);
                        }
                        boolean pass = outputStateGroup.updateOutputCondition(1, 0);
                        if (pass) {
                            if (outputLimitGroupRepsPerLevel[level.getLevelNumber()].put(groupKey,
                                    aNewData.getArray()) == null) {
                                if (prototype.isSelectRStream()) {
                                    generateOutputBatched(false, groupKey, level, aNewData.getArray(), true,
                                            generateSynthetic, oldEventsPerLevel, oldEventsSortKeyPerLevel);
                                    oldEventCount++;
                                }
                            }
                        }
                    }
                }
            }
            if (oldData != null) {
                for (MultiKey<EventBean> anOldData : oldData) {
                    Object groupKeyComplete = generateGroupKey(anOldData.getArray(), false);
                    for (AggregationGroupByRollupLevel level : prototype.getGroupByRollupDesc().getLevels()) {
                        Object groupKey = level.computeSubkey(groupKeyComplete);

                        aggregationService.setCurrentAccess(groupKey, agentInstanceContext.getAgentInstanceId(),
                                level);
                        if (InstrumentationHelper.ENABLED) {
                            InstrumentationHelper.get().qHavingClauseJoin(anOldData.getArray());
                        }
                        Boolean result = (Boolean) havingPerLevel[level.getLevelNumber()]
                                .evaluate(anOldData.getArray(), false, agentInstanceContext);
                        if (InstrumentationHelper.ENABLED) {
                            InstrumentationHelper.get().aHavingClauseJoin(result);
                        }
                        if ((result == null) || (!result)) {
                            continue;
                        }

                        OutputConditionPolled outputStateGroup = outputState[level.getLevelNumber()].get(groupKey);
                        if (outputStateGroup == null) {
                            try {
                                outputStateGroup = OutputConditionPolledFactory
                                        .createCondition(prototype.getOutputLimitSpec(), agentInstanceContext);
                            } catch (ExprValidationException e) {
                                throw handleConditionValidationException(e);
                            }
                            outputState[level.getLevelNumber()].put(groupKey, outputStateGroup);
                        }
                        boolean pass = outputStateGroup.updateOutputCondition(1, 0);
                        if (pass) {
                            if (outputLimitGroupRepsPerLevel[level.getLevelNumber()].put(groupKey,
                                    anOldData.getArray()) == null) {
                                if (prototype.isSelectRStream()) {
                                    generateOutputBatched(false, groupKey, level, anOldData.getArray(), false,
                                            generateSynthetic, oldEventsPerLevel, oldEventsSortKeyPerLevel);
                                    oldEventCount++;
                                }
                            }
                        }
                    }
                }
            }
        }
        return oldEventCount;
    }

    private int handleOutputLimitFirstViewNoHaving(List<UniformPair<EventBean[]>> viewEventsList,
            boolean generateSynthetic, List<EventBean>[] oldEventsPerLevel,
            List<Object>[] oldEventsSortKeyPerLevel) {

        int oldEventCount = 0;

        // outer loop is the events
        for (UniformPair<EventBean[]> pair : viewEventsList) {
            EventBean[] newData = pair.getFirst();
            EventBean[] oldData = pair.getSecond();

            // apply to aggregates
            Object[] groupKeysPerLevel = new Object[prototype.getGroupByRollupDesc().getLevels().length];
            EventBean[] eventsPerStream;
            if (newData != null) {
                for (EventBean aNewData : newData) {
                    eventsPerStream = new EventBean[] { aNewData };
                    Object groupKeyComplete = generateGroupKey(eventsPerStream, true);
                    for (AggregationGroupByRollupLevel level : prototype.getGroupByRollupDesc().getLevels()) {
                        Object groupKey = level.computeSubkey(groupKeyComplete);
                        groupKeysPerLevel[level.getLevelNumber()] = groupKey;

                        OutputConditionPolled outputStateGroup = outputState[level.getLevelNumber()].get(groupKey);
                        if (outputStateGroup == null) {
                            try {
                                outputStateGroup = OutputConditionPolledFactory
                                        .createCondition(prototype.getOutputLimitSpec(), agentInstanceContext);
                            } catch (ExprValidationException e) {
                                throw handleConditionValidationException(e);
                            }
                            outputState[level.getLevelNumber()].put(groupKey, outputStateGroup);
                        }
                        boolean pass = outputStateGroup.updateOutputCondition(1, 0);
                        if (pass) {
                            if (outputLimitGroupRepsPerLevel[level.getLevelNumber()].put(groupKey,
                                    eventsPerStream) == null) {
                                if (prototype.isSelectRStream()) {
                                    generateOutputBatched(false, groupKey, level, eventsPerStream, true,
                                            generateSynthetic, oldEventsPerLevel, oldEventsSortKeyPerLevel);
                                    oldEventCount++;
                                }
                            }
                        }
                    }
                    aggregationService.applyEnter(eventsPerStream, groupKeysPerLevel, agentInstanceContext);
                }
            }
            if (oldData != null) {
                for (EventBean anOldData : oldData) {
                    eventsPerStream = new EventBean[] { anOldData };
                    Object groupKeyComplete = generateGroupKey(eventsPerStream, false);
                    for (AggregationGroupByRollupLevel level : prototype.getGroupByRollupDesc().getLevels()) {
                        Object groupKey = level.computeSubkey(groupKeyComplete);
                        groupKeysPerLevel[level.getLevelNumber()] = groupKey;

                        OutputConditionPolled outputStateGroup = outputState[level.getLevelNumber()].get(groupKey);
                        if (outputStateGroup == null) {
                            try {
                                outputStateGroup = OutputConditionPolledFactory
                                        .createCondition(prototype.getOutputLimitSpec(), agentInstanceContext);
                            } catch (ExprValidationException e) {
                                throw handleConditionValidationException(e);
                            }
                            outputState[level.getLevelNumber()].put(groupKey, outputStateGroup);
                        }
                        boolean pass = outputStateGroup.updateOutputCondition(1, 0);
                        if (pass) {
                            if (outputLimitGroupRepsPerLevel[level.getLevelNumber()].put(groupKey,
                                    eventsPerStream) == null) {
                                if (prototype.isSelectRStream()) {
                                    generateOutputBatched(false, groupKey, level, eventsPerStream, false,
                                            generateSynthetic, oldEventsPerLevel, oldEventsSortKeyPerLevel);
                                    oldEventCount++;
                                }
                            }
                        }
                    }
                    aggregationService.applyLeave(eventsPerStream, groupKeysPerLevel, agentInstanceContext);
                }
            }
        }
        return oldEventCount;
    }

    private UniformPair<EventBean[]> handleOutputLimitDefaultView(List<UniformPair<EventBean[]>> viewEventsList,
            boolean generateSynthetic) {

        List<EventBean> newEvents = new ArrayList<EventBean>();
        List<Object> newEventsSortKey = null;
        if (orderByProcessor != null) {
            newEventsSortKey = new ArrayList<Object>();
        }

        List<EventBean> oldEvents = null;
        List<Object> oldEventsSortKey = null;
        if (prototype.isSelectRStream()) {
            oldEvents = new ArrayList<EventBean>();
            if (orderByProcessor != null) {
                oldEventsSortKey = new ArrayList<Object>();
            }
        }

        for (UniformPair<EventBean[]> pair : viewEventsList) {
            EventBean[] newData = pair.getFirst();
            EventBean[] oldData = pair.getSecond();

            resetEventPerGroupBuf();
            Object[][] newDataMultiKey = generateGroupKeysView(newData, eventPerGroupBuf, true);
            Object[][] oldDataMultiKey = generateGroupKeysView(oldData, eventPerGroupBuf, false);

            if (prototype.isSelectRStream()) {
                generateOutputBatchedCollectNonJoin(eventPerGroupBuf, false, generateSynthetic, oldEvents,
                        oldEventsSortKey);
            }

            // update aggregates
            EventBean[] eventsPerStream = new EventBean[1];
            if (newData != null) {
                for (int i = 0; i < newData.length; i++) {
                    eventsPerStream[0] = newData[i];
                    aggregationService.applyEnter(eventsPerStream, newDataMultiKey[i], agentInstanceContext);
                }
            }
            if (oldData != null) {
                for (int i = 0; i < oldData.length; i++) {
                    eventsPerStream[0] = oldData[i];
                    aggregationService.applyLeave(eventsPerStream, oldDataMultiKey[i], agentInstanceContext);
                }
            }

            generateOutputBatchedCollectNonJoin(eventPerGroupBuf, true, generateSynthetic, newEvents,
                    newEventsSortKey);
        }

        return convertToArrayMaySort(newEvents, newEventsSortKey, oldEvents, oldEventsSortKey);
    }

    private UniformPair<EventBean[]> handleOutputLimitDefaultJoin(
            List<UniformPair<Set<MultiKey<EventBean>>>> viewEventsList, boolean generateSynthetic) {

        List<EventBean> newEvents = new ArrayList<EventBean>();
        List<Object> newEventsSortKey = null;
        if (orderByProcessor != null) {
            newEventsSortKey = new ArrayList<Object>();
        }

        List<EventBean> oldEvents = null;
        List<Object> oldEventsSortKey = null;
        if (prototype.isSelectRStream()) {
            oldEvents = new ArrayList<EventBean>();
            if (orderByProcessor != null) {
                oldEventsSortKey = new ArrayList<Object>();
            }
        }

        for (UniformPair<Set<MultiKey<EventBean>>> pair : viewEventsList) {
            Set<MultiKey<EventBean>> newData = pair.getFirst();
            Set<MultiKey<EventBean>> oldData = pair.getSecond();

            resetEventPerGroupJoinBuf();
            Object[][] newDataMultiKey = generateGroupKeysJoin(newData, eventPerGroupJoinBuf, true);
            Object[][] oldDataMultiKey = generateGroupKeysJoin(oldData, eventPerGroupJoinBuf, false);

            if (prototype.isSelectRStream()) {
                generateOutputBatchedCollectJoin(eventPerGroupJoinBuf, false, generateSynthetic, oldEvents,
                        oldEventsSortKey);
            }

            // update aggregates
            if (newData != null) {
                int count = 0;
                for (MultiKey<EventBean> newEvent : newData) {
                    aggregationService.applyEnter(newEvent.getArray(), newDataMultiKey[count++],
                            agentInstanceContext);
                }
            }
            if (oldData != null) {
                int count = 0;
                for (MultiKey<EventBean> oldEvent : oldData) {
                    aggregationService.applyLeave(oldEvent.getArray(), oldDataMultiKey[count++],
                            agentInstanceContext);
                }
            }

            generateOutputBatchedCollectJoin(eventPerGroupJoinBuf, true, generateSynthetic, newEvents,
                    newEventsSortKey);
        }

        return convertToArrayMaySort(newEvents, newEventsSortKey, oldEvents, oldEventsSortKey);
    }

    public boolean hasAggregation() {
        return true;
    }

    public void removed(Object key) {
        throw new UnsupportedOperationException();
    }

    protected Object generateGroupKey(EventBean[] eventsPerStream, boolean isNewData) {
        if (InstrumentationHelper.ENABLED) {
            InstrumentationHelper.get().qResultSetProcessComputeGroupKeys(isNewData,
                    prototype.getGroupKeyNodeExpressions(), eventsPerStream);
            Object keyObject;
            if (prototype.getGroupKeyNode() != null) {
                keyObject = prototype.getGroupKeyNode().evaluate(eventsPerStream, isNewData, agentInstanceContext);
            } else {
                ExprEvaluator[] evals = prototype.getGroupKeyNodes();
                Object[] keys = new Object[evals.length];
                for (int i = 0; i < evals.length; i++) {
                    keys[i] = evals[i].evaluate(eventsPerStream, isNewData, agentInstanceContext);
                }
                keyObject = new MultiKeyUntyped(keys);
            }

            InstrumentationHelper.get().aResultSetProcessComputeGroupKeys(isNewData, keyObject);
            return keyObject;
        }

        if (prototype.getGroupKeyNode() != null) {
            return prototype.getGroupKeyNode().evaluate(eventsPerStream, isNewData, agentInstanceContext);
        } else {
            ExprEvaluator[] evals = prototype.getGroupKeyNodes();
            Object[] keys = new Object[evals.length];
            for (int i = 0; i < evals.length; i++) {
                keys[i] = evals[i].evaluate(eventsPerStream, isNewData, agentInstanceContext);
            }
            return new MultiKeyUntyped(keys);
        }
    }

    private void generateOutputBatched(boolean join, Object mk, AggregationGroupByRollupLevel level,
            EventBean[] eventsPerStream, boolean isNewData, boolean isSynthesize, List<EventBean>[] resultEvents,
            List<Object>[] optSortKeys) {
        List<EventBean> resultList = resultEvents[level.getLevelNumber()];
        List<Object> sortKeys = optSortKeys == null ? null : optSortKeys[level.getLevelNumber()];
        generateOutputBatched(join, mk, level, eventsPerStream, isNewData, isSynthesize, resultList, sortKeys);
    }

    protected void generateOutputBatched(boolean join, Object mk, AggregationGroupByRollupLevel level,
            EventBean[] eventsPerStream, boolean isNewData, boolean isSynthesize, List<EventBean> resultEvents,
            List<Object> optSortKeys) {
        aggregationService.setCurrentAccess(mk, agentInstanceContext.getAgentInstanceId(), level);

        if (prototype.getPerLevelExpression().getOptionalHavingNodes() != null) {
            if (InstrumentationHelper.ENABLED) {
                if (!join)
                    InstrumentationHelper.get().qHavingClauseNonJoin(eventsPerStream[0]);
                else
                    InstrumentationHelper.get().qHavingClauseJoin(eventsPerStream);
            }
            Boolean result = (Boolean) prototype.getPerLevelExpression().getOptionalHavingNodes()[level
                    .getLevelNumber()].evaluate(eventsPerStream, isNewData, agentInstanceContext);
            if (InstrumentationHelper.ENABLED) {
                if (!join)
                    InstrumentationHelper.get().aHavingClauseNonJoin(result);
                else
                    InstrumentationHelper.get().aHavingClauseJoin(result);
            }
            if ((result == null) || (!result)) {
                return;
            }
        }

        resultEvents.add(prototype.getPerLevelExpression().getSelectExprProcessor()[level.getLevelNumber()]
                .process(eventsPerStream, isNewData, isSynthesize, agentInstanceContext));

        if (prototype.isSorting()) {
            optSortKeys.add(orderByProcessor.getSortKey(eventsPerStream, isNewData, agentInstanceContext,
                    prototype.getPerLevelExpression().getOptionalOrderByElements()[level.getLevelNumber()]));
        }
    }

    protected void generateOutputBatchedMapUnsorted(boolean join, Object mk, AggregationGroupByRollupLevel level,
            EventBean[] eventsPerStream, boolean isNewData, boolean isSynthesize,
            Map<Object, EventBean> resultEvents) {
        aggregationService.setCurrentAccess(mk, agentInstanceContext.getAgentInstanceId(), level);

        if (prototype.getPerLevelExpression().getOptionalHavingNodes() != null) {
            if (InstrumentationHelper.ENABLED) {
                if (!join)
                    InstrumentationHelper.get().qHavingClauseNonJoin(eventsPerStream[0]);
                else
                    InstrumentationHelper.get().qHavingClauseJoin(eventsPerStream);
            }
            Boolean result = (Boolean) prototype.getPerLevelExpression().getOptionalHavingNodes()[level
                    .getLevelNumber()].evaluate(eventsPerStream, isNewData, agentInstanceContext);
            if (InstrumentationHelper.ENABLED) {
                if (!join)
                    InstrumentationHelper.get().aHavingClauseNonJoin(result);
                else
                    InstrumentationHelper.get().aHavingClauseJoin(result);
            }
            if ((result == null) || (!result)) {
                return;
            }
        }

        resultEvents.put(mk, prototype.getPerLevelExpression().getSelectExprProcessor()[level.getLevelNumber()]
                .process(eventsPerStream, isNewData, isSynthesize, agentInstanceContext));
    }

    private UniformPair<EventBean[]> handleOutputLimitLastView(List<UniformPair<EventBean[]>> viewEventsList,
            boolean generateSynthetic) {
        int oldEventCount = 0;
        if (prototype.isSelectRStream()) {
            rstreamEventSortArrayPair.reset();
        }

        for (Map<Object, EventBean[]> aGroupRepsView : outputLimitGroupRepsPerLevel) {
            aGroupRepsView.clear();
        }

        // outer loop is the events
        for (UniformPair<EventBean[]> pair : viewEventsList) {
            EventBean[] newData = pair.getFirst();
            EventBean[] oldData = pair.getSecond();

            // apply to aggregates
            Object[] groupKeysPerLevel = new Object[prototype.getGroupByRollupDesc().getLevels().length];
            EventBean[] eventsPerStream;
            if (newData != null) {
                for (EventBean aNewData : newData) {
                    eventsPerStream = new EventBean[] { aNewData };
                    Object groupKeyComplete = generateGroupKey(eventsPerStream, true);
                    for (AggregationGroupByRollupLevel level : prototype.getGroupByRollupDesc().getLevels()) {
                        Object groupKey = level.computeSubkey(groupKeyComplete);
                        groupKeysPerLevel[level.getLevelNumber()] = groupKey;
                        if (outputLimitGroupRepsPerLevel[level.getLevelNumber()].put(groupKey,
                                eventsPerStream) == null) {
                            if (prototype.isSelectRStream()) {
                                generateOutputBatched(false, groupKey, level, eventsPerStream, true,
                                        generateSynthetic, rstreamEventSortArrayPair.getEventsPerLevel(),
                                        rstreamEventSortArrayPair.getSortKeyPerLevel());
                                oldEventCount++;
                            }
                        }
                    }
                    aggregationService.applyEnter(eventsPerStream, groupKeysPerLevel, agentInstanceContext);
                }
            }
            if (oldData != null) {
                for (EventBean anOldData : oldData) {
                    eventsPerStream = new EventBean[] { anOldData };
                    Object groupKeyComplete = generateGroupKey(eventsPerStream, false);
                    for (AggregationGroupByRollupLevel level : prototype.getGroupByRollupDesc().getLevels()) {
                        Object groupKey = level.computeSubkey(groupKeyComplete);
                        groupKeysPerLevel[level.getLevelNumber()] = groupKey;
                        if (outputLimitGroupRepsPerLevel[level.getLevelNumber()].put(groupKey,
                                eventsPerStream) == null) {
                            if (prototype.isSelectRStream()) {
                                generateOutputBatched(true, groupKey, level, eventsPerStream, true,
                                        generateSynthetic, rstreamEventSortArrayPair.getEventsPerLevel(),
                                        rstreamEventSortArrayPair.getSortKeyPerLevel());
                                oldEventCount++;
                            }
                        }
                    }
                    aggregationService.applyLeave(eventsPerStream, groupKeysPerLevel, agentInstanceContext);
                }
            }
        }

        return generateAndSort(outputLimitGroupRepsPerLevel, generateSynthetic, oldEventCount);
    }

    private UniformPair<EventBean[]> handleOutputLimitLastJoin(
            List<UniformPair<Set<MultiKey<EventBean>>>> viewEventsList, boolean generateSynthetic) {
        int oldEventCount = 0;
        if (prototype.isSelectRStream()) {
            rstreamEventSortArrayPair.reset();
        }

        for (Map<Object, EventBean[]> aGroupRepsView : outputLimitGroupRepsPerLevel) {
            aGroupRepsView.clear();
        }

        // outer loop is the events
        for (UniformPair<Set<MultiKey<EventBean>>> pair : viewEventsList) {
            Set<MultiKey<EventBean>> newData = pair.getFirst();
            Set<MultiKey<EventBean>> oldData = pair.getSecond();

            // apply to aggregates
            Object[] groupKeysPerLevel = new Object[prototype.getGroupByRollupDesc().getLevels().length];
            if (newData != null) {
                for (MultiKey<EventBean> aNewData : newData) {
                    Object groupKeyComplete = generateGroupKey(aNewData.getArray(), true);
                    for (AggregationGroupByRollupLevel level : prototype.getGroupByRollupDesc().getLevels()) {
                        Object groupKey = level.computeSubkey(groupKeyComplete);
                        groupKeysPerLevel[level.getLevelNumber()] = groupKey;
                        if (outputLimitGroupRepsPerLevel[level.getLevelNumber()].put(groupKey,
                                aNewData.getArray()) == null) {
                            if (prototype.isSelectRStream()) {
                                generateOutputBatched(false, groupKey, level, aNewData.getArray(), true,
                                        generateSynthetic, rstreamEventSortArrayPair.getEventsPerLevel(),
                                        rstreamEventSortArrayPair.getSortKeyPerLevel());
                                oldEventCount++;
                            }
                        }
                    }
                    aggregationService.applyEnter(aNewData.getArray(), groupKeysPerLevel, agentInstanceContext);
                }
            }
            if (oldData != null) {
                for (MultiKey<EventBean> anOldData : oldData) {
                    Object groupKeyComplete = generateGroupKey(anOldData.getArray(), false);
                    for (AggregationGroupByRollupLevel level : prototype.getGroupByRollupDesc().getLevels()) {
                        Object groupKey = level.computeSubkey(groupKeyComplete);
                        groupKeysPerLevel[level.getLevelNumber()] = groupKey;
                        if (outputLimitGroupRepsPerLevel[level.getLevelNumber()].put(groupKey,
                                anOldData.getArray()) == null) {
                            if (prototype.isSelectRStream()) {
                                generateOutputBatched(true, groupKey, level, anOldData.getArray(), true,
                                        generateSynthetic, rstreamEventSortArrayPair.getEventsPerLevel(),
                                        rstreamEventSortArrayPair.getSortKeyPerLevel());
                                oldEventCount++;
                            }
                        }
                    }
                    aggregationService.applyLeave(anOldData.getArray(), groupKeysPerLevel, agentInstanceContext);
                }
            }
        }

        return generateAndSort(outputLimitGroupRepsPerLevel, generateSynthetic, oldEventCount);
    }

    private UniformPair<EventBean[]> handleOutputLimitAllView(List<UniformPair<EventBean[]>> viewEventsList,
            boolean generateSynthetic) {

        int oldEventCount = 0;
        if (prototype.isSelectRStream()) {
            rstreamEventSortArrayPair.reset();

            for (AggregationGroupByRollupLevel level : prototype.getGroupByRollupDesc().getLevels()) {
                Map<Object, EventBean[]> groupGenerators = outputLimitGroupRepsPerLevel[level.getLevelNumber()];
                for (Map.Entry<Object, EventBean[]> entry : groupGenerators.entrySet()) {
                    generateOutputBatched(false, entry.getKey(), level, entry.getValue(), false, generateSynthetic,
                            rstreamEventSortArrayPair.getEventsPerLevel(),
                            rstreamEventSortArrayPair.getSortKeyPerLevel());
                    oldEventCount++;
                }
            }
        }

        // outer loop is the events
        for (UniformPair<EventBean[]> pair : viewEventsList) {
            EventBean[] newData = pair.getFirst();
            EventBean[] oldData = pair.getSecond();

            // apply to aggregates
            Object[] groupKeysPerLevel = new Object[prototype.getGroupByRollupDesc().getLevels().length];
            if (newData != null) {
                for (EventBean aNewData : newData) {
                    EventBean[] eventsPerStream = new EventBean[] { aNewData };
                    Object groupKeyComplete = generateGroupKey(eventsPerStream, true);
                    for (AggregationGroupByRollupLevel level : prototype.getGroupByRollupDesc().getLevels()) {
                        Object groupKey = level.computeSubkey(groupKeyComplete);
                        groupKeysPerLevel[level.getLevelNumber()] = groupKey;
                        Object existing = outputLimitGroupRepsPerLevel[level.getLevelNumber()].put(groupKey,
                                eventsPerStream);

                        if (existing == null && prototype.isSelectRStream()) {
                            generateOutputBatched(false, groupKey, level, eventsPerStream, true, generateSynthetic,
                                    rstreamEventSortArrayPair.getEventsPerLevel(),
                                    rstreamEventSortArrayPair.getSortKeyPerLevel());
                            oldEventCount++;
                        }
                    }
                    aggregationService.applyEnter(eventsPerStream, groupKeysPerLevel, agentInstanceContext);
                }
            }
            if (oldData != null) {
                for (EventBean anOldData : oldData) {
                    EventBean[] eventsPerStream = new EventBean[] { anOldData };
                    Object groupKeyComplete = generateGroupKey(eventsPerStream, false);
                    for (AggregationGroupByRollupLevel level : prototype.getGroupByRollupDesc().getLevels()) {
                        Object groupKey = level.computeSubkey(groupKeyComplete);
                        groupKeysPerLevel[level.getLevelNumber()] = groupKey;
                        Object existing = outputLimitGroupRepsPerLevel[level.getLevelNumber()].put(groupKey,
                                eventsPerStream);

                        if (existing == null && prototype.isSelectRStream()) {
                            generateOutputBatched(false, groupKey, level, eventsPerStream, false, generateSynthetic,
                                    rstreamEventSortArrayPair.getEventsPerLevel(),
                                    rstreamEventSortArrayPair.getSortKeyPerLevel());
                            oldEventCount++;
                        }
                    }
                    aggregationService.applyLeave(eventsPerStream, groupKeysPerLevel, agentInstanceContext);
                }
            }
        }

        return generateAndSort(outputLimitGroupRepsPerLevel, generateSynthetic, oldEventCount);
    }

    private UniformPair<EventBean[]> handleOutputLimitAllJoin(
            List<UniformPair<Set<MultiKey<EventBean>>>> joinEventsSet, boolean generateSynthetic) {

        int oldEventCount = 0;
        if (prototype.isSelectRStream()) {
            rstreamEventSortArrayPair.reset();

            for (AggregationGroupByRollupLevel level : prototype.getGroupByRollupDesc().getLevels()) {
                Map<Object, EventBean[]> groupGenerators = outputLimitGroupRepsPerLevel[level.getLevelNumber()];
                for (Map.Entry<Object, EventBean[]> entry : groupGenerators.entrySet()) {
                    generateOutputBatched(false, entry.getKey(), level, entry.getValue(), false, generateSynthetic,
                            rstreamEventSortArrayPair.getEventsPerLevel(),
                            rstreamEventSortArrayPair.getSortKeyPerLevel());
                    oldEventCount++;
                }
            }
        }

        // outer loop is the events
        for (UniformPair<Set<MultiKey<EventBean>>> pair : joinEventsSet) {
            Set<MultiKey<EventBean>> newData = pair.getFirst();
            Set<MultiKey<EventBean>> oldData = pair.getSecond();

            // apply to aggregates
            Object[] groupKeysPerLevel = new Object[prototype.getGroupByRollupDesc().getLevels().length];
            if (newData != null) {
                for (MultiKey<EventBean> aNewData : newData) {
                    Object groupKeyComplete = generateGroupKey(aNewData.getArray(), true);
                    for (AggregationGroupByRollupLevel level : prototype.getGroupByRollupDesc().getLevels()) {
                        Object groupKey = level.computeSubkey(groupKeyComplete);
                        groupKeysPerLevel[level.getLevelNumber()] = groupKey;
                        Object existing = outputLimitGroupRepsPerLevel[level.getLevelNumber()].put(groupKey,
                                aNewData.getArray());

                        if (existing == null && prototype.isSelectRStream()) {
                            generateOutputBatched(false, groupKey, level, aNewData.getArray(), true,
                                    generateSynthetic, rstreamEventSortArrayPair.getEventsPerLevel(),
                                    rstreamEventSortArrayPair.getSortKeyPerLevel());
                            oldEventCount++;
                        }
                    }
                    aggregationService.applyEnter(aNewData.getArray(), groupKeysPerLevel, agentInstanceContext);
                }
            }
            if (oldData != null) {
                for (MultiKey<EventBean> anOldData : oldData) {
                    Object groupKeyComplete = generateGroupKey(anOldData.getArray(), false);
                    for (AggregationGroupByRollupLevel level : prototype.getGroupByRollupDesc().getLevels()) {
                        Object groupKey = level.computeSubkey(groupKeyComplete);
                        groupKeysPerLevel[level.getLevelNumber()] = groupKey;
                        Object existing = outputLimitGroupRepsPerLevel[level.getLevelNumber()].put(groupKey,
                                anOldData.getArray());

                        if (existing == null && prototype.isSelectRStream()) {
                            generateOutputBatched(false, groupKey, level, anOldData.getArray(), false,
                                    generateSynthetic, rstreamEventSortArrayPair.getEventsPerLevel(),
                                    rstreamEventSortArrayPair.getSortKeyPerLevel());
                            oldEventCount++;
                        }
                    }
                    aggregationService.applyLeave(anOldData.getArray(), groupKeysPerLevel, agentInstanceContext);
                }
            }
        }

        return generateAndSort(outputLimitGroupRepsPerLevel, generateSynthetic, oldEventCount);
    }

    private void generateOutputBatchedCollectNonJoin(Map<Object, EventBean>[] eventPairs, boolean isNewData,
            boolean generateSynthetic, List<EventBean> events, List<Object> sortKey) {
        AggregationGroupByRollupLevel[] levels = prototype.getGroupByRollupDesc().getLevels();
        EventBean[] eventsPerStream = new EventBean[1];

        for (AggregationGroupByRollupLevel level : levels) {
            Map<Object, EventBean> eventsForLevel = eventPairs[level.getLevelNumber()];
            for (Map.Entry<Object, EventBean> pair : eventsForLevel.entrySet()) {
                eventsPerStream[0] = pair.getValue();
                generateOutputBatched(false, pair.getKey(), level, eventsPerStream, isNewData, generateSynthetic,
                        events, sortKey);
            }
        }
    }

    private void generateOutputBatchedCollectJoin(Map<Object, EventBean[]>[] eventPairs, boolean isNewData,
            boolean generateSynthetic, List<EventBean> events, List<Object> sortKey) {
        AggregationGroupByRollupLevel[] levels = prototype.getGroupByRollupDesc().getLevels();

        for (AggregationGroupByRollupLevel level : levels) {
            Map<Object, EventBean[]> eventsForLevel = eventPairs[level.getLevelNumber()];
            for (Map.Entry<Object, EventBean[]> pair : eventsForLevel.entrySet()) {
                generateOutputBatched(false, pair.getKey(), level, pair.getValue(), isNewData, generateSynthetic,
                        events, sortKey);
            }
        }
    }

    private void resetEventPerGroupBuf() {
        for (Map<Object, EventBean> anEventPerGroupBuf : eventPerGroupBuf) {
            anEventPerGroupBuf.clear();
        }
    }

    private void resetEventPerGroupJoinBuf() {
        for (Map<Object, EventBean[]> anEventPerGroupBuf : eventPerGroupJoinBuf) {
            anEventPerGroupBuf.clear();
        }
    }

    private EventsAndSortKeysPair getOldEventsSortKeys(int oldEventCount, List<EventBean>[] oldEventsPerLevel,
            List<Object>[] oldEventsSortKeyPerLevel) {
        EventBean[] oldEventsArr = new EventBean[oldEventCount];
        Object[] oldEventsSortKeys = null;
        if (orderByProcessor != null) {
            oldEventsSortKeys = new Object[oldEventCount];
        }
        int countEvents = 0;
        int countSortKeys = 0;
        for (AggregationGroupByRollupLevel level : prototype.getGroupByRollupDesc().getLevels()) {
            List<EventBean> events = oldEventsPerLevel[level.getLevelNumber()];
            for (EventBean event : events) {
                oldEventsArr[countEvents++] = event;
            }
            if (orderByProcessor != null) {
                List<Object> sortKeys = oldEventsSortKeyPerLevel[level.getLevelNumber()];
                for (Object sortKey : sortKeys) {
                    oldEventsSortKeys[countSortKeys++] = sortKey;
                }
            }
        }
        return new EventsAndSortKeysPair(oldEventsArr, oldEventsSortKeys);
    }

    protected Object[][] generateGroupKeysView(EventBean[] events, Map<Object, EventBean>[] eventPerKey,
            boolean isNewData) {
        if (events == null) {
            return null;
        }

        Object[][] result = new Object[events.length][];
        EventBean[] eventsPerStream = new EventBean[1];

        for (int i = 0; i < events.length; i++) {
            eventsPerStream[0] = events[i];
            Object groupKeyComplete = generateGroupKey(eventsPerStream, isNewData);
            AggregationGroupByRollupLevel[] levels = prototype.getGroupByRollupDesc().getLevels();
            result[i] = new Object[levels.length];
            for (int j = 0; j < levels.length; j++) {
                Object subkey = levels[j].computeSubkey(groupKeyComplete);
                result[i][j] = subkey;
                eventPerKey[levels[j].getLevelNumber()].put(subkey, events[i]);
            }
        }

        return result;
    }

    private Object[][] generateGroupKeysJoin(Set<MultiKey<EventBean>> events,
            Map<Object, EventBean[]>[] eventPerKey, boolean isNewData) {
        if (events == null || events.isEmpty()) {
            return null;
        }

        Object[][] result = new Object[events.size()][];

        int count = -1;
        for (MultiKey<EventBean> eventrow : events) {
            count++;
            Object groupKeyComplete = generateGroupKey(eventrow.getArray(), isNewData);
            AggregationGroupByRollupLevel[] levels = prototype.getGroupByRollupDesc().getLevels();
            result[count] = new Object[levels.length];
            for (int j = 0; j < levels.length; j++) {
                Object subkey = levels[j].computeSubkey(groupKeyComplete);
                result[count][j] = subkey;
                eventPerKey[levels[j].getLevelNumber()].put(subkey, eventrow.getArray());
            }
        }

        return result;
    }

    private UniformPair<EventBean[]> generateAndSort(Map<Object, EventBean[]>[] outputLimitGroupRepsPerLevel,
            boolean generateSynthetic, int oldEventCount) {
        // generate old events: ordered by level by default
        EventBean[] oldEventsArr = null;
        Object[] oldEventSortKeys = null;
        if (prototype.isSelectRStream() && oldEventCount > 0) {
            EventsAndSortKeysPair pair = getOldEventsSortKeys(oldEventCount,
                    rstreamEventSortArrayPair.getEventsPerLevel(), rstreamEventSortArrayPair.getSortKeyPerLevel());
            oldEventsArr = pair.getEvents();
            oldEventSortKeys = pair.getSortKeys();
        }

        List<EventBean> newEvents = new ArrayList<EventBean>();
        List<Object> newEventsSortKey = null;
        if (orderByProcessor != null) {
            newEventsSortKey = new ArrayList<Object>();
        }

        for (AggregationGroupByRollupLevel level : prototype.getGroupByRollupDesc().getLevels()) {
            Map<Object, EventBean[]> groupGenerators = outputLimitGroupRepsPerLevel[level.getLevelNumber()];
            for (Map.Entry<Object, EventBean[]> entry : groupGenerators.entrySet()) {
                generateOutputBatched(false, entry.getKey(), level, entry.getValue(), true, generateSynthetic,
                        newEvents, newEventsSortKey);
            }
        }

        EventBean[] newEventsArr = (newEvents.isEmpty()) ? null
                : newEvents.toArray(new EventBean[newEvents.size()]);
        if (orderByProcessor != null) {
            Object[] sortKeysNew = (newEventsSortKey.isEmpty()) ? null
                    : newEventsSortKey.toArray(new Object[newEventsSortKey.size()]);
            newEventsArr = orderByProcessor.sort(newEventsArr, sortKeysNew, agentInstanceContext);
            if (prototype.isSelectRStream()) {
                oldEventsArr = orderByProcessor.sort(oldEventsArr, oldEventSortKeys, agentInstanceContext);
            }
        }

        if ((newEventsArr == null) && (oldEventsArr == null)) {
            return null;
        }
        return new UniformPair<EventBean[]>(newEventsArr, oldEventsArr);
    }

    public void applyViewResult(EventBean[] newData, EventBean[] oldData) {
        EventBean[] eventsPerStream = new EventBean[1];
        if (newData != null) {
            for (EventBean aNewData : newData) {
                eventsPerStream[0] = aNewData;
                Object[] keys = generateGroupKeysNonJoin(eventsPerStream, true);
                aggregationService.applyEnter(eventsPerStream, keys, agentInstanceContext);
            }
        }
        if (oldData != null) {
            for (EventBean anOldData : oldData) {
                eventsPerStream[0] = anOldData;
                Object[] keys = generateGroupKeysNonJoin(eventsPerStream, false);
                aggregationService.applyLeave(eventsPerStream, keys, agentInstanceContext);
            }
        }
    }

    public void applyJoinResult(Set<MultiKey<EventBean>> newEvents, Set<MultiKey<EventBean>> oldEvents) {
        if (newEvents != null) {
            for (MultiKey<EventBean> mk : newEvents) {
                Object[] keys = generateGroupKeysNonJoin(mk.getArray(), true);
                aggregationService.applyEnter(mk.getArray(), keys, agentInstanceContext);
            }
        }
        if (oldEvents != null) {
            for (MultiKey<EventBean> mk : oldEvents) {
                Object[] keys = generateGroupKeysNonJoin(mk.getArray(), false);
                aggregationService.applyLeave(mk.getArray(), keys, agentInstanceContext);
            }
        }
    }

    public void processOutputLimitedLastAllNonBufferedView(EventBean[] newData, EventBean[] oldData,
            boolean isGenerateSynthetic, boolean isAll) {
        if (isAll) {
            outputAllHelper.processView(newData, oldData, isGenerateSynthetic);
        } else {
            outputLastHelper.processView(newData, oldData, isGenerateSynthetic);
        }
    }

    public void processOutputLimitedLastAllNonBufferedJoin(Set<MultiKey<EventBean>> newEvents,
            Set<MultiKey<EventBean>> oldEvents, boolean isGenerateSynthetic, boolean isAll) {
        if (isAll) {
            outputAllHelper.processJoin(newEvents, oldEvents, isGenerateSynthetic);
        } else {
            outputLastHelper.processJoin(newEvents, oldEvents, isGenerateSynthetic);
        }
    }

    public UniformPair<EventBean[]> continueOutputLimitedLastAllNonBufferedView(boolean isSynthesize,
            boolean isAll) {
        if (isAll) {
            return outputAllHelper.outputView(isSynthesize);
        }
        return outputLastHelper.outputView(isSynthesize);
    }

    public UniformPair<EventBean[]> continueOutputLimitedLastAllNonBufferedJoin(boolean isSynthesize,
            boolean isAll) {
        if (isAll) {
            return outputAllHelper.outputJoin(isSynthesize);
        }
        return outputLastHelper.outputJoin(isSynthesize);
    }

    private UniformPair<EventBean[]> convertToArrayMaySort(List<EventBean> newEvents, List<Object> newEventsSortKey,
            List<EventBean> oldEvents, List<Object> oldEventsSortKey) {
        EventBean[] newEventsArr = (newEvents.isEmpty()) ? null
                : newEvents.toArray(new EventBean[newEvents.size()]);
        EventBean[] oldEventsArr = null;
        if (prototype.isSelectRStream()) {
            oldEventsArr = (oldEvents.isEmpty()) ? null : oldEvents.toArray(new EventBean[oldEvents.size()]);
        }

        if (orderByProcessor != null) {
            Object[] sortKeysNew = (newEventsSortKey.isEmpty()) ? null
                    : newEventsSortKey.toArray(new Object[newEventsSortKey.size()]);
            newEventsArr = orderByProcessor.sort(newEventsArr, sortKeysNew, agentInstanceContext);
            if (prototype.isSelectRStream()) {
                Object[] sortKeysOld = (oldEventsSortKey.isEmpty()) ? null
                        : oldEventsSortKey.toArray(new Object[oldEventsSortKey.size()]);
                oldEventsArr = orderByProcessor.sort(oldEventsArr, sortKeysOld, agentInstanceContext);
            }
        }

        if ((newEventsArr == null) && (oldEventsArr == null)) {
            return null;
        }
        return new UniformPair<EventBean[]>(newEventsArr, oldEventsArr);
    }

    private EPException handleConditionValidationException(ExprValidationException e) {
        return new EPException(
                "Error starting output limit for group for statement '"
                        + agentInstanceContext.getStatementContext().getStatementName() + "': " + e.getMessage(),
                e);
    }

    private Object[] generateGroupKeysNonJoin(EventBean[] eventsPerStream, boolean isNewData) {
        Object groupKeyComplete = generateGroupKey(eventsPerStream, true);
        AggregationGroupByRollupLevel[] levels = prototype.getGroupByRollupDesc().getLevels();
        Object[] result = new Object[levels.length];
        for (int j = 0; j < levels.length; j++) {
            Object subkey = levels[j].computeSubkey(groupKeyComplete);
            result[j] = subkey;
        }
        return result;
    }

    private static class EventArrayAndSortKeyArray {
        private final List<EventBean>[] eventsPerLevel;
        private final List<Object>[] sortKeyPerLevel;

        private EventArrayAndSortKeyArray(List<EventBean>[] eventsPerLevel, List<Object>[] sortKeyPerLevel) {
            this.eventsPerLevel = eventsPerLevel;
            this.sortKeyPerLevel = sortKeyPerLevel;
        }

        public List<EventBean>[] getEventsPerLevel() {
            return eventsPerLevel;
        }

        public List<Object>[] getSortKeyPerLevel() {
            return sortKeyPerLevel;
        }

        public void reset() {
            for (List<EventBean> anEventsPerLevel : eventsPerLevel) {
                anEventsPerLevel.clear();
            }
            if (sortKeyPerLevel != null) {
                for (List<Object> anSortKeyPerLevel : sortKeyPerLevel) {
                    anSortKeyPerLevel.clear();
                }
            }
        }
    }

    private static class EventsAndSortKeysPair {
        private final EventBean[] events;
        private final Object[] sortKeys;

        private EventsAndSortKeysPair(EventBean[] events, Object[] sortKeys) {
            this.events = events;
            this.sortKeys = sortKeys;
        }

        public EventBean[] getEvents() {
            return events;
        }

        public Object[] getSortKeys() {
            return sortKeys;
        }
    }
}