Merge pull request 'Split @Event Parameters Into @Polymorphic and @Property, Remove Marker Interfaces' (#5) from f/new-annotations into develop
Reviewed-on: https://git.kske.dev/kske/event-bus/pulls/5 Reviewed-by: delvh <leon@kske.dev>
This commit is contained in:
commit
39c51c8953
63
README.md
63
README.md
@ -3,7 +3,7 @@
|
|||||||
## Introduction
|
## Introduction
|
||||||
|
|
||||||
This library allows passing events between different objects without them having a direct reference to each other.
|
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.
|
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.
|
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.
|
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.
|
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).
|
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
|
## 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
|
```java
|
||||||
import dev.kske.eventbus.core.IEvent;
|
public class SimpleEvent {}
|
||||||
|
|
||||||
public class SimpleEvent implements IEvent {}
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Next, an event listener for the `SimpleEvent` is declared:
|
Next, an event listener for the `SimpleEvent` is declared:
|
||||||
@ -30,7 +27,7 @@ Next, an event listener for the `SimpleEvent` is declared:
|
|||||||
```java
|
```java
|
||||||
import dev.kske.eventbus.core.*;
|
import dev.kske.eventbus.core.*;
|
||||||
|
|
||||||
public class SimpleEventListener implements EventListener {
|
public class SimpleEventListener {
|
||||||
|
|
||||||
public SimpleEventListener() {
|
public SimpleEventListener() {
|
||||||
|
|
||||||
@ -55,63 +52,65 @@ 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 define in which order they will be executed.
|
||||||
Event Bus provides a mechanism to ensure the correct propagation of events: the `priority`.
|
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
|
```java
|
||||||
@Event(priority=100)
|
@Event
|
||||||
|
@Priority(250)
|
||||||
|
private void onSimpleEvent(SimpleEvent event) { ... }
|
||||||
```
|
```
|
||||||
|
|
||||||
The default priority for events is `100`.
|
|
||||||
|
|
||||||
**Important:**
|
**Important:**
|
||||||
Events are dispatched top-down, meaning the event handler with the highest priority will be executed first.
|
Events are dispatched to handlers in descending order of their priority.
|
||||||
|
The execution order is undefined for handlers with the same priority.
|
||||||
|
|
||||||
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:
|
||||||
|
|
||||||
```java
|
```java
|
||||||
@Event(eventType = SimpleEvent.class)
|
@Event(SimpleEvent.class)
|
||||||
private void onSimpleEvent() {
|
private void onSimpleEvent() {
|
||||||
System.out.println("SimpleEvent received!");
|
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
|
## 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:
|
||||||
|
|
||||||
```java
|
```java
|
||||||
@Event(eventType = SimpleEvent.class, priority=100)
|
@Event(SimpleEvent.class)
|
||||||
|
@Priority(100)
|
||||||
private void onSimpleEvent() {
|
private void onSimpleEvent() {
|
||||||
EventBus.getInstance().cancel();
|
EventBus.getInstance().cancel();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Event(eventType = SimpleEvent.class, priority=50)
|
@Event(SimpleEvent.class)
|
||||||
|
@Priority(50)
|
||||||
private void onSimpleEvent2() {
|
private void onSimpleEvent2() {
|
||||||
System.out.println("Will not be printed!");
|
System.out.println("Will not be printed!");
|
||||||
}
|
}
|
||||||
@ -152,12 +151,18 @@ 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
|
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.
|
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.
|
||||||
|
|
||||||
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:
|
When using Maven, it can be registered using the Maven Compiler Plugin:
|
||||||
|
|
||||||
|
@ -34,13 +34,12 @@ public class EventProcessor extends AbstractProcessor {
|
|||||||
|
|
||||||
private void processRound(Set<ExecutableElement> eventHandlers) {
|
private void processRound(Set<ExecutableElement> eventHandlers) {
|
||||||
for (ExecutableElement eventHandler : 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
|
// Determine how the event type is defined
|
||||||
boolean useParameter;
|
boolean useParameter;
|
||||||
try {
|
try {
|
||||||
eventAnnotation.eventType();
|
eventAnnotation.value();
|
||||||
throw new EventBusException(
|
throw new EventBusException(
|
||||||
"Could not determine event type of handler " + eventHandler);
|
"Could not determine event type of handler " + eventHandler);
|
||||||
} catch (MirroredTypeException e) {
|
} catch (MirroredTypeException e) {
|
||||||
@ -68,24 +67,15 @@ public class EventProcessor extends AbstractProcessor {
|
|||||||
var paramElement = eventHandler.getParameters().get(0);
|
var paramElement = eventHandler.getParameters().get(0);
|
||||||
var paramType = paramElement.asType();
|
var paramType = paramElement.asType();
|
||||||
|
|
||||||
// Check for valid event type
|
// Check for handlers for abstract types that aren't polymorphic
|
||||||
if (useParameter && !processingEnv.getTypeUtils().isAssignable(paramType,
|
if (eventHandler.getAnnotation(Polymorphic.class) == null
|
||||||
getTypeMirror(IEvent.class)))
|
&& paramType.getKind() == TypeKind.DECLARED) {
|
||||||
error(paramElement, "Parameter must implement IEvent");
|
|
||||||
|
|
||||||
// Check for handlers for abstract types that don't include subtypes
|
|
||||||
if (!eventAnnotation.includeSubtypes() && 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))
|
||||||
warning(paramElement,
|
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
|
|
||||||
if (!eventListener.getInterfaces().contains(getTypeMirror(EventListener.class)))
|
|
||||||
warning(eventHandler.getEnclosingElement(),
|
|
||||||
"Class should implement EventListener interface");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -9,11 +9,10 @@ import java.lang.annotation.*;
|
|||||||
* Indicates that a method is an event handler. To be successfully used as such, the method has to
|
* Indicates that a method is an event handler. To be successfully used as such, the method has to
|
||||||
* comply with the following specifications:
|
* comply with the following specifications:
|
||||||
* <ul>
|
* <ul>
|
||||||
* <li>Declared inside a class that implements {@link EventListener}</li>
|
|
||||||
* <li>Specifying an event type by either
|
* <li>Specifying an event type by either
|
||||||
* <ul>
|
* <ul>
|
||||||
* <li>Declaring one parameter of a type that implements {@link IEvent}</li>
|
* <li>Declaring one object parameter</li>
|
||||||
* <li>Defining the class of the event using the {@link Event#eventType()} value</li>
|
* <li>Defining the class of the event using the annotation value</li>
|
||||||
* </ul>
|
* </ul>
|
||||||
* </li>
|
* </li>
|
||||||
* <li>Return type of {@code void}</li>
|
* <li>Return type of {@code void}</li>
|
||||||
@ -21,31 +20,14 @@ import java.lang.annotation.*;
|
|||||||
*
|
*
|
||||||
* @author Kai S. K. Engelbart
|
* @author Kai S. K. Engelbart
|
||||||
* @since 0.0.1
|
* @since 0.0.1
|
||||||
|
* @see Polymorphic
|
||||||
|
* @see Priority
|
||||||
*/
|
*/
|
||||||
@Documented
|
@Documented
|
||||||
@Retention(RUNTIME)
|
@Retention(RUNTIME)
|
||||||
@Target(METHOD)
|
@Target(METHOD)
|
||||||
public @interface Event {
|
public @interface Event {
|
||||||
|
|
||||||
/**
|
|
||||||
* Defines the priority of the event handler. Handlers are executed in descending order of their
|
|
||||||
* priority.
|
|
||||||
* <p>
|
|
||||||
* 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 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.
|
||||||
@ -53,9 +35,9 @@ public @interface Event {
|
|||||||
* This is useful when the event handler does not utilize the event instance.
|
* This is useful when the event handler does not utilize the event instance.
|
||||||
*
|
*
|
||||||
* @return the event type accepted by the handler
|
* @return the event type accepted by the handler
|
||||||
* @since 0.0.3
|
* @since 1.0.0
|
||||||
*/
|
*/
|
||||||
Class<? extends IEvent> 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
|
* Signifies that the event type the handler listens to is determined by the type of its only
|
||||||
@ -63,5 +45,5 @@ public @interface Event {
|
|||||||
*
|
*
|
||||||
* @since 0.0.3
|
* @since 0.0.3
|
||||||
*/
|
*/
|
||||||
static final class USE_PARAMETER implements IEvent {}
|
static final class USE_PARAMETER {}
|
||||||
}
|
}
|
||||||
|
@ -51,9 +51,9 @@ public final class EventBus {
|
|||||||
return instance;
|
return instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
private final Map<Class<? extends IEvent>, TreeSet<EventHandler>> bindings =
|
private final Map<Class<?>, TreeSet<EventHandler>> bindings =
|
||||||
new ConcurrentHashMap<>();
|
new ConcurrentHashMap<>();
|
||||||
private final Set<EventListener> registeredListeners =
|
private final Set<Object> registeredListeners =
|
||||||
ConcurrentHashMap.newKeySet();
|
ConcurrentHashMap.newKeySet();
|
||||||
private final ThreadLocal<DispatchState> dispatchState =
|
private final ThreadLocal<DispatchState> dispatchState =
|
||||||
ThreadLocal.withInitial(DispatchState::new);
|
ThreadLocal.withInitial(DispatchState::new);
|
||||||
@ -65,7 +65,7 @@ public final class EventBus {
|
|||||||
* @param event the event to dispatch
|
* @param event the event to dispatch
|
||||||
* @since 0.0.1
|
* @since 0.0.1
|
||||||
*/
|
*/
|
||||||
public void dispatch(IEvent event) {
|
public void dispatch(Object event) {
|
||||||
Objects.requireNonNull(event);
|
Objects.requireNonNull(event);
|
||||||
logger.log(Level.INFO, "Dispatching event {0}", 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
|
* @return all event handlers registered for the event class
|
||||||
* @since 0.0.1
|
* @since 0.0.1
|
||||||
*/
|
*/
|
||||||
private List<EventHandler> getHandlersFor(Class<? extends IEvent> eventClass) {
|
private List<EventHandler> getHandlersFor(Class<?> eventClass) {
|
||||||
|
|
||||||
// Get handlers defined for the event class
|
// Get handlers defined for the event class
|
||||||
Set<EventHandler> handlers = bindings.getOrDefault(eventClass, new TreeSet<>());
|
Set<EventHandler> handlers = bindings.getOrDefault(eventClass, new TreeSet<>());
|
||||||
@ -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);
|
||||||
@ -133,7 +133,7 @@ public final class EventBus {
|
|||||||
* @since 0.0.1
|
* @since 0.0.1
|
||||||
* @see Event
|
* @see Event
|
||||||
*/
|
*/
|
||||||
public void registerListener(EventListener listener) throws EventBusException {
|
public void registerListener(Object listener) throws EventBusException {
|
||||||
Objects.requireNonNull(listener);
|
Objects.requireNonNull(listener);
|
||||||
if (registeredListeners.contains(listener))
|
if (registeredListeners.contains(listener))
|
||||||
throw new EventBusException(listener + " already registered!");
|
throw new EventBusException(listener + " already registered!");
|
||||||
@ -170,7 +170,7 @@ public final class EventBus {
|
|||||||
* @param listener the listener to remove
|
* @param listener the listener to remove
|
||||||
* @since 0.0.1
|
* @since 0.0.1
|
||||||
*/
|
*/
|
||||||
public void removeListener(EventListener listener) {
|
public void removeListener(Object listener) {
|
||||||
Objects.requireNonNull(listener);
|
Objects.requireNonNull(listener);
|
||||||
logger.log(Level.INFO, "Removing event listener {0}", listener.getClass().getName());
|
logger.log(Level.INFO, "Removing event listener {0}", listener.getClass().getName());
|
||||||
|
|
||||||
@ -204,7 +204,7 @@ public final class EventBus {
|
|||||||
* @return all registered event listeners
|
* @return all registered event listeners
|
||||||
* @since 0.0.1
|
* @since 0.0.1
|
||||||
*/
|
*/
|
||||||
public Set<EventListener> getRegisteredListeners() {
|
public Set<Object> getRegisteredListeners() {
|
||||||
return Collections.unmodifiableSet(registeredListeners);
|
return Collections.unmodifiableSet(registeredListeners);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -13,10 +13,20 @@ import dev.kske.eventbus.core.Event.USE_PARAMETER;
|
|||||||
*/
|
*/
|
||||||
final class EventHandler implements Comparable<EventHandler> {
|
final class EventHandler implements Comparable<EventHandler> {
|
||||||
|
|
||||||
private final EventListener listener;
|
/**
|
||||||
|
* 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 Object listener;
|
||||||
private final Method method;
|
private final Method method;
|
||||||
private final Event annotation;
|
private final Class<?> eventType;
|
||||||
private final Class<? extends IEvent> eventType;
|
private final boolean useParameter;
|
||||||
|
private final boolean polymorphic;
|
||||||
|
private final int priority;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs an event handler.
|
* Constructs an event handler.
|
||||||
@ -28,17 +38,16 @@ final class EventHandler implements Comparable<EventHandler> {
|
|||||||
* specification
|
* specification
|
||||||
* @since 0.0.1
|
* @since 0.0.1
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("unchecked")
|
EventHandler(Object listener, Method method, Event annotation) throws EventBusException {
|
||||||
EventHandler(EventListener listener, Method method, Event annotation) throws EventBusException {
|
|
||||||
this.listener = listener;
|
this.listener = listener;
|
||||||
this.method = method;
|
this.method = method;
|
||||||
this.annotation = annotation;
|
useParameter = annotation.value() == USE_PARAMETER.class;
|
||||||
|
|
||||||
// Check for correct method signature and return type
|
// 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!");
|
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!");
|
throw new EventBusException(method + " defines an ambiguous event type!");
|
||||||
|
|
||||||
if (method.getParameterCount() > 1)
|
if (method.getParameterCount() > 1)
|
||||||
@ -47,31 +56,28 @@ final class EventHandler implements Comparable<EventHandler> {
|
|||||||
if (!method.getReturnType().equals(void.class))
|
if (!method.getReturnType().equals(void.class))
|
||||||
throw new EventBusException(method + " does not have a return type of void!");
|
throw new EventBusException(method + " does not have a return type of void!");
|
||||||
|
|
||||||
// Determine the event type
|
// Determine handler properties
|
||||||
Class<? extends IEvent> eventType = annotation.eventType();
|
eventType = useParameter ? method.getParameterTypes()[0] : annotation.value();
|
||||||
if (eventType.equals(USE_PARAMETER.class)) {
|
polymorphic = method.isAnnotationPresent(Polymorphic.class);
|
||||||
var param = method.getParameterTypes()[0];
|
priority = method.isAnnotationPresent(Priority.class)
|
||||||
if (!IEvent.class.isAssignableFrom(param))
|
? method.getAnnotation(Priority.class).value()
|
||||||
throw new EventBusException(param + " is not of type IEvent!");
|
: DEFAULT_PRIORITY;
|
||||||
eventType = (Class<? extends IEvent>) param;
|
|
||||||
}
|
|
||||||
this.eventType = eventType;
|
|
||||||
|
|
||||||
// Allow access if the method is non-public
|
// Allow access if the method is non-public
|
||||||
method.setAccessible(true);
|
method.setAccessible(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Compares this to another event handler based on {@link Event#priority()}. In case of equal
|
* Compares this to another event handler based on priority. In case of equal priority a
|
||||||
* priority a non-zero value based on hash codes is returned.
|
* 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
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public int compareTo(EventHandler other) {
|
public int compareTo(EventHandler other) {
|
||||||
int priority = other.annotation.priority() - annotation.priority();
|
int priority = other.priority - this.priority;
|
||||||
if (priority == 0)
|
if (priority == 0)
|
||||||
priority = listener.hashCode() - other.listener.hashCode();
|
priority = listener.hashCode() - other.listener.hashCode();
|
||||||
return priority == 0 ? hashCode() - other.hashCode() : priority;
|
return priority == 0 ? hashCode() - other.hashCode() : priority;
|
||||||
@ -79,7 +85,9 @@ final class EventHandler implements Comparable<EventHandler> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return String.format("EventHandler[method=%s, annotation=%s]", method, annotation);
|
return String.format(
|
||||||
|
"EventHandler[method=%s, eventType=%s, useParameter=%b, polymorphic=%b, priority=%d]",
|
||||||
|
method, eventType, useParameter, polymorphic, priority);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -89,17 +97,13 @@ final class EventHandler implements Comparable<EventHandler> {
|
|||||||
* @throws EventBusException if the handler throws an exception
|
* @throws EventBusException if the handler throws an exception
|
||||||
* @since 0.0.1
|
* @since 0.0.1
|
||||||
*/
|
*/
|
||||||
void execute(IEvent event) throws EventBusException {
|
void execute(Object event) throws EventBusException {
|
||||||
try {
|
try {
|
||||||
if (annotation.eventType().equals(USE_PARAMETER.class))
|
if (useParameter)
|
||||||
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -108,29 +112,25 @@ final class EventHandler implements Comparable<EventHandler> {
|
|||||||
* @return the listener containing this handler
|
* @return the listener containing this handler
|
||||||
* @since 0.0.1
|
* @since 0.0.1
|
||||||
*/
|
*/
|
||||||
EventListener getListener() { return listener; }
|
Object getListener() { return listener; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the event annotation
|
* @return the event type this handler listens for
|
||||||
* @since 0.0.1
|
|
||||||
*/
|
|
||||||
Event getAnnotation() { return annotation; }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return the priority of the event annotation
|
|
||||||
* @since 0.0.1
|
|
||||||
*/
|
|
||||||
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
|
* @since 0.0.3
|
||||||
*/
|
*/
|
||||||
Class<? extends IEvent> getEventType() { return eventType; }
|
Class<?> getEventType() { return eventType; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the priority of this handler
|
||||||
|
* @since 0.0.1
|
||||||
|
* @see Priority
|
||||||
|
*/
|
||||||
|
int getPriority() { return priority; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return whether this handler is polymorphic
|
||||||
|
* @since 1.0.0
|
||||||
|
* @see Polymorphic
|
||||||
|
*/
|
||||||
|
boolean isPolymorphic() { return polymorphic; }
|
||||||
}
|
}
|
||||||
|
@ -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 {}
|
|
@ -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 {}
|
|
@ -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 {}
|
@ -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.
|
||||||
|
* <p>
|
||||||
|
* Handlers without this annotation have the default priority of 100.
|
||||||
|
* <p>
|
||||||
|
* 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();
|
||||||
|
}
|
@ -11,7 +11,7 @@ import org.junit.jupiter.api.*;
|
|||||||
* @author Leon Hofmeister
|
* @author Leon Hofmeister
|
||||||
* @since 0.1.0
|
* @since 0.1.0
|
||||||
*/
|
*/
|
||||||
class CancelTest implements EventListener {
|
class CancelTest {
|
||||||
|
|
||||||
EventBus bus;
|
EventBus bus;
|
||||||
int hits;
|
int hits;
|
||||||
@ -39,13 +39,15 @@ class CancelTest implements EventListener {
|
|||||||
assertEquals(1, hits);
|
assertEquals(1, hits);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Event(eventType = SimpleEvent.class, priority = 100)
|
@Event(SimpleEvent.class)
|
||||||
|
@Priority(100)
|
||||||
void onSimpleFirst() {
|
void onSimpleFirst() {
|
||||||
++hits;
|
++hits;
|
||||||
bus.cancel();
|
bus.cancel();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Event(eventType = SimpleEvent.class, priority = 50)
|
@Event(SimpleEvent.class)
|
||||||
|
@Priority(50)
|
||||||
void onSimpleSecond() {
|
void onSimpleSecond() {
|
||||||
++hits;
|
++hits;
|
||||||
}
|
}
|
||||||
|
@ -10,7 +10,7 @@ import org.junit.jupiter.api.*;
|
|||||||
* @author Kai S. K. Engelbart
|
* @author Kai S. K. Engelbart
|
||||||
* @since 0.0.1
|
* @since 0.0.1
|
||||||
*/
|
*/
|
||||||
class DispatchTest implements EventListener {
|
class DispatchTest {
|
||||||
|
|
||||||
EventBus bus;
|
EventBus bus;
|
||||||
static int hits;
|
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.
|
* and a static handler.
|
||||||
*
|
*
|
||||||
* @since 0.0.1
|
* @since 0.0.1
|
||||||
@ -38,19 +38,22 @@ class DispatchTest implements EventListener {
|
|||||||
bus.dispatch(new SimpleEvent());
|
bus.dispatch(new SimpleEvent());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Event(eventType = SimpleEvent.class, includeSubtypes = true, priority = 200)
|
@Event(SimpleEvent.class)
|
||||||
|
@Priority(200)
|
||||||
|
@Polymorphic
|
||||||
void onSimpleEventFirst() {
|
void onSimpleEventFirst() {
|
||||||
++hits;
|
++hits;
|
||||||
assertTrue(hits == 1 || hits == 2);
|
assertTrue(hits == 1 || hits == 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Event(eventType = SimpleEvent.class, priority = 150)
|
@Event(SimpleEvent.class)
|
||||||
|
@Priority(150)
|
||||||
static void onSimpleEventSecond() {
|
static void onSimpleEventSecond() {
|
||||||
++hits;
|
++hits;
|
||||||
assertEquals(3, hits);
|
assertEquals(3, hits);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Event(priority = 100)
|
@Event
|
||||||
void onSimpleEventThird(SimpleEvent event) {
|
void onSimpleEventThird(SimpleEvent event) {
|
||||||
++hits;
|
++hits;
|
||||||
assertEquals(4, hits);
|
assertEquals(4, hits);
|
||||||
|
@ -6,4 +6,4 @@ package dev.kske.eventbus.core;
|
|||||||
* @author Kai S. K. Engelbart
|
* @author Kai S. K. Engelbart
|
||||||
* @since 0.0.1
|
* @since 0.0.1
|
||||||
*/
|
*/
|
||||||
public class SimpleEvent implements IEvent {}
|
public class SimpleEvent {}
|
||||||
|
Loading…
Reference in New Issue
Block a user