/*
*
*/
package vroom.common.utilities.callbacks;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.EnumMap;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* <code>CallBackManagerDelegate</code> is a delegate class that is used to register and notify callbacks in a procedure<br/>
* Beware that by default the callbacks will be associated with the <b>instance</b> of the event object
* {@link #registerCallback(ICallback, ICallbackEventTypes)}. It is therefore recommended to use enumerations as event
* types.
*
* @author Victor Pillac, <a href="http://uniandes.edu.co">Universidad de Los Andes</a> - <a
* href="http://copa.uniandes.edu.co">Copa</a>, <a href="http://www.emn.fr">Ecole des Mines de Nantes</a>-<a
* href="http://www.irccyn.ec-nantes.fr/irccyn/d/en/equipes/Slp">SLP</a>
* @version 1.0 #updated 16-Feb-2010 10:07:40 a.m.
* @param <S>
* the type of source that will generate events
* @param <T>
* the type of {@link ICallbackEventTypes} that will be used to associate callbacks to events
*/
public class CallbackManagerDelegate<S, T extends ICallbackEventTypes> {
private Thread mCallbackThread;
private final String mThreadLabel;
private final CallbackStack mCallbackStack;
private final CallbackComparator mComparator = new CallbackComparator();
/**
* A map containing the callbacks associated with events in priority order (callbacks with lower values for
* {@link ICallback#getPriority()} are first)
*/
private Map<T, List<ICallback<S, T>>> mCallbackMapping;
/**
* Creates a new generic <code>CallbackManagerDelegate</code> able to manage any type of event
*/
public CallbackManagerDelegate(String label) {
this(new HashMap<T, List<ICallback<S, T>>>(), label);
}
/**
* Creates a new <code>CallbackManagerDelegate</code> optimized for the events of the given enumeration
*
* @param eventsEnum
* an enumeration of the events that will be managed by this instance
* @param label
* a label for the callback thread
*/
@SuppressWarnings("unchecked")
public <K extends Enum<K>> CallbackManagerDelegate(Class<K> eventsEnum, String label) {
if (!ICallbackEventTypes.class.isAssignableFrom(eventsEnum)) {
throw new IllegalArgumentException(
"Argument eventsEnum must implement ICallbackEvent<S,T>");
} else {
this.mCallbackMapping = (Map<T, List<ICallback<S, T>>>) new EnumMap<K, List<ICallback<S, T>>>(
eventsEnum);
}
this.mCallbackStack = new CallbackStack();
mThreadLabel = label;
}
/**
* Creates a new <code>CallbackManagerDelegate</code> based on the given <code>map</code>
*
* @param map
* the map that will be used to associate callbacks to events
*/
public CallbackManagerDelegate(Map<T, List<ICallback<S, T>>> map, String label) {
this.mCallbackMapping = map;
this.mCallbackStack = new CallbackStack();
mThreadLabel = label;
}
private void setupAndStartThread() {
this.mCallbackThread = new Thread(this.mCallbackStack, mThreadLabel + "-cb");
this.mCallbackThread.setDaemon(true);
this.mCallbackThread.start();
}
/**
* Call @link{ICallback<S,T>#execute(ICallbackEvent<S,T>,Object)} on the callbacks associated with the given
* <code>event</code>, passing the optional <code>parameter</code>
*
* @param event
* the event that has occurred and for which the associated callbacks will be executed
*/
public void callbacks(ICallbackEvent<S, T> event) {
if (this.mCallbackMapping.containsKey(event.getType())) {
List<ICallback<S, T>> callbacks = this.mCallbackMapping.get(event.getType());
if (callbacks != null) {
for (ICallback<S, T> cb : callbacks) {
if (cb.isExecutedSynchronously()) {
cb.execute(event);
} else {
this.mCallbackStack.put(cb, event);
}
}
}
}
}
/**
* Register the given <code>callback</code> to the <code>eventType</code><br/>
*
* @param callback
* the callback that will be associated with <code>event</code>
* @param eventType
* the event to which the given <code>callback</code> will be associated
*/
public void registerCallback(ICallback<S, T> callback, T eventType) {
List<ICallback<S, T>> callbacks;
// Lazy start of the callback thread
if (mCallbackThread == null && !callback.isExecutedSynchronously())
setupAndStartThread();
if (this.mCallbackMapping.containsKey(eventType)) {
callbacks = this.mCallbackMapping.get(eventType);
} else {
// create and add the callback list
callbacks = new ArrayList<ICallback<S, T>>();
this.mCallbackMapping.put(eventType, callbacks);
}
// add the callback to the list
callbacks.add(callback);
// Sort the callback list
Collections.sort(callbacks, mComparator);
}
/**
* Unregister the given <code>callback</code> from the <code>eventType</code> <br/>
* If <code>callback</code> was registered more than once then only the first reference will be removed
*
* @param callback
* the callback that will no longer be associated with <code>event</code>
* @param eventType
* the considered event
*/
public void unregisterCallback(ICallback<S, T> callback, T eventType) {
List<ICallback<S, T>> callbacks;
if (this.mCallbackMapping.containsKey(eventType)) {
callbacks = this.mCallbackMapping.get(eventType);
callbacks.remove(callback);
}
}
/**
* A {@link Comparator} for {@link ICallback} instances used to order the list of callbacks associated with an event
*
* @author Victor Pillac, <a href="http://uniandes.edu.co">Universidad de Los Andes</a> - <a
* href="http://copa.uniandes.edu.co">Copa</a>, <a href="http://www.emn.fr">Ecole des Mines de Nantes</a>-<a
* href= "http://www.irccyn.ec-nantes.fr/irccyn/d/en/equipes/Slp">SLP</a>
*/
private class CallbackComparator implements Comparator<ICallback<S, T>> {
@Override
public int compare(ICallback<S, T> c1, ICallback<S, T> c2) {
if (c1 == null) {
return 1;
}
if (c2 == null) {
return -1;
}
return c2.getPriority() - c1.getPriority();
}
}
/**
* Will cause the suspension of the callback thread
*/
public void stop() {
this.mCallbackStack.stop();
}
@Override
public void finalize() throws Throwable {
stop();
super.finalize();
}
}// end CallBackManagerDelegate