Split @Event Parameters Into @Polymorphic and @Property, Remove Marker Interfaces #5

Merged
kske merged 4 commits from f/new-annotations into develop 2021-02-15 20:38:19 +01:00
7 changed files with 75 additions and 43 deletions
Showing only changes of commit 9b1c708514 - Show all commits

View File

@ -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!");
}

View File

@ -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

View File

@ -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.
* <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 the event type the handler listens to. If this value is set, the handler is not
* allowed to declare parameters.

View File

@ -13,11 +13,20 @@ import dev.kske.eventbus.core.Event.USE_PARAMETER;
*/
final class EventHandler implements Comparable<EventHandler> {
/**
* 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<? extends IEvent> eventType;
private final boolean polymorphic;
private final int priority;
/**
* Constructs an event handler.
@ -58,14 +67,17 @@ final class EventHandler implements Comparable<EventHandler> {
}
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.
kske marked this conversation as resolved
Review

What about using Integer.compare(this.priority, other.priority)?
Or is that too much overhead? 😉

What about using `Integer.compare(this.priority, other.priority)`? Or is that too much overhead? 😉
Review

Actually, yes I think so. Integer.compare calls Integer.valueOf two times, generating two objects in the process just to execute a statement similar to the one I wrote.

But, to your credit, I didn't even consider the possibility, to thanks for the suggestion :)

Actually, yes I think so. `Integer.compare` calls `Integer.valueOf` two times, generating two objects in the process just to execute a statement similar to the one I wrote. But, to your credit, I didn't even consider the possibility, to thanks for the suggestion :)
* <p>
* 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<EventHandler> {
*/
@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<EventHandler> {
@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);
}
/**
kske marked this conversation as resolved Outdated
Outdated
Review

Why is that now an object instead of an event?

Why is that now an object instead of an event?
Outdated
Review

Because I got rid of the IEvent interface so that every object can be used as an event.

Because I got rid of the `IEvent` interface so that every object can be used as an event.
@ -109,16 +122,17 @@ final class EventHandler implements Comparable<EventHandler> {
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<? extends IEvent> 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<EventHandler> {
* @see Polymorphic
*/
boolean isPolymorphic() { return polymorphic; }
/**
* @return the event type this handler listens to
* @since 0.0.3
*/
Class<? extends IEvent> getEventType() { return eventType; }
}

View File

@ -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();
}

View File

@ -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;
}

View File

@ -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);