
io.divide.otto.AnnotatedHandlerFinder Maven / Gradle / Ivy
The newest version!
/*
* Copyright (C) 2012 Square, Inc.
* Copyright (C) 2007 The Guava Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.divide.otto;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
/**
* Helper methods for finding methods annotated with {@link io.divide.otto.Produce} and {@link io.divide.otto.Subscribe}.
*
* @author Cliff Biffle
* @author Louis Wasserman
* @author Jake Wharton
*/
final public class AnnotatedHandlerFinder {
/** Cache event bus producer methods for each class. */
private static final Map, Map, Method>> PRODUCERS_CACHE =
new HashMap, Map, Method>>();
/** Cache event bus subscriber methods for each class. */
private static final Map, Map, Set>> SUBSCRIBERS_CACHE =
new HashMap, Map, Set>>();
/**
* Load all methods annotated with {@link io.divide.otto.Produce} or {@link io.divide.otto.Subscribe} into their respective caches for the
* specified class.
*/
private static void loadAnnotatedMethods(Class> listenerClass) {
Map, Set> subscriberMethods = new HashMap, Set>();
Map, Method> producerMethods = new HashMap, Method>();
for (Method method : listenerClass.getDeclaredMethods()) {
if (method.isAnnotationPresent(Subscribe.class)) {
Class>[] parameterTypes = method.getParameterTypes();
if (parameterTypes.length != 1) {
throw new IllegalArgumentException("Method " + method + " has @Subscribe annotation but requires "
+ parameterTypes.length + " arguments. Methods must require a single argument.");
}
Class> eventType = parameterTypes[0];
if (eventType.isInterface()) {
throw new IllegalArgumentException("Method " + method + " has @Subscribe annotation on " + eventType
+ " which is an interface. Subscription must be on a concrete class type.");
}
if ((method.getModifiers() & Modifier.PUBLIC) == 0) {
throw new IllegalArgumentException("Method " + method + " has @Subscribe annotation on " + eventType
+ " but is not 'public'.");
}
Set methods = subscriberMethods.get(eventType);
if (methods == null) {
methods = new HashSet();
subscriberMethods.put(eventType, methods);
}
methods.add(method);
} else if (method.isAnnotationPresent(Produce.class)) {
Class>[] parameterTypes = method.getParameterTypes();
if (parameterTypes.length != 0) {
throw new IllegalArgumentException("Method " + method + "has @Produce annotation but requires "
+ parameterTypes.length + " arguments. Methods must require zero arguments.");
}
if (method.getReturnType() == Void.class) {
throw new IllegalArgumentException("Method " + method
+ " has a return type of void. Must declare a non-void type.");
}
Class> eventType = method.getReturnType();
if (eventType.isInterface()) {
throw new IllegalArgumentException("Method " + method + " has @Produce annotation on " + eventType
+ " which is an interface. Producers must return a concrete class type.");
}
if (eventType.equals(Void.TYPE)) {
throw new IllegalArgumentException("Method " + method + " has @Produce annotation but has no return type.");
}
if ((method.getModifiers() & Modifier.PUBLIC) == 0) {
throw new IllegalArgumentException("Method " + method + " has @Produce annotation on " + eventType
+ " but is not 'public'.");
}
if (producerMethods.containsKey(eventType)) {
throw new IllegalArgumentException("Producer for type " + eventType + " has already been registered.");
}
producerMethods.put(eventType, method);
}
}
PRODUCERS_CACHE.put(listenerClass, producerMethods);
SUBSCRIBERS_CACHE.put(listenerClass, subscriberMethods);
}
/** This implementation finds all methods marked with a {@link io.divide.otto.Produce} annotation. */
public static Map, EventProducer> findAllProducers(Object listener) {
final Class> listenerClass = listener.getClass();
Map, EventProducer> handlersInMethod = new HashMap, EventProducer>();
if (!PRODUCERS_CACHE.containsKey(listenerClass)) {
loadAnnotatedMethods(listenerClass);
}
Map, Method> methods = PRODUCERS_CACHE.get(listenerClass);
if (!methods.isEmpty()) {
for (Map.Entry, Method> e : methods.entrySet()) {
EventProducer producer = new EventProducer(listener, e.getValue());
handlersInMethod.put(e.getKey(), producer);
}
}
return handlersInMethod;
}
/** This implementation finds all methods marked with a {@link io.divide.otto.Subscribe} annotation. */
static Map, Set> findAllSubscribers(Object listener) {
Class> listenerClass = listener.getClass();
Map, Set> handlersInMethod = new HashMap, Set>();
if (!SUBSCRIBERS_CACHE.containsKey(listenerClass)) {
loadAnnotatedMethods(listenerClass);
}
Map, Set> methods = SUBSCRIBERS_CACHE.get(listenerClass);
if (!methods.isEmpty()) {
for (Map.Entry, Set> e : methods.entrySet()) {
Set handlers = new HashSet();
for (Method m : e.getValue()) {
handlers.add(new EventHandler(listener, m));
}
handlersInMethod.put(e.getKey(), handlers);
}
}
return handlersInMethod;
}
private AnnotatedHandlerFinder() {
// No instances.
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy