uk.co.codera.lang.Announcer Maven / Gradle / Ivy
package uk.co.codera.lang;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.List;
public class Announcer {
@FunctionalInterface
public static interface ExceptionHandler {
void onException(Throwable e);
}
private static final class DoNothingExceptionHandler implements ExceptionHandler {
@Override
public void onException(Throwable e) {
// do nothing
}
}
public static final ExceptionHandler EXCEPTION_HANDLER_DO_NOTHING = new DoNothingExceptionHandler();
private final T proxy;
private final List listeners = new ArrayList<>();
private ExceptionHandler exceptionHandler = EXCEPTION_HANDLER_DO_NOTHING;
private Announcer(Class extends T> listenerType) {
this.proxy = listenerType.cast(Proxy.newProxyInstance(listenerType.getClassLoader(),
new Class>[] { listenerType }, invocationHandler()));
}
public static Announcer to(Class listenerType) {
return new Announcer<>(listenerType);
}
public static Announcer to(Class listenerType, Iterable listeners) {
return to(listenerType).addListeners(listeners);
}
public Announcer addListener(T listener) {
this.listeners.add(listener);
return this;
}
public Announcer addListeners(Iterable listeners) {
for (T listener : listeners) {
addListener(listener);
}
return this;
}
public Announcer removeListener(T listener) {
this.listeners.remove(listener);
return this;
}
public int numberListeners() {
return this.listeners.size();
}
public Announcer useExceptionHandler(ExceptionHandler handler) {
this.exceptionHandler = handler;
return this;
}
public T announce() {
return this.proxy;
}
private void announce(Method m, Object[] args) {
for (T listener : this.listeners) {
invokeListener(listener, m, args);
}
}
@SuppressWarnings("squid:S1166")
private void invokeListener(T listener, Method method, Object[] args) {
try {
method.invoke(listener, args);
} catch (IllegalAccessException | InvocationTargetException e) {
Throwable cause = e.getCause();
if (cause instanceof Error) {
throw (Error) cause;
}
this.exceptionHandler.onException(cause);
}
}
private InvocationHandler invocationHandler() {
return (proxiedObject, method, args) -> {
announce(method, args);
return null;
};
}
}