Support parameter-less event handlers

- Add eventType value to Event
- Move semantic event handler checks to EventHandler
- Use Objects#requireNonNull(T) on public API method parameters
- Update README with a parameter-less event handlers section
This commit is contained in:
2020-09-08 19:47:21 +02:00
parent 5f88ad6095
commit 7a3debe444
5 changed files with 95 additions and 25 deletions

View File

@ -10,7 +10,11 @@ import java.lang.annotation.*;
* comply with the following specifications:
* <ul>
* <li>Declared inside a class that implements {@link EventListener}</li>
* <li>One parameter of a type that implements {@link IEvent}</li>
* <li>Specifying an event type by either</li>
* <ul>
* <li>Declaring one parameter of a type that implements {@link IEvent}</li>
* <li>Defining the class of the event using the {@link Event#eventType()} value</li>
* </ul>
* <li>Return type of {@code void}</li>
* </ul>
*
@ -31,4 +35,22 @@ public @interface Event {
* @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.
* <p>
* This is useful when the event handler does not utilize the event instance.
*
* @since 0.0.3
*/
Class<? extends IEvent> eventType() default USE_PARAMETER.class;
/**
* Signifies that the event type the handler listens to is determined by the type of its only
* parameter.
*
* @since 0.0.3
*/
static final class USE_PARAMETER implements IEvent {}
}

View File

@ -43,6 +43,7 @@ public final class EventBus {
* @since 0.0.1
*/
public void dispatch(IEvent event) {
Objects.requireNonNull(event);
getHandlersFor(event.getClass()).forEach(handler -> handler.execute(event));
}
@ -63,11 +64,12 @@ public final class EventBus {
*
* @param listener the listener to register
* @throws EventBusException if the listener is already registered or a declared event handler
* does not comply to the specification
* does not comply with the specification
* @since 0.0.1
* @see Event
*/
public void registerListener(EventListener listener) throws EventBusException {
Objects.requireNonNull(listener);
if (registeredListeners.contains(listener))
throw new EventBusException(listener + " already registered!");
@ -79,23 +81,12 @@ public final class EventBus {
if (annotation == null)
continue;
// Check for correct method signature and return type
if (method.getParameterCount() != 1)
throw new EventBusException(method + " does not have an argument count of 1!");
if (!method.getReturnType().equals(void.class))
throw new EventBusException(method + " does not have a return type of void!");
var param = method.getParameterTypes()[0];
if (!IEvent.class.isAssignableFrom(param))
throw new EventBusException(param + " is not of type IEvent!");
@SuppressWarnings("unchecked")
var realParam = (Class<? extends IEvent>) param;
if (!bindings.containsKey(realParam))
bindings.put(realParam, new TreeSet<>());
bindings.get(realParam).add(new EventHandler(listener, method, annotation));
// Initialize and bind the handler
var handler = new EventHandler(listener, method, annotation);
if (!bindings.containsKey(handler.getEventType()))
bindings.put(handler.getEventType(), new TreeSet<>());
bindings.get(handler.getEventType())
.add(handler);
}
}
@ -106,6 +97,7 @@ public final class EventBus {
* @since 0.0.1
*/
public void removeListener(EventListener listener) {
Objects.requireNonNull(listener);
for (var binding : bindings.values()) {
var it = binding.iterator();
while (it.hasNext())

View File

@ -2,6 +2,8 @@ package dev.kske.eventbus;
import java.lang.reflect.*;
import dev.kske.eventbus.Event.USE_PARAMETER;
/**
* Internal representation of an event handling method.
*
@ -14,6 +16,7 @@ final class EventHandler implements Comparable<EventHandler> {
private final EventListener listener;
private final Method method;
private final Event annotation;
private final Class<? extends IEvent> eventType;
/**
* Constructs an event handler.
@ -21,12 +24,40 @@ final class EventHandler implements Comparable<EventHandler> {
* @param listener the listener containing the handler
* @param method the handler method
* @param annotation the event annotation
* @throws EventBusException if the method or the annotation do not comply with the
* specification
* @since 0.0.1
*/
EventHandler(EventListener listener, Method method, Event annotation) {
@SuppressWarnings("unchecked")
EventHandler(EventListener listener, Method method, Event annotation) throws EventBusException {
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))
throw new EventBusException(method + " does not define an event type!");
if (method.getParameterCount() == 1 && !annotation.eventType().equals(USE_PARAMETER.class))
throw new EventBusException(method + " defines an ambiguous event type!");
if (method.getParameterCount() > 1)
throw new EventBusException(method + " defines more than one parameter!");
if (!method.getReturnType().equals(void.class))
throw new EventBusException(method + " does not have a return type of void!");
// Determine the event type
Class<? extends IEvent> eventType = annotation.eventType();
if (eventType.equals(USE_PARAMETER.class)) {
var param = method.getParameterTypes()[0];
if (!IEvent.class.isAssignableFrom(param))
throw new EventBusException(param + " is not of type IEvent!");
eventType = (Class<? extends IEvent>) param;
}
this.eventType = eventType;
// Allow access if the method is non-public
method.setAccessible(true);
}
@ -55,7 +86,10 @@ final class EventHandler implements Comparable<EventHandler> {
*/
void execute(IEvent event) throws EventBusException {
try {
method.invoke(listener, event);
if (annotation.eventType().equals(USE_PARAMETER.class))
method.invoke(listener, event);
else
method.invoke(listener);
} catch (
IllegalAccessException
| IllegalArgumentException
@ -82,4 +116,10 @@ final class EventHandler implements Comparable<EventHandler> {
* @since 0.0.1
*/
int getPriority() { return annotation.priority(); }
/**
* @return the event type this handler listens to
* @since 0.0.3
*/
Class<? extends IEvent> getEventType() { return eventType; }
}