From 3a6ebe9a1903506272334eedea1d1428a8e9a35c Mon Sep 17 00:00:00 2001 From: kske Date: Mon, 15 Feb 2021 10:55:30 +0100 Subject: [PATCH 1/4] Replace includeSubtypes with @Polymorphic The new @Polymorphic annotation serves the exact same purpose as @Event(includeSubtypes = true), but should be easier to read in complex handler declarations. It has to be used in conjunction with the @Event annotation, not instead of it. --- README.md | 18 +++++----- .../dev/kske/eventbus/ap/EventProcessor.java | 5 +-- .../java/dev/kske/eventbus/core/Event.java | 9 +---- .../java/dev/kske/eventbus/core/EventBus.java | 2 +- .../dev/kske/eventbus/core/EventHandler.java | 35 +++++++++---------- .../dev/kske/eventbus/core/Polymorphic.java | 20 +++++++++++ .../dev/kske/eventbus/core/DispatchTest.java | 3 +- 7 files changed, 54 insertions(+), 38 deletions(-) create mode 100644 event-bus-core/src/main/java/dev/kske/eventbus/core/Polymorphic.java diff --git a/README.md b/README.md index 1bd8862..62ed2c8 100644 --- a/README.md +++ b/README.md @@ -55,21 +55,23 @@ Note that creating static event handlers like this ```java @Event -private static void onSimpleEvent(SimpleEvent event) ... +private static void onSimpleEvent(SimpleEvent event) { ... } ``` is technically possible, however you would still have to create an instance of the event listener to register it at an event bus. -## Event handlers for subtypes +## Polymorphic Event Handlers On certain occasions it's 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: +To include subtypes for an event handler, use the `@Polymorphic` annotation in addition to `@Event`: ```java -@Event(includeSubtypes = true) +@Event +@Polymorphic +private void onSimpleEvent(SimpleEvent event) { ... } ``` -## Event handler execution order +## Event Handler Execution Order Sometimes when using multiple handlers for one event, it might be useful to know in which order they will be executed. Event Bus provides a mechanism to ensure the correct propagation of events: the `priority`. @@ -86,7 +88,7 @@ Events are dispatched top-down, meaning the event handler with the highest prior If no priority is set or multiple handlers have the same priority, the order of execution is undefined. -## Parameter-less event handlers +## Parameter-Less Event Handlers In some cases an event handler is not interested in the dispatched event instance. To avoid declaring a useless parameter just to specify the event type of the handler, there is an alternative: @@ -100,7 +102,7 @@ private void onSimpleEvent() { Make sure that you **do not** declare both a parameter and the `eventType` value of the annotation, as this would be ambiguous. -## Event consumption +## Event Consumption In some cases it might be useful to stop the propagation of an event. Event Bus makes this possible with event consumption: @@ -152,7 +154,7 @@ Then, require the Event Bus Core module in your `module-info.java`: requires dev.kske.eventbus.core; ``` -# Compile-Time Error Checking with Event Bus AP +## Compile-Time Error Checking with Event Bus AP To assist you with writing event listeners, the Event Bus AP (Annotation Processor) module enforces correct usage of the `@Event` annotation during compile time. This reduces difficult-to-debug bugs that occur during runtime to compile-time errors which can be easily fixed. diff --git a/event-bus-ap/src/main/java/dev/kske/eventbus/ap/EventProcessor.java b/event-bus-ap/src/main/java/dev/kske/eventbus/ap/EventProcessor.java index 53ad735..9e661a6 100644 --- a/event-bus-ap/src/main/java/dev/kske/eventbus/ap/EventProcessor.java +++ b/event-bus-ap/src/main/java/dev/kske/eventbus/ap/EventProcessor.java @@ -73,8 +73,9 @@ public class EventProcessor extends AbstractProcessor { getTypeMirror(IEvent.class))) error(paramElement, "Parameter must implement IEvent"); - // Check for handlers for abstract types that don't include subtypes - if (!eventAnnotation.includeSubtypes() && paramType.getKind() == TypeKind.DECLARED) { + // Check for handlers for abstract types that aren't polymorphic + if (eventHandler.getAnnotation(Polymorphic.class) == null + && paramType.getKind() == TypeKind.DECLARED) { var declaredElement = ((DeclaredType) paramType).asElement(); if (declaredElement.getKind() == ElementKind.INTERFACE || declaredElement.getModifiers().contains(Modifier.ABSTRACT)) diff --git a/event-bus-core/src/main/java/dev/kske/eventbus/core/Event.java b/event-bus-core/src/main/java/dev/kske/eventbus/core/Event.java index 25ee7a3..1011d89 100644 --- a/event-bus-core/src/main/java/dev/kske/eventbus/core/Event.java +++ b/event-bus-core/src/main/java/dev/kske/eventbus/core/Event.java @@ -21,6 +21,7 @@ import java.lang.annotation.*; * * @author Kai S. K. Engelbart * @since 0.0.1 + * @see Polymorphic */ @Documented @Retention(RUNTIME) @@ -38,14 +39,6 @@ public @interface Event { */ int priority() default 100; - /** - * Defines whether instances of subtypes of the event type are dispatched to the event handler. - * - * @return whether the event handler includes subtypes - * @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/event-bus-core/src/main/java/dev/kske/eventbus/core/EventBus.java b/event-bus-core/src/main/java/dev/kske/eventbus/core/EventBus.java index 2248668..40ed4fa 100644 --- a/event-bus-core/src/main/java/dev/kske/eventbus/core/EventBus.java +++ b/event-bus-core/src/main/java/dev/kske/eventbus/core/EventBus.java @@ -104,7 +104,7 @@ public final class EventBus { for (var binding : bindings.entrySet()) if (binding.getKey().isAssignableFrom(eventClass)) for (var handler : binding.getValue()) - if (handler.includeSubtypes()) + if (handler.isPolymorphic()) handlers.add(handler); return new ArrayList<>(handlers); diff --git a/event-bus-core/src/main/java/dev/kske/eventbus/core/EventHandler.java b/event-bus-core/src/main/java/dev/kske/eventbus/core/EventHandler.java index aae6387..436bf70 100644 --- a/event-bus-core/src/main/java/dev/kske/eventbus/core/EventHandler.java +++ b/event-bus-core/src/main/java/dev/kske/eventbus/core/EventHandler.java @@ -13,10 +13,11 @@ import dev.kske.eventbus.core.Event.USE_PARAMETER; */ final class EventHandler implements Comparable { - private final EventListener listener; - private final Method method; - private final Event annotation; - private final Class eventType; + private final EventListener listener; + private final Method method; + private final Event annotation; + private final Class eventType; + private final boolean polymorphic; /** * Constructs an event handler. @@ -30,9 +31,9 @@ final class EventHandler implements Comparable { */ @SuppressWarnings("unchecked") EventHandler(EventListener listener, Method method, Event annotation) throws EventBusException { - this.listener = listener; - this.method = method; - this.annotation = annotation; + this.listener = listener; + this.method = method; + this.annotation = annotation; // Check for correct method signature and return type if (method.getParameterCount() == 0 && annotation.eventType().equals(USE_PARAMETER.class)) @@ -55,7 +56,8 @@ final class EventHandler implements Comparable { throw new EventBusException(param + " is not of type IEvent!"); eventType = (Class) param; } - this.eventType = eventType; + this.eventType = eventType; + polymorphic = method.isAnnotationPresent(Polymorphic.class); // Allow access if the method is non-public method.setAccessible(true); @@ -65,7 +67,7 @@ final class EventHandler implements Comparable { * Compares this to another event handler based on {@link Event#priority()}. In case of equal * priority a non-zero value based on hash codes is returned. *

