All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.mostlyoriginal.api.event.dispatcher.FastEventDispatcher Maven / Gradle / Ivy

Go to download

Drop-in low throughput synchronous immediate delivery eventbus for artemis-odb.

There is a newer version: 2.5.0
Show newest version
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!");
	}

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy