All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.minestom.server.event.EventNodeLazyImpl Maven / Gradle / Ivy

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 extends EventNodeImpl {
    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 holder;
    private final WeakReference owner;
    @SuppressWarnings("unused")
    private boolean mapped;

    EventNodeLazyImpl(@NotNull EventNodeImpl holder,
                      @NotNull Object owner, @NotNull EventFilter filter) {
        super(owner.toString(), filter, null);
        this.holder = holder;
        this.owner = new WeakReference<>(owner);
    }

    @Override
    public @NotNull EventNode addChild(@NotNull EventNode child) {
        ensureMap();
        return super.addChild(child);
    }

    @Override
    public @NotNull EventNode addListener(@NotNull EventListener listener) {
        ensureMap();
        return super.addListener(listener);
    }

    @Override
    public @NotNull  EventNode addListener(@NotNull Class eventType, @NotNull Consumer<@NotNull E1> listener) {
        ensureMap();
        return super.addListener(eventType, listener);
    }

    @Override
    public @NotNull  EventNode map(@NotNull H value, @NotNull EventFilter filter) {
        final Object owner = retrieveOwner();
        if (owner != value) {
            throw new IllegalArgumentException("Cannot map an object to an already mapped node.");
        }
        return (EventNode) this;
    }

    @Override
    public void register(@NotNull EventBinding binding) {
        ensureMap();
        super.register(binding);
    }

    private void ensureMap() {
        if (MAPPED.compareAndSet(this, false, true)) {
            synchronized (GLOBAL_CHILD_LOCK) {
                var previous = this.holder.registeredMappedNode.putIfAbsent(retrieveOwner(), EventNodeImpl.class.cast(this));
                if (previous == null) invalidateEventsFor(holder);
            }
        }
    }

    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;
    }
}