- * This is used to retrieve event handlers in the correct order from a tree set. + * This is used to retrieve event handlers in order of descending priority from a tree set. * * @since 0.0.1 */ @@ -91,15 +93,11 @@ final class EventHandler implements Comparable { */ void execute(IEvent event) throws EventBusException { try { - if (annotation.eventType().equals(USE_PARAMETER.class)) + if (annotation.eventType() == USE_PARAMETER.class) method.invoke(listener, event); else method.invoke(listener); - } catch ( - IllegalAccessException - | IllegalArgumentException - | InvocationTargetException e - ) { + } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { throw new EventBusException("Failed to invoke event handler!", e); } } @@ -123,10 +121,11 @@ final class EventHandler implements Comparable { int getPriority() { return annotation.priority(); } /** - * @return whether this handler includes subtypes - * @since 0.0.4 + * @return whether this handler is polymorphic + * @since 1.0.0 + * @see Polymorphic */ - boolean includeSubtypes() { return annotation.includeSubtypes(); } + boolean isPolymorphic() { return polymorphic; } /** * @return the event type this handler listens to diff --git a/event-bus-core/src/main/java/dev/kske/eventbus/core/Polymorphic.java b/event-bus-core/src/main/java/dev/kske/eventbus/core/Polymorphic.java new file mode 100644 index 0000000..bbed5d8 --- /dev/null +++ b/event-bus-core/src/main/java/dev/kske/eventbus/core/Polymorphic.java @@ -0,0 +1,20 @@ +package dev.kske.eventbus.core; + +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.*; + +/** + * Allows an event handler to receive events that are subtypes of the declared event type. + *

+ * This is useful when defining an event handler for an interface or an abstract class. + * + * @author Kai S. K. Engelbart + * @since 1.0.0 + * @see Event + */ +@Documented +@Retention(RUNTIME) +@Target(METHOD) +public @interface Polymorphic {} diff --git a/event-bus-core/src/test/java/dev/kske/eventbus/core/DispatchTest.java b/event-bus-core/src/test/java/dev/kske/eventbus/core/DispatchTest.java index a683bc7..0239a8c 100644 --- a/event-bus-core/src/test/java/dev/kske/eventbus/core/DispatchTest.java +++ b/event-bus-core/src/test/java/dev/kske/eventbus/core/DispatchTest.java @@ -38,7 +38,8 @@ class DispatchTest implements EventListener { bus.dispatch(new SimpleEvent()); } - @Event(eventType = SimpleEvent.class, includeSubtypes = true, priority = 200) + @Event(eventType = SimpleEvent.class, priority = 200) + @Polymorphic void onSimpleEventFirst() { ++hits; assertTrue(hits == 1 || hits == 2); -- 2.30.2 From 9b1c708514d003fa67aaa4f548bf7d8b914d67fc Mon Sep 17 00:00:00 2001 From: kske Date: Mon, 15 Feb 2021 12:06:33 +0100 Subject: [PATCH 2/4] Replace priority with @Priority The new @Priority annotation serves the exact same purpose as @Event(priority = ...), but should be easier to read in complex handler declarations. It has to be used in conjunction with the @Event annotation, not instead of it. --- README.md | 22 +++++------ .../dev/kske/eventbus/ap/EventProcessor.java | 2 +- .../java/dev/kske/eventbus/core/Event.java | 12 +----- .../dev/kske/eventbus/core/EventHandler.java | 38 +++++++++++-------- .../java/dev/kske/eventbus/core/Priority.java | 30 +++++++++++++++ .../dev/kske/eventbus/core/CancelTest.java | 6 ++- .../dev/kske/eventbus/core/DispatchTest.java | 8 ++-- 7 files changed, 75 insertions(+), 43 deletions(-) create mode 100644 event-bus-core/src/main/java/dev/kske/eventbus/core/Priority.java diff --git a/README.md b/README.md index 62ed2c8..f6fa60c 100644 --- a/README.md +++ b/README.md @@ -73,20 +73,18 @@ private void onSimpleEvent(SimpleEvent event) { ... } ## Event Handler Execution Order -Sometimes when using multiple handlers for one event, it might be useful to know in which order they will be executed. -Event Bus provides a mechanism to ensure the correct propagation of events: the `priority`. +Sometimes when using multiple handlers for one event, it might be useful to define in which order they will be executed. +Event Bus assigns a priority to every handler, which is `100` by default, but can be explicitly set using the `@Priority` annotation in addition to `@Event`: -Priority can be set on the `@Event` annotation like that: ```java -@Event(priority=100) +@Event +@Priority(250) +private void onSimpleEvent(SimpleEvent event) { ... } ``` -The default priority for events is `100`. - **Important:** -Events are dispatched top-down, meaning the event handler with the highest priority will be executed first. - -If no priority is set or multiple handlers have the same priority, the order of execution is undefined. +Events are dispatched to handlers in descending order of their priority. +The execution order is undefined for handlers with the same priority. ## Parameter-Less Event Handlers @@ -108,12 +106,14 @@ In some cases it might be useful to stop the propagation of an event. Event Bus makes this possible with event consumption: ```java -@Event(eventType = SimpleEvent.class, priority=100) +@Event(eventType = SimpleEvent.class) +@Priority(100) private void onSimpleEvent() { EventBus.getInstance().cancel(); } -@Event(eventType = SimpleEvent.class, priority=50) +@Event(eventType = SimpleEvent.class) +@Priority(50) private void onSimpleEvent2() { System.out.println("Will not be printed!"); } diff --git a/event-bus-ap/src/main/java/dev/kske/eventbus/ap/EventProcessor.java b/event-bus-ap/src/main/java/dev/kske/eventbus/ap/EventProcessor.java index 9e661a6..f38b997 100644 --- a/event-bus-ap/src/main/java/dev/kske/eventbus/ap/EventProcessor.java +++ b/event-bus-ap/src/main/java/dev/kske/eventbus/ap/EventProcessor.java @@ -80,7 +80,7 @@ public class EventProcessor extends AbstractProcessor { if (declaredElement.getKind() == ElementKind.INTERFACE || declaredElement.getModifiers().contains(Modifier.ABSTRACT)) warning(paramElement, - "Parameter should be instantiable or handler should include subtypes"); + "Parameter should be instantiable or handler should use @Polymorphic"); } // Check listener for interface implementation diff --git a/event-bus-core/src/main/java/dev/kske/eventbus/core/Event.java b/event-bus-core/src/main/java/dev/kske/eventbus/core/Event.java index 1011d89..34be499 100644 --- a/event-bus-core/src/main/java/dev/kske/eventbus/core/Event.java +++ b/event-bus-core/src/main/java/dev/kske/eventbus/core/Event.java @@ -22,23 +22,13 @@ import java.lang.annotation.*; * @author Kai S. K. Engelbart * @since 0.0.1 * @see Polymorphic + * @see Priority */ @Documented @Retention(RUNTIME) @Target(METHOD) public @interface Event { - /** - * Defines the priority of the event handler. Handlers are executed in descending order of their - * priority. - *

- * The execution order of handlers with the same priority is undefined. - * - * @return the priority of the event handler - * @since 0.0.1 - */ - int priority() default 100; - /** * Defines the event type the handler listens to. If this value is set, the handler is not * allowed to declare parameters. diff --git a/event-bus-core/src/main/java/dev/kske/eventbus/core/EventHandler.java b/event-bus-core/src/main/java/dev/kske/eventbus/core/EventHandler.java index 436bf70..d36549f 100644 --- a/event-bus-core/src/main/java/dev/kske/eventbus/core/EventHandler.java +++ b/event-bus-core/src/main/java/dev/kske/eventbus/core/EventHandler.java @@ -13,11 +13,20 @@ import dev.kske.eventbus.core.Event.USE_PARAMETER; */ final class EventHandler implements Comparable { + /** + * The priority assigned to every event handler without an explicitly defined priority. + * + * @since 1.0.0 + * @see Priority + */ + public static final int DEFAULT_PRIORITY = 100; + private final EventListener listener; private final Method method; private final Event annotation; private final Class eventType; private final boolean polymorphic; + private final int priority; /** * Constructs an event handler. @@ -58,14 +67,17 @@ final class EventHandler implements Comparable { } this.eventType = eventType; polymorphic = method.isAnnotationPresent(Polymorphic.class); + priority = method.isAnnotationPresent(Priority.class) + ? method.getAnnotation(Priority.class).value() + : DEFAULT_PRIORITY; // Allow access if the method is non-public method.setAccessible(true); } /** - * Compares this to another event handler based on {@link Event#priority()}. In case of equal - * priority a non-zero value based on hash codes is returned. + * Compares this to another event handler based on priority. In case of equal priority a + * non-zero value based on hash codes is returned. *

* This is used to retrieve event handlers in order of descending priority from a tree set. * @@ -73,7 +85,7 @@ final class EventHandler implements Comparable { */ @Override public int compareTo(EventHandler other) { - int priority = other.annotation.priority() - annotation.priority(); + int priority = other.priority - this.priority; if (priority == 0) priority = listener.hashCode() - other.listener.hashCode(); return priority == 0 ? hashCode() - other.hashCode() : priority; @@ -81,7 +93,8 @@ final class EventHandler implements Comparable { @Override public String toString() { - return String.format("EventHandler[method=%s, annotation=%s]", method, annotation); + return String.format("EventHandler[method=%s, eventType=%s, polymorphic=%b, priority=%d]", + method, annotation.eventType(), polymorphic, priority); } /** @@ -109,16 +122,17 @@ final class EventHandler implements Comparable { EventListener getListener() { return listener; } /** - * @return the event annotation - * @since 0.0.1 + * @return the event type this handler listens to + * @since 0.0.3 */ - Event getAnnotation() { return annotation; } + Class getEventType() { return eventType; } /** - * @return the priority of the event annotation + * @return the priority of this handler * @since 0.0.1 + * @see Priority */ - int getPriority() { return annotation.priority(); } + int getPriority() { return priority; } /** * @return whether this handler is polymorphic @@ -126,10 +140,4 @@ final class EventHandler implements Comparable { * @see Polymorphic */ boolean isPolymorphic() { return polymorphic; } - - /** - * @return the event type this handler listens to - * @since 0.0.3 - */ - Class getEventType() { return eventType; } } diff --git a/event-bus-core/src/main/java/dev/kske/eventbus/core/Priority.java b/event-bus-core/src/main/java/dev/kske/eventbus/core/Priority.java new file mode 100644 index 0000000..1218df3 --- /dev/null +++ b/event-bus-core/src/main/java/dev/kske/eventbus/core/Priority.java @@ -0,0 +1,30 @@ +package dev.kske.eventbus.core; + +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.*; + +/** + * Defines the priority of an event handler. Handlers are executed in descending order of their + * priority. + *

+ * Handlers without this annotation have the default priority of 100. + *

+ * The execution order of handlers with the same priority is undefined. + * + * @author Kai S. K. Engelbart + * @since 1.0.0 + * @see Event + */ +@Documented +@Retention(RUNTIME) +@Target(METHOD) +public @interface Priority { + + /** + * @return the priority of the event handler + * @since 1.0.0 + */ + int value(); +} diff --git a/event-bus-core/src/test/java/dev/kske/eventbus/core/CancelTest.java b/event-bus-core/src/test/java/dev/kske/eventbus/core/CancelTest.java index 0d25f93..a2da0bf 100644 --- a/event-bus-core/src/test/java/dev/kske/eventbus/core/CancelTest.java +++ b/event-bus-core/src/test/java/dev/kske/eventbus/core/CancelTest.java @@ -39,13 +39,15 @@ class CancelTest implements EventListener { assertEquals(1, hits); } - @Event(eventType = SimpleEvent.class, priority = 100) + @Event(eventType = SimpleEvent.class) + @Priority(100) void onSimpleFirst() { ++hits; bus.cancel(); } - @Event(eventType = SimpleEvent.class, priority = 50) + @Event(eventType = SimpleEvent.class) + @Priority(50) void onSimpleSecond() { ++hits; } diff --git a/event-bus-core/src/test/java/dev/kske/eventbus/core/DispatchTest.java b/event-bus-core/src/test/java/dev/kske/eventbus/core/DispatchTest.java index 0239a8c..c3a5a04 100644 --- a/event-bus-core/src/test/java/dev/kske/eventbus/core/DispatchTest.java +++ b/event-bus-core/src/test/java/dev/kske/eventbus/core/DispatchTest.java @@ -38,20 +38,22 @@ class DispatchTest implements EventListener { bus.dispatch(new SimpleEvent()); } - @Event(eventType = SimpleEvent.class, priority = 200) + @Event(eventType = SimpleEvent.class) + @Priority(200) @Polymorphic void onSimpleEventFirst() { ++hits; assertTrue(hits == 1 || hits == 2); } - @Event(eventType = SimpleEvent.class, priority = 150) + @Event(eventType = SimpleEvent.class) + @Priority(150) static void onSimpleEventSecond() { ++hits; assertEquals(3, hits); } - @Event(priority = 100) + @Event void onSimpleEventThird(SimpleEvent event) { ++hits; assertEquals(4, hits); -- 2.30.2 From cd2e7ad0231b457ccd53a932de98a878923da537 Mon Sep 17 00:00:00 2001 From: kske Date: Mon, 15 Feb 2021 13:42:20 +0100 Subject: [PATCH 3/4] Rename Event#eventType to Event#value for more concise usage --- README.md | 14 ++++++--- .../dev/kske/eventbus/ap/EventProcessor.java | 2 +- .../java/dev/kske/eventbus/core/Event.java | 6 ++-- .../dev/kske/eventbus/core/EventHandler.java | 29 ++++++++++--------- .../dev/kske/eventbus/core/CancelTest.java | 4 +-- .../dev/kske/eventbus/core/DispatchTest.java | 4 +-- 6 files changed, 34 insertions(+), 25 deletions(-) diff --git a/README.md b/README.md index f6fa60c..c6e2888 100644 --- a/README.md +++ b/README.md @@ -92,13 +92,13 @@ In some cases an event handler is not interested in the dispatched event instanc To avoid declaring a useless parameter just to specify the event type of the handler, there is an alternative: ```java -@Event(eventType = SimpleEvent.class) +@Event(SimpleEvent.class) private void onSimpleEvent() { System.out.println("SimpleEvent received!"); } ``` -Make sure that you **do not** declare both a parameter and the `eventType` value of the annotation, as this would be ambiguous. +Make sure that you **do not** both declare a parameter and specify the event type in the annotation, as this would be ambiguous. ## Event Consumption @@ -106,13 +106,13 @@ In some cases it might be useful to stop the propagation of an event. Event Bus makes this possible with event consumption: ```java -@Event(eventType = SimpleEvent.class) +@Event(SimpleEvent.class) @Priority(100) private void onSimpleEvent() { EventBus.getInstance().cancel(); } -@Event(eventType = SimpleEvent.class) +@Event(SimpleEvent.class) @Priority(50) private void onSimpleEvent2() { System.out.println("Will not be printed!"); @@ -154,6 +154,12 @@ Then, require the Event Bus Core module in your `module-info.java`: requires dev.kske.eventbus.core; ``` +If you intend to use event handlers that are inaccessible to Event Bus by means of Java language access control, make sure to allow reflective access from your module: + +```java +opens my.module to dev.kske.eventbus.core; +``` + ## Compile-Time Error Checking with Event Bus AP To assist you with writing event listeners, the Event Bus AP (Annotation Processor) module enforces correct usage of the `@Event` annotation during compile time. diff --git a/event-bus-ap/src/main/java/dev/kske/eventbus/ap/EventProcessor.java b/event-bus-ap/src/main/java/dev/kske/eventbus/ap/EventProcessor.java index f38b997..c05f15f 100644 --- a/event-bus-ap/src/main/java/dev/kske/eventbus/ap/EventProcessor.java +++ b/event-bus-ap/src/main/java/dev/kske/eventbus/ap/EventProcessor.java @@ -40,7 +40,7 @@ public class EventProcessor extends AbstractProcessor { // Determine how the event type is defined boolean useParameter; try { - eventAnnotation.eventType(); + eventAnnotation.value(); throw new EventBusException( "Could not determine event type of handler " + eventHandler); } catch (MirroredTypeException e) { diff --git a/event-bus-core/src/main/java/dev/kske/eventbus/core/Event.java b/event-bus-core/src/main/java/dev/kske/eventbus/core/Event.java index 34be499..7a6b837 100644 --- a/event-bus-core/src/main/java/dev/kske/eventbus/core/Event.java +++ b/event-bus-core/src/main/java/dev/kske/eventbus/core/Event.java @@ -13,7 +13,7 @@ import java.lang.annotation.*; *

  • Specifying an event type by either *
      *
    • Declaring one parameter of a type that implements {@link IEvent}
    • - *
    • Defining the class of the event using the {@link Event#eventType()} value
    • + *
    • Defining the class of the event using the annotation value
    • *
    *
  • *
  • Return type of {@code void}
  • @@ -36,9 +36,9 @@ public @interface Event { * This is useful when the event handler does not utilize the event instance. * * @return the event type accepted by the handler - * @since 0.0.3 + * @since 1.0.0 */ - Class eventType() default USE_PARAMETER.class; + Class value() default USE_PARAMETER.class; /** * Signifies that the event type the handler listens to is determined by the type of its only diff --git a/event-bus-core/src/main/java/dev/kske/eventbus/core/EventHandler.java b/event-bus-core/src/main/java/dev/kske/eventbus/core/EventHandler.java index d36549f..16b4561 100644 --- a/event-bus-core/src/main/java/dev/kske/eventbus/core/EventHandler.java +++ b/event-bus-core/src/main/java/dev/kske/eventbus/core/EventHandler.java @@ -23,8 +23,8 @@ final class EventHandler implements Comparable { private final EventListener listener; private final Method method; - private final Event annotation; private final Class eventType; + private final boolean useParameter; private final boolean polymorphic; private final int priority; @@ -42,13 +42,13 @@ final class EventHandler implements Comparable { EventHandler(EventListener listener, Method method, Event annotation) throws EventBusException { this.listener = listener; this.method = method; - this.annotation = annotation; + useParameter = annotation.value() == USE_PARAMETER.class; // Check for correct method signature and return type - if (method.getParameterCount() == 0 && annotation.eventType().equals(USE_PARAMETER.class)) + if (method.getParameterCount() == 0 && useParameter) throw new EventBusException(method + " does not define an event type!"); - if (method.getParameterCount() == 1 && !annotation.eventType().equals(USE_PARAMETER.class)) + if (method.getParameterCount() == 1 && !useParameter) throw new EventBusException(method + " defines an ambiguous event type!"); if (method.getParameterCount() > 1) @@ -58,16 +58,18 @@ final class EventHandler implements Comparable { throw new EventBusException(method + " does not have a return type of void!"); // Determine the event type - Class eventType = annotation.eventType(); - if (eventType.equals(USE_PARAMETER.class)) { + if (useParameter) { var param = method.getParameterTypes()[0]; if (!IEvent.class.isAssignableFrom(param)) throw new EventBusException(param + " is not of type IEvent!"); eventType = (Class) param; + } else { + eventType = annotation.value(); } - this.eventType = eventType; - polymorphic = method.isAnnotationPresent(Polymorphic.class); - priority = method.isAnnotationPresent(Priority.class) + + // Determine additional handler properties + polymorphic = method.isAnnotationPresent(Polymorphic.class); + priority = method.isAnnotationPresent(Priority.class) ? method.getAnnotation(Priority.class).value() : DEFAULT_PRIORITY; @@ -93,8 +95,9 @@ final class EventHandler implements Comparable { @Override public String toString() { - return String.format("EventHandler[method=%s, eventType=%s, polymorphic=%b, priority=%d]", - method, annotation.eventType(), polymorphic, priority); + return String.format( + "EventHandler[method=%s, eventType=%s, useParameter=%b, polymorphic=%b, priority=%d]", + method, eventType, useParameter, polymorphic, priority); } /** @@ -106,7 +109,7 @@ final class EventHandler implements Comparable { */ void execute(IEvent event) throws EventBusException { try { - if (annotation.eventType() == USE_PARAMETER.class) + if (useParameter) method.invoke(listener, event); else method.invoke(listener); @@ -122,7 +125,7 @@ final class EventHandler implements Comparable { EventListener getListener() { return listener; } /** - * @return the event type this handler listens to + * @return the event type this handler listens for * @since 0.0.3 */ Class getEventType() { return eventType; } diff --git a/event-bus-core/src/test/java/dev/kske/eventbus/core/CancelTest.java b/event-bus-core/src/test/java/dev/kske/eventbus/core/CancelTest.java index a2da0bf..cde0128 100644 --- a/event-bus-core/src/test/java/dev/kske/eventbus/core/CancelTest.java +++ b/event-bus-core/src/test/java/dev/kske/eventbus/core/CancelTest.java @@ -39,14 +39,14 @@ class CancelTest implements EventListener { assertEquals(1, hits); } - @Event(eventType = SimpleEvent.class) + @Event(SimpleEvent.class) @Priority(100) void onSimpleFirst() { ++hits; bus.cancel(); } - @Event(eventType = SimpleEvent.class) + @Event(SimpleEvent.class) @Priority(50) void onSimpleSecond() { ++hits; diff --git a/event-bus-core/src/test/java/dev/kske/eventbus/core/DispatchTest.java b/event-bus-core/src/test/java/dev/kske/eventbus/core/DispatchTest.java index c3a5a04..3fb1a3b 100644 --- a/event-bus-core/src/test/java/dev/kske/eventbus/core/DispatchTest.java +++ b/event-bus-core/src/test/java/dev/kske/eventbus/core/DispatchTest.java @@ -38,7 +38,7 @@ class DispatchTest implements EventListener { bus.dispatch(new SimpleEvent()); } - @Event(eventType = SimpleEvent.class) + @Event(SimpleEvent.class) @Priority(200) @Polymorphic void onSimpleEventFirst() { @@ -46,7 +46,7 @@ class DispatchTest implements EventListener { assertTrue(hits == 1 || hits == 2); } - @Event(eventType = SimpleEvent.class) + @Event(SimpleEvent.class) @Priority(150) static void onSimpleEventSecond() { ++hits; -- 2.30.2 From 002180ed3b37162c527358bd7bfc86b8d274ad5c Mon Sep 17 00:00:00 2001 From: kske Date: Mon, 15 Feb 2021 14:43:34 +0100 Subject: [PATCH 4/4] Remove EventListener and IEvent marker interfaces This allows Event Bus to interface with existing classes without modification. --- README.md | 13 +++---- .../dev/kske/eventbus/ap/EventProcessor.java | 13 +------ .../java/dev/kske/eventbus/core/Event.java | 7 ++-- .../java/dev/kske/eventbus/core/EventBus.java | 16 ++++----- .../dev/kske/eventbus/core/EventHandler.java | 34 +++++++------------ .../dev/kske/eventbus/core/EventListener.java | 12 ------- .../java/dev/kske/eventbus/core/IEvent.java | 12 ------- .../dev/kske/eventbus/core/CancelTest.java | 2 +- .../dev/kske/eventbus/core/DispatchTest.java | 4 +-- .../dev/kske/eventbus/core/SimpleEvent.java | 2 +- 10 files changed, 33 insertions(+), 82 deletions(-) delete mode 100644 event-bus-core/src/main/java/dev/kske/eventbus/core/EventListener.java delete mode 100644 event-bus-core/src/main/java/dev/kske/eventbus/core/IEvent.java diff --git a/README.md b/README.md index c6e2888..8666ba8 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ ## Introduction This library allows passing events between different objects without them having a direct reference to each other. -Any class can be made an event by implementing the `IEvent` interface. +Any object can serve as an event. Using an instance of the `EventBus` class, an instant of the event class can be dispatched. This means that it will be forwarded to all listeners registered for it at the event bus. @@ -13,16 +13,13 @@ In addition, a singleton instance of the event bus is provided by the `EventBus# To listen to events, register event handling methods using the `Event` annotation. For this to work, the method must have a return type of `void` and declare a single parameter of the desired event type. Alternatively, a parameter-less event handler can be declared as shown [below](#parameter-less-event-handlers). -Additionally, the class containing the method must implement the `EventListener` interface. ## A Simple Example -Lets look at a simple example: we declare the empty class `SimpleEvent` that implements `IEvent` and can thus be used as an event. +Lets look at a simple example: we declare the empty class `SimpleEvent` whose objects can be used as events. ```java -import dev.kske.eventbus.core.IEvent; - -public class SimpleEvent implements IEvent {} +public class SimpleEvent {} ``` Next, an event listener for the `SimpleEvent` is declared: @@ -30,7 +27,7 @@ Next, an event listener for the `SimpleEvent` is declared: ```java import dev.kske.eventbus.core.*; -public class SimpleEventListener implements EventListener { +public class SimpleEventListener { public SimpleEventListener() { @@ -165,7 +162,7 @@ opens my.module to dev.kske.eventbus.core; To assist you with writing event listeners, the Event Bus AP (Annotation Processor) module enforces correct usage of the `@Event` annotation during compile time. This reduces difficult-to-debug bugs that occur during runtime to compile-time errors which can be easily fixed. -The event annotation processor detects invalid event handlers, missing `EventListener` implementations, event type issues with more to come in future versions. +The event annotation processor detects invalid event handlers and event type issues with more to come in future versions. When using Maven, it can be registered using the Maven Compiler Plugin: diff --git a/event-bus-ap/src/main/java/dev/kske/eventbus/ap/EventProcessor.java b/event-bus-ap/src/main/java/dev/kske/eventbus/ap/EventProcessor.java index c05f15f..f6cc75d 100644 --- a/event-bus-ap/src/main/java/dev/kske/eventbus/ap/EventProcessor.java +++ b/event-bus-ap/src/main/java/dev/kske/eventbus/ap/EventProcessor.java @@ -34,8 +34,7 @@ public class EventProcessor extends AbstractProcessor { private void processRound(Set eventHandlers) { for (ExecutableElement eventHandler : eventHandlers) { - TypeElement eventListener = (TypeElement) eventHandler.getEnclosingElement(); - Event eventAnnotation = eventHandler.getAnnotation(Event.class); + Event eventAnnotation = eventHandler.getAnnotation(Event.class); // Determine how the event type is defined boolean useParameter; @@ -68,11 +67,6 @@ public class EventProcessor extends AbstractProcessor { var paramElement = eventHandler.getParameters().get(0); var paramType = paramElement.asType(); - // Check for valid event type - if (useParameter && !processingEnv.getTypeUtils().isAssignable(paramType, - getTypeMirror(IEvent.class))) - error(paramElement, "Parameter must implement IEvent"); - // Check for handlers for abstract types that aren't polymorphic if (eventHandler.getAnnotation(Polymorphic.class) == null && paramType.getKind() == TypeKind.DECLARED) { @@ -82,11 +76,6 @@ public class EventProcessor extends AbstractProcessor { warning(paramElement, "Parameter should be instantiable or handler should use @Polymorphic"); } - - // Check listener for interface implementation - if (!eventListener.getInterfaces().contains(getTypeMirror(EventListener.class))) - warning(eventHandler.getEnclosingElement(), - "Class should implement EventListener interface"); } } diff --git a/event-bus-core/src/main/java/dev/kske/eventbus/core/Event.java b/event-bus-core/src/main/java/dev/kske/eventbus/core/Event.java index 7a6b837..743c803 100644 --- a/event-bus-core/src/main/java/dev/kske/eventbus/core/Event.java +++ b/event-bus-core/src/main/java/dev/kske/eventbus/core/Event.java @@ -9,10 +9,9 @@ import java.lang.annotation.*; * Indicates that a method is an event handler. To be successfully used as such, the method has to * comply with the following specifications: *
      - *
    • Declared inside a class that implements {@link EventListener}
    • *
    • Specifying an event type by either *
        - *
      • Declaring one parameter of a type that implements {@link IEvent}
      • + *
      • Declaring one object parameter
      • *
      • Defining the class of the event using the annotation value
      • *
      *
    • @@ -38,7 +37,7 @@ public @interface Event { * @return the event type accepted by the handler * @since 1.0.0 */ - Class value() default USE_PARAMETER.class; + Class value() default USE_PARAMETER.class; /** * Signifies that the event type the handler listens to is determined by the type of its only @@ -46,5 +45,5 @@ public @interface Event { * * @since 0.0.3 */ - static final class USE_PARAMETER implements IEvent {} + static final class USE_PARAMETER {} } diff --git a/event-bus-core/src/main/java/dev/kske/eventbus/core/EventBus.java b/event-bus-core/src/main/java/dev/kske/eventbus/core/EventBus.java index 40ed4fa..8f30e89 100644 --- a/event-bus-core/src/main/java/dev/kske/eventbus/core/EventBus.java +++ b/event-bus-core/src/main/java/dev/kske/eventbus/core/EventBus.java @@ -51,11 +51,11 @@ public final class EventBus { return instance; } - private final Map, TreeSet> bindings = + private final Map, TreeSet> bindings = new ConcurrentHashMap<>(); - private final Set registeredListeners = + private final Set registeredListeners = ConcurrentHashMap.newKeySet(); - private final ThreadLocal dispatchState = + private final ThreadLocal dispatchState = ThreadLocal.withInitial(DispatchState::new); /** @@ -65,7 +65,7 @@ public final class EventBus { * @param event the event to dispatch * @since 0.0.1 */ - public void dispatch(IEvent event) { + public void dispatch(Object event) { Objects.requireNonNull(event); logger.log(Level.INFO, "Dispatching event {0}", event); @@ -95,7 +95,7 @@ public final class EventBus { * @return all event handlers registered for the event class * @since 0.0.1 */ - private List getHandlersFor(Class eventClass) { + private List getHandlersFor(Class eventClass) { // Get handlers defined for the event class Set handlers = bindings.getOrDefault(eventClass, new TreeSet<>()); @@ -133,7 +133,7 @@ public final class EventBus { * @since 0.0.1 * @see Event */ - public void registerListener(EventListener listener) throws EventBusException { + public void registerListener(Object listener) throws EventBusException { Objects.requireNonNull(listener); if (registeredListeners.contains(listener)) throw new EventBusException(listener + " already registered!"); @@ -170,7 +170,7 @@ public final class EventBus { * @param listener the listener to remove * @since 0.0.1 */ - public void removeListener(EventListener listener) { + public void removeListener(Object listener) { Objects.requireNonNull(listener); logger.log(Level.INFO, "Removing event listener {0}", listener.getClass().getName()); @@ -204,7 +204,7 @@ public final class EventBus { * @return all registered event listeners * @since 0.0.1 */ - public Set getRegisteredListeners() { + public Set getRegisteredListeners() { return Collections.unmodifiableSet(registeredListeners); } } diff --git a/event-bus-core/src/main/java/dev/kske/eventbus/core/EventHandler.java b/event-bus-core/src/main/java/dev/kske/eventbus/core/EventHandler.java index 16b4561..f1bbea3 100644 --- a/event-bus-core/src/main/java/dev/kske/eventbus/core/EventHandler.java +++ b/event-bus-core/src/main/java/dev/kske/eventbus/core/EventHandler.java @@ -21,12 +21,12 @@ final class EventHandler implements Comparable { */ public static final int DEFAULT_PRIORITY = 100; - private final EventListener listener; - private final Method method; - private final Class eventType; - private final boolean useParameter; - private final boolean polymorphic; - private final int priority; + private final Object listener; + private final Method method; + private final Class eventType; + private final boolean useParameter; + private final boolean polymorphic; + private final int priority; /** * Constructs an event handler. @@ -38,8 +38,7 @@ final class EventHandler implements Comparable { * specification * @since 0.0.1 */ - @SuppressWarnings("unchecked") - EventHandler(EventListener listener, Method method, Event annotation) throws EventBusException { + EventHandler(Object listener, Method method, Event annotation) throws EventBusException { this.listener = listener; this.method = method; useParameter = annotation.value() == USE_PARAMETER.class; @@ -57,17 +56,8 @@ final class EventHandler implements Comparable { if (!method.getReturnType().equals(void.class)) throw new EventBusException(method + " does not have a return type of void!"); - // Determine the event type - if (useParameter) { - var param = method.getParameterTypes()[0]; - if (!IEvent.class.isAssignableFrom(param)) - throw new EventBusException(param + " is not of type IEvent!"); - eventType = (Class) param; - } else { - eventType = annotation.value(); - } - - // Determine additional handler properties + // Determine handler properties + eventType = useParameter ? method.getParameterTypes()[0] : annotation.value(); polymorphic = method.isAnnotationPresent(Polymorphic.class); priority = method.isAnnotationPresent(Priority.class) ? method.getAnnotation(Priority.class).value() @@ -107,7 +97,7 @@ final class EventHandler implements Comparable { * @throws EventBusException if the handler throws an exception * @since 0.0.1 */ - void execute(IEvent event) throws EventBusException { + void execute(Object event) throws EventBusException { try { if (useParameter) method.invoke(listener, event); @@ -122,13 +112,13 @@ final class EventHandler implements Comparable { * @return the listener containing this handler * @since 0.0.1 */ - EventListener getListener() { return listener; } + Object getListener() { return listener; } /** * @return the event type this handler listens for * @since 0.0.3 */ - Class getEventType() { return eventType; } + Class getEventType() { return eventType; } /** * @return the priority of this handler diff --git a/event-bus-core/src/main/java/dev/kske/eventbus/core/EventListener.java b/event-bus-core/src/main/java/dev/kske/eventbus/core/EventListener.java deleted file mode 100644 index 4e54505..0000000 --- a/event-bus-core/src/main/java/dev/kske/eventbus/core/EventListener.java +++ /dev/null @@ -1,12 +0,0 @@ -package dev.kske.eventbus.core; - -/** - * Marker interface for event listeners. Event listeners can contain event handling methods to which - * events can be dispatched. - * - * @author Kai S. K. Engelbart - * @since 0.0.1 - * @see Event - * @see EventBus - */ -public interface EventListener {} diff --git a/event-bus-core/src/main/java/dev/kske/eventbus/core/IEvent.java b/event-bus-core/src/main/java/dev/kske/eventbus/core/IEvent.java deleted file mode 100644 index bb09b18..0000000 --- a/event-bus-core/src/main/java/dev/kske/eventbus/core/IEvent.java +++ /dev/null @@ -1,12 +0,0 @@ -package dev.kske.eventbus.core; - -/** - * Marker interface for event objects. Event objects can be used as event handler parameters and - * thus can be dispatched to the event bus. - * - * @author Kai S. K. Engelbart - * @since 0.0.1 - * @see Event - * @see EventBus - */ -public interface IEvent {} diff --git a/event-bus-core/src/test/java/dev/kske/eventbus/core/CancelTest.java b/event-bus-core/src/test/java/dev/kske/eventbus/core/CancelTest.java index cde0128..5f715c7 100644 --- a/event-bus-core/src/test/java/dev/kske/eventbus/core/CancelTest.java +++ b/event-bus-core/src/test/java/dev/kske/eventbus/core/CancelTest.java @@ -11,7 +11,7 @@ import org.junit.jupiter.api.*; * @author Leon Hofmeister * @since 0.1.0 */ -class CancelTest implements EventListener { +class CancelTest { EventBus bus; int hits; diff --git a/event-bus-core/src/test/java/dev/kske/eventbus/core/DispatchTest.java b/event-bus-core/src/test/java/dev/kske/eventbus/core/DispatchTest.java index 3fb1a3b..95bae3f 100644 --- a/event-bus-core/src/test/java/dev/kske/eventbus/core/DispatchTest.java +++ b/event-bus-core/src/test/java/dev/kske/eventbus/core/DispatchTest.java @@ -10,7 +10,7 @@ import org.junit.jupiter.api.*; * @author Kai S. K. Engelbart * @since 0.0.1 */ -class DispatchTest implements EventListener { +class DispatchTest { EventBus bus; static int hits; @@ -27,7 +27,7 @@ class DispatchTest implements EventListener { } /** - * Tests {@link EventBus#dispatch(IEvent)} with multiple handler priorities, a subtype handler + * Tests {@link EventBus#dispatch(Object)} with multiple handler priorities, a subtype handler * and a static handler. * * @since 0.0.1 diff --git a/event-bus-core/src/test/java/dev/kske/eventbus/core/SimpleEvent.java b/event-bus-core/src/test/java/dev/kske/eventbus/core/SimpleEvent.java index ede1afa..bed60bc 100644 --- a/event-bus-core/src/test/java/dev/kske/eventbus/core/SimpleEvent.java +++ b/event-bus-core/src/test/java/dev/kske/eventbus/core/SimpleEvent.java @@ -6,4 +6,4 @@ package dev.kske.eventbus.core; * @author Kai S. K. Engelbart * @since 0.0.1 */ -public class SimpleEvent implements IEvent {} +public class SimpleEvent {} -- 2.30.2