/**************************************************************************************
* Copyright (C) 2008 EsperTech, Inc. All rights reserved. *
* http://esper.codehaus.org *
* 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.internal;
import com.espertech.esper.client.EventBean;
import com.espertech.esper.collection.RollingEventBuffer;
import com.espertech.esper.collection.ViewUpdatedCollection;
import com.espertech.esper.view.window.RelativeAccessByEventNIndex;
import java.util.*;
/**
* Buffers view-posted insert stream (new data) and remove stream (old data) events for
* use with determining prior results in these streams, for multiple different prior events.
* <p>
* Buffers only exactly those events in new data and old data that are being asked for via the
* 2 or more 'prior' functions that specify different indexes. For example "select prior(2, price), prior(1, price)"
* results in on buffer instance handling both the need to the immediatly prior (1) and the 2-events-ago
* event (2).
* <p>
* As all views are required to post new data and post old data that removes the new data to subsequent views,
* this buffer can be attached to all views and should not result in a memory leak.
* <p>
* When the buffer receives old data (rstream) events it removes the prior events to the rstream events
* from the buffer the next time it receives a post (not immediatly) to allow queries to the buffer.
*/
public class PriorEventBufferMulti implements ViewUpdatedCollection, RelativeAccessByEventNIndex
{
private final int priorToIndexesSize;
private final int[] priorToIndexes;
private final Map<EventBean, EventBean[]> priorEventMap;
private final RollingEventBuffer newEvents;
private EventBean[] lastOldData;
/**
* Ctor.
* @param priorToIndexSet holds a list of prior-event indexes.
* <p> For example, an array {0,4,6} means the current event, 4 events before the current event
* and 6 events before the current event.
*/
public PriorEventBufferMulti(int[] priorToIndexSet)
{
// Determine the maximum prior index to retain
int maxPriorIndex = 0;
for (Integer priorIndex : priorToIndexSet)
{
if (priorIndex > maxPriorIndex)
{
maxPriorIndex = priorIndex;
}
}
// Copy the set of indexes into an array, sort in ascending order
priorToIndexesSize = priorToIndexSet.length;
priorToIndexes = new int[priorToIndexesSize];
int count = 0;
for (Integer priorIndex : priorToIndexSet)
{
priorToIndexes[count++] = priorIndex;
}
Arrays.sort(priorToIndexes);
// Construct a rolling buffer of new data for holding max index + 1 (position 1 requires 2 events to keep)
newEvents = new RollingEventBuffer(maxPriorIndex + 1);
priorEventMap = new HashMap<EventBean, EventBean[]>();
}
public void update(EventBean[] newData, EventBean[] oldData)
{
// Remove last old data posted in previous post
if (lastOldData != null)
{
for (int i = 0; i < lastOldData.length; i++)
{
priorEventMap.remove(lastOldData[i]);
}
}
// Post new data to rolling buffer starting with the oldest
if (newData != null)
{
for (int i = 0; i < newData.length; i++)
{
EventBean newEvent = newData[i];
// Add new event
newEvents.add(newEvent);
// Save prior index events in array
EventBean[] priorEvents = new EventBean[priorToIndexesSize];
for (int j = 0; j < priorToIndexesSize; j++)
{
int priorIndex = priorToIndexes[j];
priorEvents[j] = newEvents.get(priorIndex);
}
priorEventMap.put(newEvent, priorEvents);
}
}
// Save old data to be removed next time we get posted results
lastOldData = oldData;
}
public void update(EventBean[] newData, EventBean[] oldData, PriorEventBufferChangeCaptureMulti capture)
{
// Remove last old data posted in previous post
if (lastOldData != null)
{
for (int i = 0; i < lastOldData.length; i++)
{
EventBean oldDataItem = lastOldData[i];
priorEventMap.remove(oldDataItem);
capture.removed(oldDataItem);
}
}
// Post new data to rolling buffer starting with the oldest
if (newData != null)
{
for (int i = 0; i < newData.length; i++)
{
EventBean newEvent = newData[i];
// Add new event
newEvents.add(newEvent);
// Save prior index events in array
EventBean[] priorEvents = new EventBean[priorToIndexesSize];
for (int j = 0; j < priorToIndexesSize; j++)
{
int priorIndex = priorToIndexes[j];
priorEvents[j] = newEvents.get(priorIndex);
}
priorEventMap.put(newEvent, priorEvents);
capture.added(newEvent, priorEvents);
}
}
// Save old data to be removed next time we get posted results
lastOldData = oldData;
}
public EventBean getRelativeToEvent(EventBean theEvent, int priorToIndex)
{
if (priorToIndex >= priorToIndexesSize)
{
throw new IllegalArgumentException("Index " + priorToIndex + " not allowed, max size is " + priorToIndexesSize);
}
EventBean[] priorEvents = priorEventMap.get(theEvent);
if (priorEvents == null)
{
throw new IllegalStateException("Event not currently in collection, event=" + theEvent);
}
return priorEvents[priorToIndex];
}
public EventBean getRelativeToEnd(EventBean theEvent, int index)
{
// No requirements to return events related to the end of the current buffer
return null;
}
public int getWindowToEventCount(EventBean evalEvent)
{
// No requirements to return events related to the end of the current buffer
return 0;
}
public Iterator<EventBean> getWindowToEvent(Object evalEvent)
{
// No requirements to return events related to the end of the current buffer
return null;
}
public Collection<EventBean> getWindowToEventCollReadOnly(Object evalEvent)
{
// No requirements to return events related to the end of the current buffer
return null;
}
public void destroy()
{
// No action required
}
public Map<EventBean, EventBean[]> getPriorEventMap() {
return priorEventMap;
}
public RollingEventBuffer getNewEvents() {
return newEvents;
}
}