com.savl.ripple.client.pubsub.Publisher Maven / Gradle / Ivy
package com.savl.ripple.client.pubsub;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.logging.Level;
import java.util.logging.Logger;
public class Publisher {
static final Logger logger = Logger.getLogger(Publisher.class.getName());
private void log(Level level, String message, Object... params) {
logger.log(level, message, params);
}
public static interface Callback {
public void called(T args);
}
public static interface ErrBack extends Callback {
public void erred(RuntimeException args);
}
public > void on(Class key, T cb) {
add(key, cb);
}
public > void on(Class key, CallbackContext executor, T cb) {
add(key, executor, cb);
}
public > void once(final Class key, final T cb) {
once(key, null, cb);
}
public > void once(final Class key, CallbackContext executor, final T cb) {
add(key, executor, cb, true);
}
public > int emit(Class key, A args) {
if (logger.isLoggable(Level.FINE)) {
log(Level.FINE, "Emitting {0} from thread: {1}", key.getSimpleName(), Thread.currentThread());
}
int executed = 0;
CallbackList callbacks = (cbs.get(key));
if (callbacks != null) {
CallbackList copy = new CallbackList(callbacks);
for (ContextedCallback pair : copy) {
boolean removed = false;
CallbackContext context = pair.context;
if (context == null) {
execute(args, pair);
executed++;
} else {
if (context.shouldExecute()) {
context.execute(pair.runnableWrappedCallback(args));
executed++;
} else if (context.shouldRemove()) {
callbacks.remove(pair);
removed = true;
}
}
// we only want to call remove once
if (pair.oneShot && !removed) {
callbacks.remove(pair);
}
}
}
return executed;
}
@SuppressWarnings("unchecked")
public static void execute(Object args, ContextedCallback pair) {
pair.callback.called(args);
}
private static class ContextedCallback {
CallbackContext context;
Callback callback;
boolean oneShot;
public ContextedCallback(Callback callback, CallbackContext context, boolean oneShot) {
this.context = context;
this.callback = callback;
this.oneShot = oneShot;
}
public Runnable runnableWrappedCallback(final Object args) {
return new Runnable() {
@Override
public void run() {
execute(args, ContextedCallback.this);
}
};
}
}
private static class CallbackList extends ArrayList {
public CallbackList() {}
public CallbackList(CallbackList callbacks) {
super(callbacks);
}
@Override
public ContextedCallback get(int index) {
return super.get(index);
}
public boolean remove(Callback t) {
Iterator iter = iterator();
while (iter.hasNext()) {
ContextedCallback next = iter.next();
if (next.callback == t) {
iter.remove();
return true;
}
}
return false;
}
public void add(CallbackContext exec, Callback cb, boolean oneShot) {
add(new ContextedCallback(cb, exec, oneShot));
}
}
private class DefaultCallbackListMap extends HashMap, CallbackList> {
public CallbackList getDefault(Class extends Callback> key) {
CallbackList list = super.get(key);
if (list == null) {
CallbackList newList = new CallbackList();
put(key, newList);
return newList;
}
return list;
}
}
@SuppressWarnings("MismatchedQueryAndUpdateOfCollection")
private final DefaultCallbackListMap cbs = new DefaultCallbackListMap();
private CallbackList listFor(Class extends Callback> key) {
return cbs.getDefault(key);
}
private > void add(Class key, Callback cb) {
add(key, null, cb, false);
}
private > void add(Class key, CallbackContext executor, Callback cb) {
add(key, executor, cb, false);
}
private > void add(Class key, CallbackContext executor, final Callback cb, boolean b) {
listFor(key).add(executor, cb, b);
}
public > boolean removeListener(Class key, Callback cb) {
return listFor(key).remove(cb);
}
public void clearAllListeners() {
cbs.clear();
}
}