package nars.util;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Deque;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import nars.config.Parameters;
/**
* 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 {
/** Observes events emitted by EventEmitter */
public interface EventObserver<C> {
public void event(Class<? extends C> event, Object[] args);
}
private final Map<Class<?>, List<EventObserver>> events;
private 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<>();
events = new ConcurrentHashMap<>();
}
/** 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 List<EventObserver> newObserverList() {
return new ArrayList();
/*return Parameters.THREADS == 1 ?
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 void on(final Class<?> event, final EventObserver 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 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);
try {
events.get(event).remove(o);
} catch(Exception ex) { }
/*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++) {
try{
EventObserver m = observers.get(i);
m.event(eventClass, params);
}catch(Exception ex){}
}
}
}