/*
***************************************************************************************
* 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.view.ext;
import com.espertech.esper.client.EventBean;
import com.espertech.esper.client.EventType;
import com.espertech.esper.collection.MultiKeyUntyped;
import com.espertech.esper.collection.OneEventCollection;
import com.espertech.esper.core.context.util.AgentInstanceViewFactoryChainContext;
import com.espertech.esper.epl.expression.core.ExprEvaluator;
import com.espertech.esper.epl.expression.core.ExprNode;
import com.espertech.esper.metrics.instrumentation.InstrumentationHelper;
import com.espertech.esper.util.CollectionUtil;
import com.espertech.esper.view.*;
import java.util.*;
/**
* Window sorting by values in the specified field extending a specified number of elements
* from the lowest value up or the highest value down.
* The view accepts 3 parameters. The first parameter is the field name to get the values to sort for,
* the second parameter defines whether to sort ascending or descending, the third parameter
* is the number of elements to keep in the sorted list.
* <p>
* The type of the field to be sorted in the event must implement the Comparable interface.
* <p>
* The natural order in which events arrived is used as the second sorting criteria. Thus should events arrive
* with equal sort values the oldest event leaves the sort window first.
* <p>
* Old values removed from a prior view are removed from the sort view.
*/
public class SortWindowView extends ViewSupport implements DataWindowView, CloneableView {
private final SortWindowViewFactory sortWindowViewFactory;
protected final ExprEvaluator[] sortCriteriaEvaluators;
private final ExprNode[] sortCriteriaExpressions;
private final EventBean[] eventsPerStream = new EventBean[1];
private final boolean[] isDescendingValues;
private final int sortWindowSize;
private final IStreamSortRankRandomAccess optionalSortedRandomAccess;
protected final AgentInstanceViewFactoryChainContext agentInstanceViewFactoryContext;
protected TreeMap<Object, Object> sortedEvents;
protected int eventCount;
public SortWindowView(SortWindowViewFactory sortWindowViewFactory,
ExprNode[] sortCriteriaExpressions,
ExprEvaluator[] sortCriteriaEvaluators,
boolean[] descendingValues,
int sortWindowSize,
IStreamSortRankRandomAccess optionalSortedRandomAccess,
boolean isSortUsingCollator,
AgentInstanceViewFactoryChainContext agentInstanceViewFactoryContext) {
this.sortWindowViewFactory = sortWindowViewFactory;
this.sortCriteriaExpressions = sortCriteriaExpressions;
this.sortCriteriaEvaluators = sortCriteriaEvaluators;
this.isDescendingValues = descendingValues;
this.sortWindowSize = sortWindowSize;
this.optionalSortedRandomAccess = optionalSortedRandomAccess;
this.agentInstanceViewFactoryContext = agentInstanceViewFactoryContext;
Comparator<Object> comparator = CollectionUtil.getComparator(sortCriteriaEvaluators, isSortUsingCollator, isDescendingValues);
sortedEvents = new TreeMap<Object, Object>(comparator);
}
/**
* Returns the field names supplying the values to sort by.
*
* @return field names to sort by
*/
protected final ExprNode[] getSortCriteriaExpressions() {
return sortCriteriaExpressions;
}
/**
* Returns the flags indicating whether to sort in descending order on each property.
*
* @return the isDescending value for each sort property
*/
protected final boolean[] getIsDescendingValues() {
return isDescendingValues;
}
/**
* Returns the number of elements kept by the sort window.
*
* @return size of window
*/
protected final int getSortWindowSize() {
return sortWindowSize;
}
public View cloneView() {
return sortWindowViewFactory.makeView(agentInstanceViewFactoryContext);
}
public final EventType getEventType() {
// The schema is the parent view's schema
return parent.getEventType();
}
public final void update(EventBean[] newData, EventBean[] oldData) {
if (InstrumentationHelper.ENABLED) {
InstrumentationHelper.get().qViewProcessIRStream(this, sortWindowViewFactory.getViewName(), newData, oldData);
}
OneEventCollection removedEvents = null;
// Remove old data
if (oldData != null) {
for (int i = 0; i < oldData.length; i++) {
EventBean oldDataItem = oldData[i];
Object sortValues = getSortValues(oldDataItem);
boolean result = CollectionUtil.removeEventByKeyLazyListMap(sortValues, oldDataItem, sortedEvents);
if (result) {
eventCount--;
if (removedEvents == null) {
removedEvents = new OneEventCollection();
}
removedEvents.add(oldDataItem);
internalHandleRemoved(sortValues, oldDataItem);
}
}
}
// Add new data
if (newData != null) {
for (int i = 0; i < newData.length; i++) {
EventBean newDataItem = newData[i];
Object sortValues = getSortValues(newDataItem);
CollectionUtil.addEventByKeyLazyListMapFront(sortValues, newDataItem, sortedEvents);
eventCount++;
internalHandleAdd(sortValues, newDataItem);
}
}
// Remove data that sorts to the bottom of the window
if (eventCount > sortWindowSize) {
int removeCount = eventCount - sortWindowSize;
for (int i = 0; i < removeCount; i++) {
// Remove the last element of the last key - sort order is key and then natural order of arrival
Object lastKey = sortedEvents.lastKey();
Object lastEntry = sortedEvents.get(lastKey);
if (lastEntry instanceof List) {
List<EventBean> events = (List<EventBean>) lastEntry;
EventBean theEvent = events.remove(events.size() - 1); // remove oldest event, newest events are first in list
eventCount--;
if (events.isEmpty()) {
sortedEvents.remove(lastKey);
}
if (removedEvents == null) {
removedEvents = new OneEventCollection();
}
removedEvents.add(theEvent);
internalHandleRemoved(lastKey, theEvent);
} else {
EventBean theEvent = (EventBean) lastEntry;
eventCount--;
sortedEvents.remove(lastKey);
if (removedEvents == null) {
removedEvents = new OneEventCollection();
}
removedEvents.add(theEvent);
internalHandleRemoved(lastKey, theEvent);
}
}
}
// If there are child views, fireStatementStopped update method
if (optionalSortedRandomAccess != null) {
optionalSortedRandomAccess.refresh(sortedEvents, eventCount, sortWindowSize);
}
if (this.hasViews()) {
EventBean[] expiredArr = null;
if (removedEvents != null) {
expiredArr = removedEvents.toArray();
}
if (InstrumentationHelper.ENABLED) {
InstrumentationHelper.get().qViewIndicate(this, sortWindowViewFactory.getViewName(), newData, expiredArr);
}
updateChildren(newData, expiredArr);
if (InstrumentationHelper.ENABLED) {
InstrumentationHelper.get().aViewIndicate();
}
}
if (InstrumentationHelper.ENABLED) {
InstrumentationHelper.get().aViewProcessIRStream();
}
}
public void internalHandleAdd(Object sortValues, EventBean newDataItem) {
// no action required
}
public void internalHandleRemoved(Object sortValues, EventBean oldDataItem) {
// no action required
}
public final Iterator<EventBean> iterator() {
return new SortWindowIterator(sortedEvents);
}
public final String toString() {
return this.getClass().getName() +
" sortFieldName=" + Arrays.toString(sortCriteriaExpressions) +
" isDescending=" + Arrays.toString(isDescendingValues) +
" sortWindowSize=" + sortWindowSize;
}
protected Object getSortValues(EventBean theEvent) {
eventsPerStream[0] = theEvent;
if (sortCriteriaExpressions.length == 1) {
return sortCriteriaEvaluators[0].evaluate(eventsPerStream, true, agentInstanceViewFactoryContext);
}
Object[] result = new Object[sortCriteriaExpressions.length];
int count = 0;
for (ExprEvaluator expr : sortCriteriaEvaluators) {
result[count++] = expr.evaluate(eventsPerStream, true, agentInstanceViewFactoryContext);
}
return new MultiKeyUntyped(result);
}
/**
* True to indicate the sort window is empty, or false if not empty.
*
* @return true if empty sort window
*/
public boolean isEmpty() {
if (sortedEvents == null) {
return true;
}
return sortedEvents.isEmpty();
}
public void visitView(ViewDataVisitor viewDataVisitor) {
viewDataVisitor.visitPrimary(sortedEvents, false, sortWindowViewFactory.getViewName(), eventCount, null);
}
public ViewFactory getViewFactory() {
return sortWindowViewFactory;
}
}