net.mostlyoriginal.api.event.dispatcher.FastEventDispatcher Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of contrib-eventbus Show documentation
Show all versions of contrib-eventbus Show documentation
Drop-in low throughput synchronous immediate delivery eventbus for artemis-odb.
package net.mostlyoriginal.api.event.dispatcher;
import com.artemis.utils.Bag;
import net.mostlyoriginal.api.event.common.Event;
import net.mostlyoriginal.api.event.common.EventDispatchStrategy;
import net.mostlyoriginal.api.event.common.EventListener;
import net.mostlyoriginal.api.utils.ClassHierarchy;
import net.mostlyoriginal.api.utils.BagUtils;
import java.util.IdentityHashMap;
/**
* Faster event dispatcher.
*
* Should suffice for most prototyping usecases.
*
* @Author DaanVanYperen
*/
public class FastEventDispatcher implements EventDispatchStrategy {
final ClassHierarchy classHierarchy = new ClassHierarchy();
/** Listeners of exact event class. Excludes superclasses. */
final IdentityHashMap, Bag> listenerCache = new IdentityHashMap<>();
/** Listeners flattened to include full hierarchy per calling event. */
final IdentityHashMap, Bag> hierarchicalListenerCache = new IdentityHashMap<>();
@Override
public void register(EventListener listener) {
if ( listener == null ) throw new NullPointerException("Listener required.");
// Bind listener to the related event class.
Bag listenersFor = getListenersFor(listener.getParameterType(), true);
if ( !listenersFor.contains(listener)) {
listenersFor.add(listener);
// the hierarchical cache is now out of date. purrrrrrrrge!
invalidateHierarchicalCache();
}
}
private void invalidateHierarchicalCache() {
if ( hierarchicalListenerCache.size() > 0 ) {
hierarchicalListenerCache.clear();
}
}
/**
* Get listeners for class (non hierarical).
*
* @param aClass Class to fetch listeners for.
* @param createIfMissing instance empty bag when not exist.
* @return Listener, or null
if missing and not allowed to create.
*/
protected Bag getListenersFor(Class> aClass, boolean createIfMissing) {
Bag listeners = listenerCache.get(aClass);
if (listeners == null && createIfMissing) {
// if listener is missing, prep an empty bag.
listeners = new Bag<>(4);
listenerCache.put(aClass, listeners);
}
return listeners;
}
/**
* Get listeners for class, including all superclasses.
* Backed by cache.
*
* Not sorted!
*
* @param aClass Class to fetch listeners for.
* @return Bag of listeners, empty if none found.
*/
protected Bag getListenersForHierarchical(Class> aClass) {
Bag listeners = hierarchicalListenerCache.get(aClass);
if (listeners == null) {
listeners = getListenersForHierarchicalUncached(aClass);
// presort the listeners by priority.
// Should speed things up in the case of an oft reused superclass.
BagUtils.sort(listeners);
hierarchicalListenerCache.put(aClass, listeners);
}
return listeners;
}
/**
* Get listeners for class, including all superclasses,
* sorted by priority.
*
* Not backed by cache.
*
* @param aClass Class to fetch listeners for.
* @return Bag of listeners, empty if none found.
*/
private Bag getListenersForHierarchicalUncached(Class> aClass) {
// get hierarchy for event.
final Class>[] classes = classHierarchy.of(aClass);
// step through hierarchy back to front, fetching the listeners for each step.
final Bag hierarchicalListeners = new Bag<>(4);
for (Class> c : classes) {
final Bag listeners = getListenersFor(c, false);
if (listeners != null) {
hierarchicalListeners.addAll(listeners);
}
}
// sort by priority.
BagUtils.sort(hierarchicalListeners);
return hierarchicalListeners;
}
/**
* Dispatch event to registered listeners.
* Events are called on the call stack, avoid deeply nested or circular event calls.
*/
@Override
public void dispatch(Event event) {
if ( event == null ) throw new NullPointerException("Event required.");
final Bag listeners = getListenersForHierarchical(event.getClass());
/** Fetch hierarchical list of listeners. */
Object[] data = listeners.getData();
for (int i = 0, s = listeners.size(); i < s; i++) {
final EventListener listener = (EventListener) data[i];
if (listener != null) {
listener.handle(event);
}
}
}
@Override
public void process() {
// not interested in this stuff
}
@Override
public T dispatch(Class type) {
throw new UnsupportedOperationException("This dispatcher doesn't dispatch events by type!");
}
}