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:
		
							
								
								
									
										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 {} | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	