Add EventNode#hasListener

This commit is contained in:
TheMode 2021-08-20 05:57:29 +02:00
parent 88a3a7d37c
commit e16a662908
2 changed files with 37 additions and 9 deletions

View File

@ -213,6 +213,20 @@ public interface EventNode<T extends Event> {
*/ */
<E extends T> @NotNull ListenerHandle<E> getHandle(@NotNull Class<E> handleType); <E extends T> @NotNull ListenerHandle<E> getHandle(@NotNull Class<E> handleType);
/**
* Gets if any listener has been registered for the given handle.
* May trigger an update if the cached data is not correct.
* <p>
* Useful if you are able to avoid expensive computation in the case where
* the event is unused. Be aware that {@link #call(Event, ListenerHandle)}
* has similar optimization built-in.
*
* @param handle the listener handle
* @param <E> the event type
* @return true if the event has 1 or more listeners
*/
<E extends T> boolean hasListener(@NotNull ListenerHandle<E> handle);
/** /**
* Execute a cancellable event with a callback to execute if the event is successful. * Execute a cancellable event with a callback to execute if the event is successful.
* Event conditions and propagation is the same as {@link #call(Event)}. * Event conditions and propagation is the same as {@link #call(Event)}.

View File

@ -43,11 +43,7 @@ class EventNodeImpl<T extends Event> implements EventNode<T> {
Check.argCondition(castedHandle.node != this, "Invalid handle owner"); Check.argCondition(castedHandle.node != this, "Invalid handle owner");
List<Consumer<T>> listeners = castedHandle.listeners; List<Consumer<T>> listeners = castedHandle.listeners;
if (!castedHandle.updated) { if (!castedHandle.updated) {
synchronized (GLOBAL_CHILD_LOCK) { castedHandle.update();
listeners.clear();
castedHandle.update(this);
castedHandle.updated = true;
}
} }
if (listeners.isEmpty()) return; if (listeners.isEmpty()) return;
for (Consumer<T> listener : listeners) { for (Consumer<T> listener : listeners) {
@ -62,6 +58,16 @@ class EventNodeImpl<T extends Event> implements EventNode<T> {
aClass -> new Handle<>(this, (Class<T>) aClass)); aClass -> new Handle<>(this, (Class<T>) aClass));
} }
@Override
public <E extends T> boolean hasListener(@NotNull ListenerHandle<E> handle) {
var castedHandle = (Handle<T>) handle;
List<Consumer<T>> listeners = castedHandle.listeners;
if (!castedHandle.updated) {
castedHandle.update();
}
return !listeners.isEmpty();
}
@Override @Override
public <E extends T> @NotNull List<EventNode<E>> findChildren(@NotNull String name, Class<E> eventType) { public <E extends T> @NotNull List<EventNode<E>> findChildren(@NotNull String name, Class<E> eventType) {
synchronized (GLOBAL_CHILD_LOCK) { synchronized (GLOBAL_CHILD_LOCK) {
@ -255,17 +261,25 @@ class EventNodeImpl<T extends Event> implements EventNode<T> {
} }
private static final class Handle<E extends Event> implements ListenerHandle<E> { private static final class Handle<E extends Event> implements ListenerHandle<E> {
private final EventNode<E> node; private final EventNodeImpl<E> node;
private final Class<E> eventType; private final Class<E> eventType;
private final List<Consumer<E>> listeners = new CopyOnWriteArrayList<>(); private final List<Consumer<E>> listeners = new CopyOnWriteArrayList<>();
private volatile boolean updated; private volatile boolean updated;
Handle(EventNode<E> node, Class<E> eventType) { Handle(EventNodeImpl<E> node, Class<E> eventType) {
this.node = node; this.node = node;
this.eventType = eventType; this.eventType = eventType;
} }
void update(EventNodeImpl<E> targetNode) { void update() {
synchronized (GLOBAL_CHILD_LOCK) {
listeners.clear();
recursiveUpdate(node);
updated = true;
}
}
private void recursiveUpdate(EventNodeImpl<E> targetNode) {
final var handleType = eventType; final var handleType = eventType;
ListenerEntry<E> entry = targetNode.listenerMap.get(handleType); ListenerEntry<E> entry = targetNode.listenerMap.get(handleType);
if (entry != null) appendEntry(listeners, entry, targetNode); if (entry != null) appendEntry(listeners, entry, targetNode);
@ -275,7 +289,7 @@ class EventNodeImpl<T extends Event> implements EventNode<T> {
children.stream() children.stream()
.filter(child -> child.eventType.isAssignableFrom(handleType)) // Invalid event type .filter(child -> child.eventType.isAssignableFrom(handleType)) // Invalid event type
.sorted(Comparator.comparing(EventNode::getPriority)) .sorted(Comparator.comparing(EventNode::getPriority))
.forEach(this::update); .forEach(this::recursiveUpdate);
} }
static <E extends Event> void appendEntry(List<Consumer<E>> handleListeners, ListenerEntry<E> entry, EventNodeImpl<E> targetNode) { static <E extends Event> void appendEntry(List<Consumer<E>> handleListeners, ListenerEntry<E> entry, EventNodeImpl<E> targetNode) {