/************************************************************************************** * 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.filter; import com.espertech.esper.client.EventType; import com.espertech.esper.collection.Pair; import java.util.*; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; /** * This class is responsible for changes to {@link EventTypeIndex} for addition and removal of filters. * It delegates the work to make modifications to the filter parameter tree to an {@link IndexTreeBuilder}. * It enforces a policy that a filter callback can only be added once. */ public class EventTypeIndexBuilder { private final Map<FilterHandle, Pair<FilterValueSet, IndexTreePath>> callbacks; private final Lock callbacksLock; private final EventTypeIndex eventTypeIndex; /** * Constructor - takes the event type index to manipulate as its parameter. * @param eventTypeIndex - index to manipulate */ public EventTypeIndexBuilder(EventTypeIndex eventTypeIndex) { this.eventTypeIndex = eventTypeIndex; this.callbacks = new HashMap<FilterHandle, Pair<FilterValueSet, IndexTreePath>>(); this.callbacksLock = new ReentrantLock(); } /** * Destroy the service. */ public void destroy() { callbacks.clear(); } /** * Add a filter to the event type index structure, and to the filter subtree. * Throws an IllegalStateException exception if the callback is already registered. * @param filterValueSet is the filter information * @param filterCallback is the callback */ public final void add(FilterValueSet filterValueSet, FilterHandle filterCallback) { EventType eventType = filterValueSet.getEventType(); // Check if a filter tree exists for this event type FilterHandleSetNode rootNode = eventTypeIndex.get(eventType); // Make sure we have a root node if (rootNode == null) { callbacksLock.lock(); try { rootNode = eventTypeIndex.get(eventType); if (rootNode == null) { rootNode = new FilterHandleSetNode(); eventTypeIndex.add(eventType, rootNode); } } finally { callbacksLock.unlock(); } } // Make sure the filter callback doesn't already exist callbacksLock.lock(); try { if (callbacks.containsKey(filterCallback)) { throw new IllegalStateException("Callback for filter specification already exists in collection"); } } finally { callbacksLock.unlock(); } // Now add to tree IndexTreeBuilder treeBuilder = new IndexTreeBuilder(); IndexTreePath path = treeBuilder.add(filterValueSet, filterCallback, rootNode); callbacksLock.lock(); try { callbacks.put(filterCallback, new Pair<FilterValueSet, IndexTreePath>(filterValueSet, path)); } finally { callbacksLock.unlock(); } } /** * Remove a filter callback from the given index node. * @param filterCallback is the callback to remove */ public final void remove(FilterHandle filterCallback) { Pair<FilterValueSet, IndexTreePath> pair = null; callbacksLock.lock(); try { pair = callbacks.get(filterCallback); } finally { callbacksLock.unlock(); } if (pair == null) { return; } EventType eventType = pair.getFirst().getEventType(); FilterHandleSetNode rootNode = eventTypeIndex.get(eventType); // Now remove from tree if (rootNode != null) { IndexTreeBuilder treeBuilder = new IndexTreeBuilder(); treeBuilder.remove(eventType, filterCallback, pair.getSecond(), rootNode); } // Remove from callbacks list callbacksLock.lock(); try { callbacks.remove(filterCallback); } finally { callbacksLock.unlock(); } } /** * Returns filters for the statement ids. * @param statementIds ids to take * @return set of filters for taken statements */ public final FilterSet take(Set<String> statementIds) { List<FilterSetEntry> list = new ArrayList<FilterSetEntry>(); callbacksLock.lock(); try { for (Map.Entry<FilterHandle, Pair<FilterValueSet, IndexTreePath>> entry : callbacks.entrySet()) { Pair<FilterValueSet, IndexTreePath> pair = entry.getValue(); if (statementIds.contains(entry.getKey().getStatementId())) { list.add(new FilterSetEntry(entry.getKey(), pair.getFirst())); EventType eventType = pair.getFirst().getEventType(); FilterHandleSetNode rootNode = eventTypeIndex.get(eventType); // Now remove from tree IndexTreeBuilder treeBuilder = new IndexTreeBuilder(); treeBuilder.remove(eventType, entry.getKey(), pair.getSecond(), rootNode); } } for (FilterSetEntry removed : list) { callbacks.remove(removed.getHandle()); } } finally { callbacksLock.unlock(); } return new FilterSet(list); } /** * Add the filters, from previously-taken filters. * @param filterSet to add */ public void apply(FilterSet filterSet) { for (FilterSetEntry entry : filterSet.getFilters()) { add(entry.getFilterValueSet(), entry.getHandle()); } } }