diff --git a/README.md b/README.md index 3345abb..1844da4 100644 --- a/README.md +++ b/README.md @@ -51,6 +51,15 @@ public class SimpleEventListener implements EventListener { In this case, an event bus is created and used locally. In a more sophisticated example the class would acquire an external event bus that is used by multiple classes. +## Event handlers for subtypes + +On certain occasions its practical for an event handler to accept both events of the specified type, as well as subclasses of that event. +To include subtypes for an event handler, use the `includeSubtypes` parameter as follows: + +```java +@Event(includeSubtypes = true) +``` + ## Parameter-less event handlers In some cases an event handler is not interested in the dispatched event instance. @@ -82,7 +91,7 @@ To include it inside your project, just add the Maven repository and the depende dev.kske event-bus - 0.0.3 + 0.0.4 ``` diff --git a/pom.xml b/pom.xml index 116c3c6..7e54c52 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ dev.kske event-bus - 0.0.3 + 0.0.4 Event Bus An event handling framework for Java utilizing annotations. diff --git a/src/main/java/dev/kske/eventbus/Event.java b/src/main/java/dev/kske/eventbus/Event.java index dc208af..ebfb0b9 100644 --- a/src/main/java/dev/kske/eventbus/Event.java +++ b/src/main/java/dev/kske/eventbus/Event.java @@ -36,6 +36,13 @@ public @interface Event { */ int priority() default 100; + /** + * Defines whether instances of subtypes of the event type are dispatched to the event handler. + * + * @since 0.0.4 + */ + boolean includeSubtypes() default false; + /** * Defines the event type the handler listens to. If this value is set, the handler is not * allowed to declare parameters. diff --git a/src/main/java/dev/kske/eventbus/EventBus.java b/src/main/java/dev/kske/eventbus/EventBus.java index cdb93af..a069e00 100644 --- a/src/main/java/dev/kske/eventbus/EventBus.java +++ b/src/main/java/dev/kske/eventbus/EventBus.java @@ -31,7 +31,7 @@ public final class EventBus { return singletonInstance; } - private final Map, Collection> bindings + private final Map, TreeSet> bindings = new ConcurrentHashMap<>(); private final Set registeredListeners = ConcurrentHashMap.newKeySet(); @@ -55,8 +55,20 @@ public final class EventBus { * @since 0.0.1 */ private List getHandlersFor(Class eventClass) { - return bindings.containsKey(eventClass) ? new ArrayList<>(bindings.get(eventClass)) - : new ArrayList<>(); + + // Get handlers defined for the event class + Set handlers + = bindings.containsKey(eventClass) ? bindings.get(eventClass) + : new TreeSet<>(); + + // Get subtype handlers + for (var binding : bindings.entrySet()) + if (binding.getKey().isAssignableFrom(eventClass)) + for (var handler : binding.getValue()) + if (handler.includeSubtypes()) + handlers.add(handler); + + return new ArrayList<>(handlers); } /** diff --git a/src/main/java/dev/kske/eventbus/EventHandler.java b/src/main/java/dev/kske/eventbus/EventHandler.java index f4aa355..2dfb257 100644 --- a/src/main/java/dev/kske/eventbus/EventHandler.java +++ b/src/main/java/dev/kske/eventbus/EventHandler.java @@ -117,6 +117,12 @@ final class EventHandler implements Comparable { */ int getPriority() { return annotation.priority(); } + /** + * @return whether this handler includes subtypes + * @since 0.0.4 + */ + boolean includeSubtypes() { return annotation.includeSubtypes(); } + /** * @return the event type this handler listens to * @since 0.0.3 diff --git a/src/test/java/dev/kske/eventbus/EventBusTest.java b/src/test/java/dev/kske/eventbus/EventBusTest.java index adb2d57..c754bb9 100644 --- a/src/test/java/dev/kske/eventbus/EventBusTest.java +++ b/src/test/java/dev/kske/eventbus/EventBusTest.java @@ -1,6 +1,6 @@ package dev.kske.eventbus; -import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.*; import org.junit.jupiter.api.*; @@ -21,18 +21,29 @@ class EventBusTest implements EventListener { @Test void testDispatch() { + EventBus.getInstance().dispatch(new SimpleEventSub()); EventBus.getInstance().dispatch(new SimpleEvent()); } - @Event(priority = 50) - private void onSimpleEventSecond(SimpleEvent event) { + @Event( + eventType = SimpleEvent.class, + includeSubtypes = true, + priority = 200 + ) + private void onSimpleEventFirst() { ++hits; - assertEquals(2, hits); + assertTrue(hits == 1 || hits == 2); } @Event(eventType = SimpleEvent.class, priority = 150) - private void onSimpleEventFirst() { + private void onSimpleEventSecond() { ++hits; - assertEquals(1, hits); + assertEquals(3, hits); + } + + @Event(priority = 50) + private void onSimpleEventThird(SimpleEvent event) { + ++hits; + assertEquals(4, hits); } } diff --git a/src/test/java/dev/kske/eventbus/SimpleEventSub.java b/src/test/java/dev/kske/eventbus/SimpleEventSub.java new file mode 100644 index 0000000..3030eaf --- /dev/null +++ b/src/test/java/dev/kske/eventbus/SimpleEventSub.java @@ -0,0 +1,9 @@ +package dev.kske.eventbus; + +/** + * Subclass of {@link SimpleEvent} for testing purposes. + * + * @author Kai S. K. Engelbart + * @since 0.0.4 + */ +public class SimpleEventSub extends SimpleEvent {}