/*
***************************************************************************************
* 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.collection.OneEventCollection;
import com.espertech.esper.core.context.util.AgentInstanceViewFactoryChainContext;
import com.espertech.esper.event.EventAdapterService;
import com.espertech.esper.view.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.*;
/**
* This view simply adds a property to the events posted to it. This is useful for the group-merge views.
*/
public final class AddPropertyValueOptionalView extends ViewSupport implements CloneableView, StoppableView {
private final AgentInstanceViewFactoryChainContext agentInstanceContext;
private final String[] propertyNames;
private final Object propertyValues;
private final EventType eventType;
private boolean mustAddProperty;
// Keep a history of posted old events to avoid reconstructing the event
// and adhere to the contract of posting the same reference to child views.
// Only for must-add-property.
private Map<EventBean, EventBean> newToOldEventMap;
/**
* Constructor.
*
* @param propertyNames is the name of the field that is added to any events received by this view.
* @param mergeValues is the values of the field that is added to any events received by this view.
* @param mergedResultEventType is the event type that the merge view reports to it's child views
* @param agentInstanceContext contains required view services
*/
public AddPropertyValueOptionalView(AgentInstanceViewFactoryChainContext agentInstanceContext, String[] propertyNames, Object mergeValues, EventType mergedResultEventType) {
this.propertyNames = propertyNames;
this.propertyValues = mergeValues;
this.eventType = mergedResultEventType;
this.agentInstanceContext = agentInstanceContext;
this.newToOldEventMap = Collections.emptyMap();
}
public View cloneView() {
return new AddPropertyValueOptionalView(agentInstanceContext, propertyNames, propertyValues, eventType);
}
public void setParent(Viewable parent) {
if (log.isDebugEnabled()) {
log.debug(".setParent parent=" + parent);
}
super.setParent(parent);
if (parent.getEventType() != eventType) {
mustAddProperty = true;
newToOldEventMap = new HashMap<EventBean, EventBean>();
} else {
mustAddProperty = false;
}
}
/**
* Returns field name for which to set the merge value for.
*
* @return field name to use to set value
*/
public final String[] getPropertyNames() {
return propertyNames;
}
/**
* Returns the value to set for the field.
*
* @return value to set
*/
public final Object getPropertyValues() {
return propertyValues;
}
public final void update(EventBean[] newData, EventBean[] oldData) {
if (!mustAddProperty) {
updateChildren(newData, oldData);
return;
}
EventBean[] newEvents = null;
EventBean[] oldEvents = null;
if (newData != null) {
newEvents = new EventBean[newData.length];
int index = 0;
for (EventBean newEvent : newData) {
EventBean theEvent = addProperty(newEvent, propertyNames, propertyValues, eventType, agentInstanceContext.getStatementContext().getEventAdapterService());
newEvents[index++] = theEvent;
newToOldEventMap.put(newEvent, theEvent);
}
}
if (oldData != null) {
oldEvents = new EventBean[oldData.length];
int index = 0;
for (EventBean oldEvent : oldData) {
EventBean outgoing = newToOldEventMap.remove(oldEvent);
if (outgoing != null) {
oldEvents[index++] = outgoing;
} else {
EventBean theEvent = addProperty(oldEvent, propertyNames, propertyValues, eventType, agentInstanceContext.getStatementContext().getEventAdapterService());
oldEvents[index++] = theEvent;
}
}
}
updateChildren(newEvents, oldEvents);
}
public final EventType getEventType() {
return eventType;
}
public final Iterator<EventBean> iterator() {
final Iterator<EventBean> parentIterator = parent.iterator();
return new Iterator<EventBean>() {
public boolean hasNext() {
return parentIterator.hasNext();
}
public EventBean next() {
EventBean nextEvent = parentIterator.next();
if (mustAddProperty) {
return addProperty(nextEvent, propertyNames, propertyValues, eventType,
agentInstanceContext.getStatementContext().getEventAdapterService());
} else {
return nextEvent;
}
}
public void remove() {
throw new UnsupportedOperationException();
}
};
}
@Override
public void stop() {
if (!newToOldEventMap.isEmpty()) {
OneEventCollection oldEvents = new OneEventCollection();
for (Map.Entry<EventBean, EventBean> oldEvent : newToOldEventMap.entrySet()) {
oldEvents.add(oldEvent.getValue());
}
if (!oldEvents.isEmpty()) {
updateChildren(null, oldEvents.toArray());
}
newToOldEventMap.clear();
}
}
/**
* Add a property to the event passed in.
*
* @param originalEvent - event to add property to
* @param propertyNames - names of properties to add
* @param propertyValues - value of properties to add
* @param targetEventType - new event type
* @param eventAdapterService - service for generating events and handling event types
* @return event with added property
*/
protected static EventBean addProperty(EventBean originalEvent,
String[] propertyNames,
Object propertyValues,
EventType targetEventType,
EventAdapterService eventAdapterService) {
Map<String, Object> values = new HashMap<String, Object>();
if (propertyValues instanceof MultiKeyUntyped) {
MultiKeyUntyped props = (MultiKeyUntyped) propertyValues;
Object[] propertyValuesArr = props.getKeys();
for (int i = 0; i < propertyNames.length; i++) {
values.put(propertyNames[i], propertyValuesArr[i]);
}
} else {
values.put(propertyNames[0], propertyValues);
}
return eventAdapterService.adapterForTypedWrapper(originalEvent, values, targetEventType);
}
public final String toString() {
return this.getClass().getName() + " propertyNames=" + Arrays.toString(propertyNames) +
" propertyValue=" + propertyValues;
}
private static final Logger log = LoggerFactory.getLogger(AddPropertyValueOptionalView.class);
}