* A node may contain any number of children and/or listeners. When an event is called,
* the node will filter it based on the parameters given at creation and then propagate
* it down to child nodes and listeners if it passes.
*
* @param The event type accepted by this node
*/
public class EventNode {
/**
* Creates an event node which accepts any event type with no filtering.
*
* @param name The name of the node
* @return An event node with no filtering
*/
@Contract(value = "_ -> new", pure = true)
public static @NotNull EventNode all(@NotNull String name) {
return type(name, EventFilter.ALL);
}
/**
* Creates an event node which accepts any event of the given type. The type is provided
* by the {@link EventFilter}.
*
* For example, you could create an event filter which only accepts player events with the following
*
* var playerEventNode = EventNode.type("demo", EventFilter.PLAYER);
*
*
* @param name The name of the event node
* @param filter The event type filter to apply
* @param The resulting event type of the node
* @return A node with just an event type filter
*/
@Contract(value = "_, _ -> new", pure = true)
public static @NotNull EventNode type(@NotNull String name,
@NotNull EventFilter filter) {
return create(name, filter, null);
}
/**
* Creates an event node which accepts any event of the given type which passes
* the provided condition. The condition is based on the event object itself.
*
* For example, you could create an event filter which only accepts player events
* where the player is in the pos x/z quadrant of the world.
*
{@code
* var playerInPosXZNode = EventNode.event("abc", EventFilter.PLAYER, event -> {
* var position = event.getPlayer().getPosition();
* return position.getX() > 0 && position.getZ() > 0;
* });
* }
*
* @param name The name of the event node
* @param filter The event type filter to apply
* @param predicate The event condition
* @param The resulting event type of the node
* @return A node with an event type filter as well as a condition on the event.
*/
@Contract(value = "_, _, _ -> new", pure = true)
public static @NotNull EventNode event(@NotNull String name,
@NotNull EventFilter filter,
@NotNull Predicate predicate) {
return create(name, filter, (e, h) -> predicate.test(e));
}
/**
* Creates an event node which accepts any event of the given type which passes
* the provided condition. The condition is based on the event object as well as
* the event handler type defined in the {@link EventFilter}.
*
* For example, you could create an event filter which only accepts player events
* where the player is in the pos x/z quadrant of the world.
*
{@code
* var playerInPosXZNode = EventNode.type("abc", EventFilter.PLAYER, (event, player) -> {
* var position = player.getPosition();
* return position.getX() > 0 && position.getZ() > 0;
* });
* }
*
* @param name The name of the event node
* @param filter The event type filter to apply
* @param predicate The event condition
* @param The resulting event type of the node
* @param The handler type of the event filter
* @return A node with an event type filter as well as a condition on the event.
*/
@Contract(value = "_, _, _ -> new", pure = true)
public static @NotNull EventNode type(@NotNull String name,
@NotNull EventFilter filter,
@NotNull BiPredicate predicate) {
return create(name, filter, predicate);
}
/**
* Creates an event node which accepts any event of the given type which passes
* the provided condition. The condition is based on the event handler defined
* by the {@link EventFilter}.
*
* For example, you could create an event filter which only accepts player events
* where the player is in creative mode.
*
* var playerIsCreative = EventNode.value("abc", EventFilter.PLAYER, Player::isCreative);
*
*
* @param name The name of the event node
* @param filter The event type filter to apply
* @param predicate The event condition
* @param The resulting event type of the node
* @param The handler type of the event filter
* @return A node with an event type filter as well as a condition on the event.
*/
@Contract(value = "_, _, _ -> new", pure = true)
public static @NotNull EventNode value(@NotNull String name,
@NotNull EventFilter filter,
@NotNull Predicate predicate) {
return create(name, filter, (e, h) -> predicate.test(h));
}
/**
* Creates an event node which accepts any event of the given type which has a handler who
* has the given tag.
*
* The {@link EventFilter}'s resulting event type must be {@link TagReadable}.
*
* @param name The name of the event node
* @param filter The event type filter to apply
* @param tag The tag which must be contained on the event handler
* @param The resulting event type of the node
* @return A node with an event type filter as well as a handler with the provided tag
*/
@Contract(value = "_, _, _ -> new", pure = true)
public static @NotNull EventNode tag(@NotNull String name,
@NotNull EventFilter filter,
@NotNull Tag> tag) {
return create(name, filter, (e, h) -> h.hasTag(tag));
}
/**
* Creates an event node which accepts any event of the given type which has a handler who
* has an applicable tag. An applicable tag means that it passes the given condition.
*
* @param name The name of the event node
* @param filter The event type filter to apply
* @param tag The tag which must be contained on the event handler
* @param consumer The condition to test against the tag, if it exists.
* @param The resulting event type of the node
* @return A node with an event type filter as well as a handler with the provided tag
*/
@Contract(value = "_, _, _, _ -> new", pure = true)
public static @NotNull EventNode tag(@NotNull String name,
@NotNull EventFilter filter,
@NotNull Tag tag,
@NotNull Predicate<@Nullable V> consumer) {
return create(name, filter, (e, h) -> consumer.test(h.getTag(tag)));
}
public static @NotNull Mapped mapped(@NotNull String name,
@NotNull EventFilter filter,
@NotNull V value) {
return new Mapped<>(name, filter, value);
}
private static EventNode create(@NotNull String name,
@NotNull EventFilter filter,
@Nullable BiPredicate predicate) {
return new EventNode<>(name, filter, predicate != null ? (e, o) -> predicate.test(e, (V) o) : null);
}
private static final Map, List>> HANDLER_SUPPLIERS = new ConcurrentHashMap<>();
private static final Object GLOBAL_CHILD_LOCK = new Object();
private final Object lock = new Object();
private final Map, ListenerEntry> listenerMap = new ConcurrentHashMap<>();
private final Set> children = new CopyOnWriteArraySet<>();
private final Map