Register handlers declared in superclasses and interfaces
This experimental extension to the listener registration process walks the inheritance tree and searches for handler declarations.
This commit is contained in:
parent
b758f4cef1
commit
5c1c0abdf0
|
@ -2,9 +2,19 @@ package dev.kske.eventbus.core;
|
||||||
|
|
||||||
import java.lang.System.Logger;
|
import java.lang.System.Logger;
|
||||||
import java.lang.System.Logger.Level;
|
import java.lang.System.Logger.Level;
|
||||||
|
import java.lang.annotation.Annotation;
|
||||||
import java.lang.reflect.InvocationTargetException;
|
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.concurrent.ConcurrentHashMap;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Event listeners can be registered at an event bus to be notified when an event is dispatched.
|
* 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();
|
priority = listener.getClass().getAnnotation(Priority.class).value();
|
||||||
|
|
||||||
registeredListeners.add(listener);
|
registeredListeners.add(listener);
|
||||||
for (var method : listener.getClass().getDeclaredMethods()) {
|
for (var method : getHandlerMethods(listener.getClass())) {
|
||||||
Event annotation = method.getAnnotation(Event.class);
|
Event annotation = method.getAnnotation(Event.class);
|
||||||
|
|
||||||
// Skip methods without annotations
|
|
||||||
if (annotation == null)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// Initialize and bind the handler
|
// Initialize and bind the handler
|
||||||
var handler = new EventHandler(listener, method, annotation, polymorphic, priority);
|
var handler = new EventHandler(listener, method, annotation, polymorphic, priority);
|
||||||
bindings.putIfAbsent(handler.getEventType(), new TreeSet<>());
|
bindings.putIfAbsent(handler.getEventType(), new TreeSet<>());
|
||||||
|
@ -211,6 +217,44 @@ public final class EventBus {
|
||||||
listener.getClass().getName());
|
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.
|
* Removes a specific listener from this event bus.
|
||||||
*
|
*
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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!");
|
||||||
|
}
|
||||||
|
}
|
|
@ -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();
|
||||||
|
}
|
Loading…
Reference in New Issue