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);
/**
* 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.
* 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");
List<Consumer<T>> listeners = castedHandle.listeners;
if (!castedHandle.updated) {
synchronized (GLOBAL_CHILD_LOCK) {
listeners.clear();
castedHandle.update(this);
castedHandle.updated = true;
}
castedHandle.update();
}
if (listeners.isEmpty()) return;
for (Consumer<T> listener : listeners) {
@ -62,6 +58,16 @@ class EventNodeImpl<T extends Event> implements EventNode<T> {
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
public <E extends T> @NotNull List<EventNode<E>> findChildren(@NotNull String name, Class<E> eventType) {
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 final EventNode<E> node;
private final EventNodeImpl<E> node;
private final Class<E> eventType;
private final List<Consumer<E>> listeners = new CopyOnWriteArrayList<>();
private volatile boolean updated;
Handle(EventNode<E> node, Class<E> eventType) {
Handle(EventNodeImpl<E> node, Class<E> eventType) {
this.node = node;
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;
ListenerEntry<E> entry = targetNode.listenerMap.get(handleType);
if (entry != null) appendEntry(listeners, entry, targetNode);
@ -275,7 +289,7 @@ class EventNodeImpl<T extends Event> implements EventNode<T> {
children.stream()
.filter(child -> child.eventType.isAssignableFrom(handleType)) // Invalid event type
.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) {