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.
This commit is contained in:
parent
e040f6ab1b
commit
3a6ebe9a19
18
README.md
18
README.md
@ -55,21 +55,23 @@ Note that creating static event handlers like this
|
|||||||
|
|
||||||
```java
|
```java
|
||||||
@Event
|
@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.
|
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.
|
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
|
```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.
|
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`.
|
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.
|
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.
|
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:
|
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.
|
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.
|
In some cases it might be useful to stop the propagation of an event.
|
||||||
Event Bus makes this possible with event consumption:
|
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;
|
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.
|
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.
|
This reduces difficult-to-debug bugs that occur during runtime to compile-time errors which can be easily fixed.
|
||||||
|
@ -73,8 +73,9 @@ public class EventProcessor extends AbstractProcessor {
|
|||||||
getTypeMirror(IEvent.class)))
|
getTypeMirror(IEvent.class)))
|
||||||
error(paramElement, "Parameter must implement IEvent");
|
error(paramElement, "Parameter must implement IEvent");
|
||||||
|
|
||||||
// Check for handlers for abstract types that don't include subtypes
|
// Check for handlers for abstract types that aren't polymorphic
|
||||||
if (!eventAnnotation.includeSubtypes() && paramType.getKind() == TypeKind.DECLARED) {
|
if (eventHandler.getAnnotation(Polymorphic.class) == null
|
||||||
|
&& paramType.getKind() == TypeKind.DECLARED) {
|
||||||
var declaredElement = ((DeclaredType) paramType).asElement();
|
var declaredElement = ((DeclaredType) paramType).asElement();
|
||||||
if (declaredElement.getKind() == ElementKind.INTERFACE
|
if (declaredElement.getKind() == ElementKind.INTERFACE
|
||||||
|| declaredElement.getModifiers().contains(Modifier.ABSTRACT))
|
|| declaredElement.getModifiers().contains(Modifier.ABSTRACT))
|
||||||
|
@ -21,6 +21,7 @@ import java.lang.annotation.*;
|
|||||||
*
|
*
|
||||||
* @author Kai S. K. Engelbart
|
* @author Kai S. K. Engelbart
|
||||||
* @since 0.0.1
|
* @since 0.0.1
|
||||||
|
* @see Polymorphic
|
||||||
*/
|
*/
|
||||||
@Documented
|
@Documented
|
||||||
@Retention(RUNTIME)
|
@Retention(RUNTIME)
|
||||||
@ -38,14 +39,6 @@ public @interface Event {
|
|||||||
*/
|
*/
|
||||||
int priority() default 100;
|
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
|
* Defines the event type the handler listens to. If this value is set, the handler is not
|
||||||
* allowed to declare parameters.
|
* allowed to declare parameters.
|
||||||
|
@ -104,7 +104,7 @@ public final class EventBus {
|
|||||||
for (var binding : bindings.entrySet())
|
for (var binding : bindings.entrySet())
|
||||||
if (binding.getKey().isAssignableFrom(eventClass))
|
if (binding.getKey().isAssignableFrom(eventClass))
|
||||||
for (var handler : binding.getValue())
|
for (var handler : binding.getValue())
|
||||||
if (handler.includeSubtypes())
|
if (handler.isPolymorphic())
|
||||||
handlers.add(handler);
|
handlers.add(handler);
|
||||||
|
|
||||||
return new ArrayList<>(handlers);
|
return new ArrayList<>(handlers);
|
||||||
|
@ -17,6 +17,7 @@ final class EventHandler implements Comparable<EventHandler> {
|
|||||||
private final Method method;
|
private final Method method;
|
||||||
private final Event annotation;
|
private final Event annotation;
|
||||||
private final Class<? extends IEvent> eventType;
|
private final Class<? extends IEvent> eventType;
|
||||||
|
private final boolean polymorphic;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs an event handler.
|
* Constructs an event handler.
|
||||||
@ -56,6 +57,7 @@ final class EventHandler implements Comparable<EventHandler> {
|
|||||||
eventType = (Class<? extends IEvent>) param;
|
eventType = (Class<? extends IEvent>) param;
|
||||||
}
|
}
|
||||||
this.eventType = eventType;
|
this.eventType = eventType;
|
||||||
|
polymorphic = method.isAnnotationPresent(Polymorphic.class);
|
||||||
|
|
||||||
// Allow access if the method is non-public
|
// Allow access if the method is non-public
|
||||||
method.setAccessible(true);
|
method.setAccessible(true);
|
||||||
@ -65,7 +67,7 @@ final class EventHandler implements Comparable<EventHandler> {
|
|||||||
* Compares this to another event handler based on {@link Event#priority()}. In case of equal
|
* 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.
|
* priority a non-zero value based on hash codes is returned.
|
||||||
* <p>
|
* <p>
|
||||||
* 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
|
* @since 0.0.1
|
||||||
*/
|
*/
|
||||||
@ -91,15 +93,11 @@ final class EventHandler implements Comparable<EventHandler> {
|
|||||||
*/
|
*/
|
||||||
void execute(IEvent event) throws EventBusException {
|
void execute(IEvent event) throws EventBusException {
|
||||||
try {
|
try {
|
||||||
if (annotation.eventType().equals(USE_PARAMETER.class))
|
if (annotation.eventType() == USE_PARAMETER.class)
|
||||||
method.invoke(listener, event);
|
method.invoke(listener, event);
|
||||||
else
|
else
|
||||||
method.invoke(listener);
|
method.invoke(listener);
|
||||||
} catch (
|
} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
|
||||||
IllegalAccessException
|
|
||||||
| IllegalArgumentException
|
|
||||||
| InvocationTargetException e
|
|
||||||
) {
|
|
||||||
throw new EventBusException("Failed to invoke event handler!", e);
|
throw new EventBusException("Failed to invoke event handler!", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -123,10 +121,11 @@ final class EventHandler implements Comparable<EventHandler> {
|
|||||||
int getPriority() { return annotation.priority(); }
|
int getPriority() { return annotation.priority(); }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return whether this handler includes subtypes
|
* @return whether this handler is polymorphic
|
||||||
* @since 0.0.4
|
* @since 1.0.0
|
||||||
|
* @see Polymorphic
|
||||||
*/
|
*/
|
||||||
boolean includeSubtypes() { return annotation.includeSubtypes(); }
|
boolean isPolymorphic() { return polymorphic; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the event type this handler listens to
|
* @return the event type this handler listens to
|
||||||
|
@ -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.
|
||||||
|
* <p>
|
||||||
|
* 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 {}
|
@ -38,7 +38,8 @@ class DispatchTest implements EventListener {
|
|||||||
bus.dispatch(new SimpleEvent());
|
bus.dispatch(new SimpleEvent());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Event(eventType = SimpleEvent.class, includeSubtypes = true, priority = 200)
|
@Event(eventType = SimpleEvent.class, priority = 200)
|
||||||
|
@Polymorphic
|
||||||
void onSimpleEventFirst() {
|
void onSimpleEventFirst() {
|
||||||
++hits;
|
++hits;
|
||||||
assertTrue(hits == 1 || hits == 2);
|
assertTrue(hits == 1 || hits == 2);
|
||||||
|
Loading…
Reference in New Issue
Block a user