/**************************************************************************************
* 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.std;
import com.espertech.esper.client.EventBean;
import com.espertech.esper.client.EventType;
import com.espertech.esper.collection.MultiKey;
import com.espertech.esper.collection.MultiKeyUntyped;
import com.espertech.esper.core.context.util.AgentInstanceViewFactoryChainContext;
import com.espertech.esper.epl.expression.ExprEvaluator;
import com.espertech.esper.epl.expression.ExprEvaluatorContext;
import com.espertech.esper.epl.expression.ExprNode;
import com.espertech.esper.epl.expression.ExprNodeUtility;
import com.espertech.esper.event.EventBeanUtility;
import com.espertech.esper.view.CloneableView;
import com.espertech.esper.view.View;
import com.espertech.esper.view.ViewSupport;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import java.util.Arrays;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* This view retains the first event for each multi-key of distinct property values.
* <p>
* The view does not post a remove stream unless explicitly deleted from.
* <p>
* The view swallows any insert stream events that provide no new distinct set of property values.
*/
public class FirstUniqueByPropertyView extends ViewSupport implements CloneableView
{
private final ExprNode[] uniqueCriteria;
protected final ExprEvaluator[] uniqueCriteriaEval;
private final int numKeys;
private EventBean[] eventsPerStream = new EventBean[1];
protected final Map<Object, EventBean> firstEvents = new LinkedHashMap<Object, EventBean>();
protected final AgentInstanceViewFactoryChainContext agentInstanceViewFactoryContext;
/**
* Constructor.
* @param uniqueCriteria is the expressions from which to pull the unique value
*/
public FirstUniqueByPropertyView(ExprNode[] uniqueCriteria, AgentInstanceViewFactoryChainContext agentInstanceViewFactoryContext)
{
this.uniqueCriteria = uniqueCriteria;
this.uniqueCriteriaEval = ExprNodeUtility.getEvaluators(uniqueCriteria);
this.agentInstanceViewFactoryContext = agentInstanceViewFactoryContext;
numKeys = uniqueCriteria.length;
}
public View cloneView()
{
return new FirstUniqueByPropertyView(uniqueCriteria, agentInstanceViewFactoryContext);
}
/**
* Returns the expressions supplying the unique value to keep the most recent record for.
* @return expressions for unique value
*/
public final ExprNode[] getUniqueCriteria()
{
return uniqueCriteria;
}
public final EventType getEventType()
{
// The schema is the parent view's schema
return parent.getEventType();
}
public final void update(EventBean[] newData, EventBean[] oldData)
{
EventBean[] newDataToPost = null;
EventBean[] oldDataToPost = null;
if (oldData != null)
{
for (EventBean oldEvent : oldData)
{
// Obtain unique value
Object key = getUniqueKey(oldEvent);
// If the old event is the current unique event, remove and post as old data
EventBean lastValue = firstEvents.get(key);
if (lastValue != oldEvent)
{
continue;
}
if (oldDataToPost == null)
{
oldDataToPost = new EventBean[]{oldEvent};
}
else
{
oldDataToPost = EventBeanUtility.addToArray(oldDataToPost, oldEvent);
}
firstEvents.remove(key);
internalHandleRemoved(key, lastValue);
}
}
if (newData != null)
{
for (EventBean newEvent : newData)
{
// Obtain unique value
Object key = getUniqueKey(newEvent);
// already-seen key
if (firstEvents.containsKey(key))
{
continue;
}
// store
firstEvents.put(key, newEvent);
internalHandleAdded(key, newEvent);
// Post the new value
if (newDataToPost == null)
{
newDataToPost = new EventBean[]{newEvent};
}
else
{
newDataToPost = EventBeanUtility.addToArray(newDataToPost, newEvent);
}
}
}
if ((this.hasViews()) && ((newDataToPost != null) || (oldDataToPost != null)))
{
updateChildren(newDataToPost, oldDataToPost);
}
}
public void internalHandleRemoved(Object key, EventBean lastValue) {
// no action required
}
public void internalHandleAdded(Object key, EventBean newEvent) {
// no action required
}
public final Iterator<EventBean> iterator()
{
return firstEvents.values().iterator();
}
public final String toString()
{
return this.getClass().getName() + " uniqueCriteria=" + Arrays.toString(uniqueCriteria);
}
protected Object getUniqueKey(EventBean theEvent)
{
eventsPerStream[0] = theEvent;
if (numKeys == 1) {
return uniqueCriteriaEval[0].evaluate(eventsPerStream, true, agentInstanceViewFactoryContext);
}
Object[] values = new Object[numKeys];
for (int i = 0; i < numKeys; i++)
{
values[i] = uniqueCriteriaEval[i].evaluate(eventsPerStream, true, agentInstanceViewFactoryContext);
}
return new MultiKeyUntyped(values);
}
/**
* Returns true if empty.
* @return true if empty
*/
public boolean isEmpty()
{
return firstEvents.isEmpty();
}
private static final Log log = LogFactory.getLog(FirstUniqueByPropertyView.class);
}