Skip to content
This repository was archived by the owner on Apr 30, 2019. It is now read-only.

Allow for explicit class targeting when registering producer/subscriber methods #182

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -130,8 +130,7 @@ private static void loadAnnotatedMethods(Class<?> listenerClass,
}

/** This implementation finds all methods marked with a {@link Produce} annotation. */
static Map<Class<?>, EventProducer> findAllProducers(Object listener) {
final Class<?> listenerClass = listener.getClass();
static Map<Class<?>, EventProducer> findAllProducers(Object listener, Class<?> listenerClass) {
Map<Class<?>, EventProducer> handlersInMethod = new HashMap<Class<?>, EventProducer>();

Map<Class<?>, Method> methods = PRODUCERS_CACHE.get(listenerClass);
Expand All @@ -150,8 +149,7 @@ static Map<Class<?>, EventProducer> findAllProducers(Object listener) {
}

/** This implementation finds all methods marked with a {@link Subscribe} annotation. */
static Map<Class<?>, Set<EventHandler>> findAllSubscribers(Object listener) {
Class<?> listenerClass = listener.getClass();
static Map<Class<?>, Set<EventHandler>> findAllSubscribers(Object listener, Class<?> listenerClass) {
Map<Class<?>, Set<EventHandler>> handlersInMethod = new HashMap<Class<?>, Set<EventHandler>>();

Map<Class<?>, Set<Method>> methods = SUBSCRIBERS_CACHE.get(listenerClass);
Expand Down
67 changes: 58 additions & 9 deletions otto/src/main/java/com/squareup/otto/Bus.java
Original file line number Diff line number Diff line change
Expand Up @@ -178,16 +178,43 @@ public Bus(ThreadEnforcer enforcer, String identifier) {
* If any producers are registering for types which already have subscribers, each subscriber will be called with
* the value from the result of calling the producer.
*
* @param object object whose handler methods should be registered.
* @throws NullPointerException if the object is null.
* @param object object whose producer and handler methods should be registered.
* @throws NullPointerException if {@code object} is null.
*/
public void register(Object object) {
if (object == null) {
throw new NullPointerException("Object to register must not be null.");
}
register(object, object.getClass());
}

/**
* Registers all handler methods on {@code object} to receive events and producer methods to provide events.
* <p>
* If any subscribers are registering for types which already have a producer they will be called immediately
* with the result of calling that producer.
* <p>
* If any producers are registering for types which already have subscribers, each subscriber will be called with
* the value from the result of calling the producer.
*
* @param object object used to trigger registered producer and handler methods. The {@code object}s class must be
* assignable to objects of the type {@code listenerClass}.
* @param listenerClass class whose producer and handler methods should be registered.
* @throws NullPointerException if {@code object} or {@code listenerClass} is null.
*/
public void register(Object object, Class<?> listenerClass) {
if (object == null) {
throw new NullPointerException("Object to trigger register methods not be null.");
}
if (listenerClass == null) {
throw new NullPointerException("Class to register must not be null.");
}
if (!listenerClass.isAssignableFrom(object.getClass())) {
throw new IllegalArgumentException("Class to unregister must be an instance of or assignable from Object class.");
}
enforcer.enforce(this);

Map<Class<?>, EventProducer> foundProducers = handlerFinder.findAllProducers(object);
Map<Class<?>, EventProducer> foundProducers = handlerFinder.findAllProducers(object, listenerClass);
for (Class<?> type : foundProducers.keySet()) {

final EventProducer producer = foundProducers.get(type);
Expand All @@ -206,7 +233,7 @@ public void register(Object object) {
}
}

Map<Class<?>, Set<EventHandler>> foundHandlersMap = handlerFinder.findAllSubscribers(object);
Map<Class<?>, Set<EventHandler>> foundHandlersMap = handlerFinder.findAllSubscribers(object, listenerClass);
for (Class<?> type : foundHandlersMap.keySet()) {
Set<EventHandler> handlers = handlersByType.get(type);
if (handlers == null) {
Expand Down Expand Up @@ -258,36 +285,58 @@ private void dispatchProducerResultToHandler(EventHandler handler, EventProducer
*
* @param object object whose producer and handler methods should be unregistered.
* @throws IllegalArgumentException if the object was not previously registered.
* @throws NullPointerException if the object is null.
* @throws NullPointerException if {@code object} is null.
*/
public void unregister(Object object) {
if (object == null) {
throw new NullPointerException("Object to unregister must not be null.");
}
unregister(object, object.getClass());
}

/**
* Unregisters all producer and handler methods on a registered {@code object}.
*
* @param object object used to trigger registered producer and handler methods. The {@code object}s class must be
* assignable to objects of the type {@code listenerClass}.
* @param listenerClass class whose producer and handler methods should be unregistered.
* @throws IllegalArgumentException if the object was not previously registered.
* @throws NullPointerException if {@code object} or {@code listenerClass} is null.
*/
public void unregister(Object object, Class<?> listenerClass) {
if (object == null) {
throw new NullPointerException("Object to trigger register methods not be null.");
}
if (listenerClass == null) {
throw new NullPointerException("Class to unregister must not be null.");
}
if (!listenerClass.isAssignableFrom(object.getClass())) {
throw new IllegalArgumentException("Class to unregister must be an instance of or assignable from Object class.");
}
enforcer.enforce(this);

Map<Class<?>, EventProducer> producersInListener = handlerFinder.findAllProducers(object);
Map<Class<?>, EventProducer> producersInListener = handlerFinder.findAllProducers(object, listenerClass);
for (Map.Entry<Class<?>, EventProducer> entry : producersInListener.entrySet()) {
final Class<?> key = entry.getKey();
EventProducer producer = getProducerForEventType(key);
EventProducer value = entry.getValue();

if (value == null || !value.equals(producer)) {
throw new IllegalArgumentException(
"Missing event producer for an annotated method. Is " + object.getClass()
"Missing event producer for an annotated method. Is " + listenerClass
+ " registered?");
}
producersByType.remove(key).invalidate();
}

Map<Class<?>, Set<EventHandler>> handlersInListener = handlerFinder.findAllSubscribers(object);
Map<Class<?>, Set<EventHandler>> handlersInListener = handlerFinder.findAllSubscribers(object, listenerClass);
for (Map.Entry<Class<?>, Set<EventHandler>> entry : handlersInListener.entrySet()) {
Set<EventHandler> currentHandlers = getHandlersForEventType(entry.getKey());
Collection<EventHandler> eventMethodsInListener = entry.getValue();

if (currentHandlers == null || !currentHandlers.containsAll(eventMethodsInListener)) {
throw new IllegalArgumentException(
"Missing event handler for an annotated method. Is " + object.getClass()
"Missing event handler for an annotated method. Is " + listenerClass
+ " registered?");
}

Expand Down
14 changes: 7 additions & 7 deletions otto/src/main/java/com/squareup/otto/HandlerFinder.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,20 +22,20 @@
/** Finds producer and subscriber methods. */
interface HandlerFinder {

Map<Class<?>, EventProducer> findAllProducers(Object listener);

Map<Class<?>, Set<EventHandler>> findAllSubscribers(Object listener);
Map<Class<?>, EventProducer> findAllProducers(Object listener, Class<?> targetClass);

Map<Class<?>, Set<EventHandler>> findAllSubscribers(Object listener, Class<?> targetClass);

HandlerFinder ANNOTATED = new HandlerFinder() {

@Override
public Map<Class<?>, EventProducer> findAllProducers(Object listener) {
return AnnotatedHandlerFinder.findAllProducers(listener);
public Map<Class<?>, EventProducer> findAllProducers(Object listener, Class<?> targetClass) {
return AnnotatedHandlerFinder.findAllProducers(listener, targetClass);
}

@Override
public Map<Class<?>, Set<EventHandler>> findAllSubscribers(Object listener) {
return AnnotatedHandlerFinder.findAllSubscribers(listener);
public Map<Class<?>, Set<EventHandler>> findAllSubscribers(Object listener, Class<?> targetClass) {
return AnnotatedHandlerFinder.findAllSubscribers(listener, targetClass);
}
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -98,13 +98,13 @@ public int compare(EventHandler eventHandler, EventHandler eventHandler1) {
};

@Override
public Map<Class<?>, EventProducer> findAllProducers(Object listener) {
return HandlerFinder.ANNOTATED.findAllProducers(listener);
public Map<Class<?>, EventProducer> findAllProducers(Object listener, Class<?> targetClass) {
return HandlerFinder.ANNOTATED.findAllProducers(listener, targetClass);
}

@Override
public Map<Class<?>, Set<EventHandler>> findAllSubscribers(Object listener) {
Map<Class<?>, Set<EventHandler>> found = HandlerFinder.ANNOTATED.findAllSubscribers(listener);
public Map<Class<?>, Set<EventHandler>> findAllSubscribers(Object listener, Class<?> targetClass) {
Map<Class<?>, Set<EventHandler>> found = HandlerFinder.ANNOTATED.findAllSubscribers(listener, targetClass);
Map<Class<?>, Set<EventHandler>> sorted = new HashMap<Class<?>, Set<EventHandler>>();
for (Map.Entry<Class<?>, Set<EventHandler>> entry : found.entrySet()) {
SortedSet<EventHandler> handlers = new TreeSet<EventHandler>(handlerComparator);
Expand Down