/*
***************************************************************************************
* 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.filter;
import com.espertech.esper.client.EventBean;
import com.espertech.esper.client.EventType;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.locks.ReadWriteLock;
/**
* Mapping of event type to a tree-like structure
* containing filter parameter constants in indexes {@link FilterParamIndexBase} and filter callbacks in {@link FilterHandleSetNode}.
* <p>
* This class evaluates events for the purpose of filtering by (1) looking up the event's {@link EventType}
* and (2) asking the subtree for this event type to evaluate the event.
* <p>
* The class performs all the locking required for multithreaded access.
*/
public class EventTypeIndex implements EventEvaluator {
private Map<EventType, FilterHandleSetNode> eventTypes;
private ReadWriteLock eventTypesRWLock;
public EventTypeIndex(FilterServiceGranularLockFactory lockFactory) {
eventTypes = new HashMap<EventType, FilterHandleSetNode>();
eventTypesRWLock = lockFactory.obtainNew();
}
/**
* Destroy the service.
*/
public void destroy() {
eventTypes.clear();
}
/**
* Add a new event type to the index and use the specified node for the root node of its subtree.
* If the event type already existed, the method will throw an IllegalStateException.
*
* @param eventType is the event type to be added to the index
* @param rootNode is the root node of the subtree for filter constant indizes and callbacks
*/
public void add(EventType eventType, FilterHandleSetNode rootNode) {
eventTypesRWLock.writeLock().lock();
try {
if (eventTypes.containsKey(eventType)) {
throw new IllegalStateException("Event type already in index, add not performed, type=" + eventType);
}
eventTypes.put(eventType, rootNode);
} finally {
eventTypesRWLock.writeLock().unlock();
}
}
public void removeType(EventType type) {
eventTypesRWLock.writeLock().lock();
try {
eventTypes.remove(type);
} finally {
eventTypesRWLock.writeLock().unlock();
}
}
/**
* Returns the root node for the given event type, or null if this event type has not been seen before.
*
* @param eventType is an event type
* @return the subtree's root node
*/
public FilterHandleSetNode get(EventType eventType) {
eventTypesRWLock.readLock().lock();
FilterHandleSetNode result = eventTypes.get(eventType);
eventTypesRWLock.readLock().unlock();
return result;
}
public void matchEvent(EventBean theEvent, Collection<FilterHandle> matches) {
EventType eventType = theEvent.getEventType();
// Attempt to match exact type
matchType(eventType, theEvent, matches);
// No supertype means we are done
if (eventType.getSuperTypes() == null) {
return;
}
for (Iterator<EventType> it = eventType.getDeepSuperTypes(); it.hasNext(); ) {
EventType superType = it.next();
matchType(superType, theEvent, matches);
}
}
/**
* Returns the current size of the known event types.
*
* @return collection size
*/
protected int size() {
return eventTypes.size();
}
protected int getFilterCountApprox() {
int count = 0;
eventTypesRWLock.readLock().lock();
try {
for (Map.Entry<EventType, FilterHandleSetNode> entry : eventTypes.entrySet()) {
count += entry.getValue().getFilterCallbackCount();
for (FilterParamIndexBase index : entry.getValue().getIndizes()) {
count += index.sizeExpensive();
}
}
} finally {
eventTypesRWLock.readLock().unlock();
}
return count;
}
private void matchType(EventType eventType, EventBean eventBean, Collection<FilterHandle> matches) {
eventTypesRWLock.readLock().lock();
FilterHandleSetNode rootNode = null;
try {
rootNode = eventTypes.get(eventType);
} finally {
eventTypesRWLock.readLock().unlock();
}
// If the top class node is null, no filters have yet been registered for this event type.
// In this case, log a message and done.
if (rootNode == null) {
return;
}
rootNode.matchEvent(eventBean, matches);
}
}