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:
		| @@ -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 {} | ||||
| } | ||||
|   | ||||
| @@ -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()) | ||||
|   | ||||
| @@ -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; } | ||||
| } | ||||
|   | ||||
| @@ -30,8 +30,8 @@ class EventBusTest implements EventListener { | ||||
| 		assertEquals(2, hits); | ||||
| 	} | ||||
|  | ||||
| 	@Event(priority = 150) | ||||
| 	private void onSimpleEventFirst(SimpleEvent event) { | ||||
| 	@Event(eventType = SimpleEvent.class, priority = 150) | ||||
| 	private void onSimpleEventFirst() { | ||||
| 		++hits; | ||||
| 		assertEquals(1, hits); | ||||
| 	} | ||||
|   | ||||
		Reference in New Issue
	
	Block a user