package spimedb.server;
import java.util.*;
/**
* Adapted from http://www.recursiverobot.com/post/86215392884/witness-a-simple-android-and-java-event-emitter
* TODO separate this into a single-thread and multithread implementation
*/
public class EventEmitter {
public static final boolean CONCURRENT = true;
/** Observes events emitted by EventEmitter */
public interface EventObserver<C> {
void event(Class<? extends C> event, Object... args);
}
private final Map<Class<?>, List<EventObserver>> events;
private final Deque<Object[]> pendingOps = new ArrayDeque();
/** EventEmitter that allows unknown events; must use concurrent collection
* for multithreading since new event classes may be added at any time.
*/
public EventEmitter() {
/*if (Parameters.THREADS > 1)
events = new ConcurrentHashMap<>();
else*/
events = new HashMap<>();
}
/** EventEmitter with a fixed set of known events; the 'events' map
* can then be made unmodifiable and non-concurrent for speed. */
public EventEmitter(Class... knownEventClasses) {
events = new HashMap(knownEventClasses.length);
for (Class c : knownEventClasses) {
events.put(c, newObserverList());
}
}
protected static List<EventObserver> newObserverList() {
return new ArrayList();
/*return Concurrent ?
new ArrayList() : Collections.synchronizedList(new ArrayList());*/
}
public final boolean isActive(final Class event) {
if (events.get(event)!=null)
if (!events.get(event).isEmpty())
return true;
return false;
}
//apply pending on/off changes when synchronizing, ex: in-between memory cycles
public void synch() {
synchronized (pendingOps) {
if (!pendingOps.isEmpty()) {
for (Object[] o : pendingOps) {
Class c = (Class)o[1];
EventObserver d = (EventObserver)o[2];
if ((Boolean)o[0]) {
_on(c,d);
}
else {
_off(c,d);
}
}
}
pendingOps.clear();
}
}
public <C> void on(final Class<? extends C> event, final EventObserver<? extends C> o) {
if (CONCURRENT) {
_on(event, o);
}
else {
synchronized(pendingOps) {
pendingOps.add(new Object[] { true, event, o });
}
}
}
private <C> void _on(final Class<? extends C> event, final EventObserver<? extends C> o) {
if (events.containsKey(event))
events.get(event).add(o);
else {
List<EventObserver> a = newObserverList();
a.add(o);
events.put(event, a);
}
}
/**
* @param event
* @param o
* @return whether it was removed
*/
public <C> void off(final Class<? extends C> event, final EventObserver<? extends C> o) {
if (CONCURRENT) {
_off(event, o);
}
else {
synchronized(pendingOps) {
pendingOps.add(new Object[] { false, event, o });
}
}
}
private void _off(final Class<?> event, final EventObserver o) {
if (null == event || null == o)
throw new RuntimeException("Invalid parameter");
if (!events.containsKey(event))
throw new RuntimeException("Unknown event: " + event);
events.get(event).remove(o);
/*if (!removed) {
throw new RuntimeException("EventObserver " + o + " was not registered for events");
}*/
}
/** for enabling many events at the same time */
public void set(final EventObserver o, final boolean enable, final Class... events) {
for (final Class c : events) {
if (enable)
on(c, o);
else
off(c, o);
}
}
public void emit(final Class eventClass, final Object... params) {
List<EventObserver> observers = events.get(eventClass);
if ((observers == null) || (observers.isEmpty())) return;
int n = observers.size();
for (int i = 0; i < n; i++) {
EventObserver m = observers.get(i);
m.event(eventClass, params);
}
}
// public void emitLater(final Class eventClass, final Object... params) {
// if (hasAnyOn(eventClass)) {
// Platform.runLater(new Runnable() {
//
// @Override
// public void run() {
// emit(eventClass, params);
// }
//
// });
// }
// }
public Map<Class<?>, List<EventObserver>> getObservers() {
return events;
}
}