package be.selckin.swu.events;
import com.google.common.base.Preconditions;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableMap.Builder;
import org.apache.wicket.Component;
import org.apache.wicket.IEventDispatcher;
import org.apache.wicket.event.IEvent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.lang.reflect.Method;
public class AnnotationEventDispatcher implements IEventDispatcher {
private static final Logger log = LoggerFactory.getLogger(AnnotationEventDispatcher.class);
private final LoadingCache<Class<?>, EventAnnotations> annotations = CacheBuilder.newBuilder().build(new ClassEventAnnotationsFunction());
@Override
public void dispatchEvent(Object sink, IEvent<?> event, Component component) {
if (sink != null && sink instanceof Component && event != null && event.getPayload() != null) {
EventCaller caller = annotations.getUnchecked(sink.getClass()).getCaller(event.getPayload().getClass());
if (caller != null)
caller.call(sink, event);
}
}
public static class EventAnnotations {
private final ImmutableMap<Class<?>, EventCaller> callers;
public EventAnnotations(ImmutableMap<Class<?>, EventCaller> methods) {
this.callers = Preconditions.checkNotNull(methods);
}
public EventCaller getCaller(Class<?> type) {
return callers.get(type);
}
}
private static class ClassEventAnnotationsFunction extends CacheLoader<Class<?>, EventAnnotations> {
@Override
public EventAnnotations load(Class<?> input) throws Exception {
Builder<Class<?>, EventCaller> builder = ImmutableMap.builder();
Class<?> current = input;
while (current != null && current != Object.class) {
for (Method method : current.getDeclaredMethods()) {
if (method.isAnnotationPresent(OnEvent.class)) {
if (!registerMethod(builder, method))
logInvalidMethod(current, method);
}
}
current = current.getSuperclass();
}
return new EventAnnotations(builder.build());
}
private boolean registerMethod(Builder<Class<?>, EventCaller> builder, Method method) {
if (!method.isAccessible())
method.setAccessible(true);
Class<?>[] parameters = method.getParameterTypes();
if (parameters.length == 1) {
Class<?> payloadType = parameters[0];
if (!IEvent.class.isAssignableFrom(payloadType)) {
builder.put(payloadType, new PayloadOnlyCaller(method));
return true;
}
} else if (parameters.length == 2) {
Class<?> payloadType = parameters[0];
Class<?> iEventType = parameters[1];
if (!IEvent.class.isAssignableFrom(payloadType) && IEvent.class.isAssignableFrom(iEventType)) {
builder.put(payloadType, new PayloadAndEventCaller(method));
return true;
}
}
return false;
}
private void logInvalidMethod(Class<?> current, Method method) {
log.warn("Invalid @OnEvent method {} in {}", method, current);
}
}
public static class EventException extends RuntimeException {
public EventException(String message, Throwable cause) {
super(message, cause);
}
}
}