Register handlers declared in superclasses and interfaces

This experimental extension to the listener registration process walks
the inheritance tree and searches for handler declarations.
Kai S. K. Engelbart 2021-05-16 14:23:03 +02:00
parent b758f4cef1
commit 5c1c0abdf0
Signed by: kske
GPG Key ID: 8BEB13EC5DF7EF13
4 changed files with 125 additions and 6 deletions

View File

@ -2,9 +2,19 @@ package dev.kske.eventbus.core;
import java.lang.System.Logger;
import java.lang.System.Logger.Level;
import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationTargetException;
import java.util.*;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.TreeSet;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
/**
* Event listeners can be registered at an event bus to be notified when an event is dispatched.
@ -188,13 +198,9 @@ public final class EventBus {
priority = listener.getClass().getAnnotation(Priority.class).value();
registeredListeners.add(listener);
for (var method : listener.getClass().getDeclaredMethods()) {
for (var method : getHandlerMethods(listener.getClass())) {
Event annotation = method.getAnnotation(Event.class);
// Skip methods without annotations
if (annotation == null)
continue;
// Initialize and bind the handler
var handler = new EventHandler(listener, method, annotation, polymorphic, priority);
bindings.putIfAbsent(handler.getEventType(), new TreeSet<>());
@ -211,6 +217,44 @@ public final class EventBus {
listener.getClass().getName());
}
/**
* Searches for event handlers declared inside the inheritance hierarchy of an event listener.
*
* This handlers defined in both superclasses and interfaces, regardless of visibility.
*
* @param listenerClass the class to inspect
* @return all event handlers defined for the given listener
* @since 1.2.0
*/
private List<Method> getHandlerMethods(Class<?> listenerClass) {
// Get declared handlers
List<Method> methods = getMethodsAnnotatedWith(listenerClass, Event.class);
// Recursively add superclass handlers
if(listenerClass.getSuperclass() != null)
methods.addAll(getHandlerMethods(listenerClass.getSuperclass()));
// Recursively add handlers defined by interfaces
for(Class<?> iClass : listenerClass.getInterfaces())
methods.addAll(getHandlerMethods(iClass));
return methods;
}
/**
* Searches for methods with a specific annotation declared inside a class.
*
* @param enclosingClass the class to inspect
* @param annotationClass the annotation to look for
* @return all methods matching the search criteria
* @since 1.2.0
*/
private List<Method> getMethodsAnnotatedWith(Class<?> enclosingClass, Class<? extends Annotation> annotationClass) {
return Arrays.stream(enclosingClass.getDeclaredMethods())
.filter(m -> m.isAnnotationPresent(annotationClass)).collect(Collectors.toList());
}
/**
* Removes a specific listener from this event bus.
*

View File

@ -0,0 +1,36 @@
package dev.kske.eventbus.core;
import static org.junit.jupiter.api.Assertions.*;
import org.junit.jupiter.api.Test;
/**
* Tests whether event handlers correctly handle inheritance.
*
* @author Kai S. K. Engelbart
* @since 1.2.0
*/
class InheritanceTest extends SimpleEventListenerBase implements SimpleEventListenerInterface {
EventBus bus;
boolean hit;
@Test
void test() {
bus = new EventBus();
bus.registerListener(this);
bus.dispatch(new SimpleEvent());
assertTrue(hit);
}
@Override
@Event(SimpleEvent.class)
public void onSimpleEventAbstractHandler() {
System.out.println("Subclass!");
}
@Override
public void onSimpleEventInterfaceHandler() {
hit = true;
}
}

View File

@ -0,0 +1,23 @@
package dev.kske.eventbus.core;
/**
* An interface defining a single handler for {@link SimpleEvent}.
*
* Used for testing event listener inheritance.
*
* @author Kai S. K. Engelbart
* @since 1.2.0
*/
abstract class SimpleEventListenerBase {
@Event(SimpleEvent.class)
void onSimpleEventAbstractHandler() {
System.out.println("Test");
}
@Event(SimpleEvent.class)
private void onSimpleEventPrivate() {
System.out.println("Private Handler!");
}
}

View File

@ -0,0 +1,16 @@
package dev.kske.eventbus.core;
/**
* An interface defining a single handler for {@link SimpleEvent}.
*
* Used for testing event listener inheritance.
*
* @author Kai S. K. Engelbart
* @since 1.2.0
*/
interface SimpleEventListenerInterface {
@Event(SimpleEvent.class)
void onSimpleEventInterfaceHandler();
}