/* *************************************************************************************** * 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.std; import com.espertech.esper.client.EventBean; import com.espertech.esper.client.EventType; import com.espertech.esper.collection.MultiKeyUntyped; 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.epl.expression.core.ExprNodeUtility; import com.espertech.esper.event.EventBeanUtility; import com.espertech.esper.metrics.instrumentation.InstrumentationHelper; import com.espertech.esper.view.*; import java.util.Arrays; import java.util.HashMap; import java.util.Iterator; 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, DataWindowView { private final FirstUniqueByPropertyViewFactory viewFactory; protected final ExprEvaluator[] uniqueCriteriaEval; private EventBean[] eventsPerStream = new EventBean[1]; protected final Map<Object, EventBean> firstEvents = new HashMap<Object, EventBean>(); protected final AgentInstanceViewFactoryChainContext agentInstanceViewFactoryContext; public FirstUniqueByPropertyView(FirstUniqueByPropertyViewFactory viewFactory, AgentInstanceViewFactoryChainContext agentInstanceViewFactoryContext) { this.viewFactory = viewFactory; this.uniqueCriteriaEval = ExprNodeUtility.getEvaluators(viewFactory.criteriaExpressions); this.agentInstanceViewFactoryContext = agentInstanceViewFactoryContext; } public View cloneView() { return viewFactory.makeView(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 viewFactory.criteriaExpressions; } 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, FirstUniqueByPropertyViewFactory.NAME, newData, 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))) { if (InstrumentationHelper.ENABLED) { InstrumentationHelper.get().qViewIndicate(this, FirstUniqueByPropertyViewFactory.NAME, newDataToPost, oldDataToPost); } updateChildren(newDataToPost, oldDataToPost); if (InstrumentationHelper.ENABLED) { InstrumentationHelper.get().aViewIndicate(); } } if (InstrumentationHelper.ENABLED) { InstrumentationHelper.get().aViewProcessIRStream(); } } 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(viewFactory.criteriaExpressions); } protected Object getUniqueKey(EventBean theEvent) { eventsPerStream[0] = theEvent; if (uniqueCriteriaEval.length == 1) { return uniqueCriteriaEval[0].evaluate(eventsPerStream, true, agentInstanceViewFactoryContext); } Object[] values = new Object[uniqueCriteriaEval.length]; for (int i = 0; i < uniqueCriteriaEval.length; 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(); } public void visitView(ViewDataVisitor viewDataVisitor) { viewDataVisitor.visitPrimary(firstEvents, true, FirstUniqueByPropertyViewFactory.NAME, firstEvents.size(), firstEvents.size()); } public ViewFactory getViewFactory() { return viewFactory; } }