Minestom/src/main/java/net/minestom/server/event/EventNodeLazyImpl.java

88 lines
2.8 KiB
Java

package net.minestom.server.event;
import org.jetbrains.annotations.NotNull;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.VarHandle;
import java.lang.ref.WeakReference;
import java.util.function.Consumer;
final class EventNodeLazyImpl<E extends Event> extends EventNodeImpl<E> {
private static final VarHandle MAPPED;
static {
try {
MAPPED = MethodHandles.lookup().findVarHandle(EventNodeLazyImpl.class, "mapped", boolean.class);
} catch (NoSuchFieldException | IllegalAccessException e) {
throw new IllegalStateException(e);
}
}
private final EventNodeImpl<? super E> holder;
private final WeakReference<Object> owner;
@SuppressWarnings("unused")
private boolean mapped;
EventNodeLazyImpl(@NotNull EventNodeImpl<? super E> holder,
@NotNull Object owner, @NotNull EventFilter<E, ?> filter) {
super(owner.toString(), filter, null);
this.holder = holder;
this.owner = new WeakReference<>(owner);
}
@Override
public @NotNull EventNode<E> addChild(@NotNull EventNode<? extends E> child) {
ensureMap();
return super.addChild(child);
}
@Override
public @NotNull EventNode<E> addListener(@NotNull EventListener<? extends E> listener) {
ensureMap();
return super.addListener(listener);
}
@Override
public @NotNull <E1 extends E> EventNode<E> addListener(@NotNull Class<E1> eventType, @NotNull Consumer<@NotNull E1> listener) {
ensureMap();
return super.addListener(eventType, listener);
}
@Override
public @NotNull <E1 extends E, H> EventNode<E1> map(@NotNull H value, @NotNull EventFilter<E1, H> filter) {
final Object owner = retrieveOwner();
if (owner != value) {
throw new IllegalArgumentException("Cannot map an object to an already mapped node.");
}
//noinspection unchecked
return (EventNode<E1>) this;
}
@Override
public void register(@NotNull EventBinding<? extends E> binding) {
ensureMap();
super.register(binding);
}
private void ensureMap() {
if (MAPPED.compareAndSet(this, false, true)) {
GLOBAL_CHILD_LOCK.lock();
try {
//noinspection unchecked,RedundantClassCall
var previous = this.holder.registeredMappedNode.putIfAbsent(retrieveOwner(), EventNodeImpl.class.cast(this));
if (previous == null) invalidateEventsFor(holder);
} finally {
GLOBAL_CHILD_LOCK.unlock();
}
}
}
private Object retrieveOwner() {
final Object owner = this.owner.get();
if (owner == null) {
throw new IllegalStateException("Node handle is null. Be sure to never cache a local node.");
}
return owner;
}
}