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:
		
							
								
								
									
										18
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										18
									
								
								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. | ||||
|   | ||||
| @@ -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)) | ||||
|   | ||||
| @@ -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. | ||||
|   | ||||
| @@ -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); | ||||
|   | ||||
| @@ -13,10 +13,11 @@ import dev.kske.eventbus.core.Event.USE_PARAMETER; | ||||
|  */ | ||||
| final class EventHandler implements Comparable<EventHandler> { | ||||
|  | ||||
| 	private final EventListener listener; | ||||
| 	private final Method method; | ||||
| 	private final Event annotation; | ||||
| 	private final Class<? extends IEvent> eventType; | ||||
| 	private final EventListener				listener; | ||||
| 	private final Method					method; | ||||
| 	private final Event						annotation; | ||||
| 	private final Class<? extends IEvent>	eventType; | ||||
| 	private final boolean					polymorphic; | ||||
|  | ||||
| 	/** | ||||
| 	 * Constructs an event handler. | ||||
| @@ -30,9 +31,9 @@ final class EventHandler implements Comparable<EventHandler> { | ||||
| 	 */ | ||||
| 	@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<EventHandler> { | ||||
| 				throw new EventBusException(param + " is not of type IEvent!"); | ||||
| 			eventType = (Class<? extends IEvent>) 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<EventHandler> { | ||||
| 	 * 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. | ||||
| 	 * <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 | ||||
| 	 */ | ||||
| @@ -91,15 +93,11 @@ final class EventHandler implements Comparable<EventHandler> { | ||||
| 	 */ | ||||
| 	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<EventHandler> { | ||||
| 	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 | ||||
|   | ||||
| @@ -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()); | ||||
| 	} | ||||
|  | ||||
| 	@Event(eventType = SimpleEvent.class, includeSubtypes = true, priority = 200) | ||||
| 	@Event(eventType = SimpleEvent.class, priority = 200) | ||||
| 	@Polymorphic | ||||
| 	void onSimpleEventFirst() { | ||||
| 		++hits; | ||||
| 		assertTrue(hits == 1 || hits == 2); | ||||
|   | ||||
		Reference in New Issue
	
	Block a user