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

added support for inheritance #109

Closed
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 @@ -19,6 +19,7 @@

import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
Expand Down Expand Up @@ -49,7 +50,12 @@ private static void loadAnnotatedMethods(Class<?> listenerClass) {
Map<Class<?>, Set<Method>> subscriberMethods = new HashMap<Class<?>, Set<Method>>();
Map<Class<?>, Method> producerMethods = new HashMap<Class<?>, Method>();

for (Method method : listenerClass.getDeclaredMethods()) {
Set<Method> allMethods = new HashSet<Method>();
Collections.addAll(allMethods, listenerClass.getDeclaredMethods());

addSuperMethodsRecursively(allMethods, listenerClass);

for (Method method : allMethods) {
if (method.isAnnotationPresent(Subscribe.class)) {
Class<?>[] parameterTypes = method.getParameterTypes();
if (parameterTypes.length != 1) {
Expand Down Expand Up @@ -110,6 +116,14 @@ private static void loadAnnotatedMethods(Class<?> listenerClass) {
SUBSCRIBERS_CACHE.put(listenerClass, subscriberMethods);
}

private static void addSuperMethodsRecursively(Set<Method> allMethods, Class<?> listenerClass) {
Class<?> superclass = listenerClass.getSuperclass();
if (superclass != null && listenerClass.isAnnotationPresent(ProcessSuperClass.class)) {
Collections.addAll(allMethods, superclass.getDeclaredMethods());
addSuperMethodsRecursively(allMethods, superclass);
}
}

/** This implementation finds all methods marked with a {@link Produce} annotation. */
static Map<Class<?>, EventProducer> findAllProducers(Object listener) {
final Class<?> listenerClass = listener.getClass();
Expand Down
16 changes: 16 additions & 0 deletions library/src/main/java/com/squareup/otto/ProcessSuperClass.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.squareup.otto;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
* Allows a class to tell otto to look for {@link Produce} and {@link Subscribe} annotated methods in super classes.
* Recursion only proceeds on classes which are annotated with this annotation, meaning in an inheritance
* hierarchy each sub-class must have this annotation.
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface ProcessSuperClass {
}
55 changes: 55 additions & 0 deletions library/src/test/java/com/squareup/otto/ProcessSuperClassTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package com.squareup.otto;

import org.junit.Test;
import static org.fest.assertions.api.Assertions.assertThat;

public class ProcessSuperClassTest {

private final Bus bus = new Bus(ThreadEnforcer.ANY, "test-bus");

@Test
public void allowsSuperClassSubscription() {

class Parent {

protected boolean messageReceived;

@Subscribe
public void onMessage(String message) {
messageReceived = true;
}
}

@ProcessSuperClass
class Child extends Parent {}

final Child child = new Child();
bus.register(child);
bus.post("Foo");

assertThat(child.messageReceived).isTrue();
}

@Test
public void doesNotProcessSuperClassWhenAnnotationIsMissing() {

class Parent {

protected boolean messageReceived;

@Subscribe
public void onMessage(String message) {
messageReceived = true;
}
}

class Child extends Parent {}

final Child child = new Child();
bus.register(child);
bus.post("Foo");

assertThat(child.messageReceived).isFalse();
}

}
5 changes: 4 additions & 1 deletion website/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -76,10 +76,13 @@ <h4>Subscribing</h4>
to an instance of the class in which the previous method was present, we can register using the
following:</p>
<pre class="prettyprint">bus.register(this);</pre>
<p><strong>Registering will only find methods on the immediate class type.</strong> Unlike the Guava event
<p><strong>Registering defaults to only find methods on the immediate class type.</strong> Unlike the Guava event
bus, Otto will not traverse the class hierarchy and add methods from base classes or interfaces that are
annotated. This is an explicit design decision to improve performance of the library as well as keep your
code simple and unambiguous.</p>
<p>In case you want to register inherited methods anyway you can annotate your classes with
<code>@ProcessSuperClass</code>. Otto will recursively traverse the inheritance hierarchy until it finds a class
which is not being annotated with this annotation or no more super classes can be found.</p>
<p>Remember to also call the <code>unregister</code> method when appropriate. See the sample application
included in the download for a complete example.</p>

Expand Down