Загрузка данных
package releon.ru.api.events.bus;
import releon.ru.api.events.Cancellable;
import releon.ru.api.events.Event;
import releon.ru.api.events.annotation.SubscribeEvent;
import releon.ru.api.events.exception.EventDispatchException;
import releon.ru.api.events.exception.EventRegistrationException;
import releon.ru.api.events.listener.EventListener;
import releon.ru.api.events.listener.RegisteredListener;
import releon.ru.api.events.types.EventPriority;
import java.lang.reflect.Method;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.atomic.AtomicLong;
public final class EventBus {
private final Map<Class<? extends Event>, CopyOnWriteArrayList<RegisteredListener<?>>> listeners = new ConcurrentHashMap<>();
private final Map<Class<? extends Event>, RegisteredListener<?>[]> listenerCache = new ConcurrentHashMap<>();
private final Map<Object, List<EventSubscription>> ownerSubscriptions = Collections.synchronizedMap(new IdentityHashMap<>());
private final AtomicLong orderCounter = new AtomicLong();
public <T extends Event> EventSubscription subscribe(Class<T> eventType, EventListener<? super T> listener) {
return subscribe(eventType, EventPriority.NORMAL, false, listener, listener);
}
public <T extends Event> EventSubscription subscribe(Class<T> eventType,
EventPriority priority,
EventListener<? super T> listener) {
return subscribe(eventType, priority, false, listener, listener);
}
public <T extends Event> EventSubscription subscribe(Class<T> eventType,
EventPriority priority,
boolean receiveCancelled,
EventListener<? super T> listener) {
return subscribe(eventType, priority, receiveCancelled, listener, listener);
}
public void register(Object owner) {
if (owner == null) {
throw new EventRegistrationException("Event owner cannot be null");
}
List<EventSubscription> subscriptions = new ArrayList<>();
for (Method method : owner.getClass().getDeclaredMethods()) {
SubscribeEvent annotation = method.getAnnotation(SubscribeEvent.class);
if (annotation == null) {
continue;
}
subscriptions.add(registerMethod(owner, method, annotation));
}
if (!subscriptions.isEmpty()) {
ownerSubscriptions.put(owner, subscriptions);
}
}
public void unregister(Object owner) {
if (owner == null) {
return;
}
List<EventSubscription> subscriptions = ownerSubscriptions.remove(owner);
if (subscriptions == null) {
return;
}
for (EventSubscription subscription : subscriptions) {
subscription.unsubscribe();
}
}
public <T extends Event> T post(T event) {
if (event == null) {
throw new EventDispatchException(null, new NullPointerException("event"));
}
RegisteredListener<?>[] eventListeners = getListeners(event.getClass());
for (RegisteredListener<?> listener : eventListeners) {
if (event instanceof Cancellable cancellable && cancellable.isCancelled() && !listener.isReceiveCancelled()) {
continue;
}
invoke(listener, event);
}
return event;
}
@SuppressWarnings("unchecked")
private <T extends Event> EventSubscription subscribe(Class<T> eventType,
EventPriority priority,
boolean receiveCancelled,
EventListener<? super T> listener,
Object owner) {
if (eventType == null) {
throw new EventRegistrationException("Event type cannot be null");
}
if (listener == null) {
throw new EventRegistrationException("Event listener cannot be null");
}
RegisteredListener<T> registered = new RegisteredListener<>(
eventType,
listener,
priority == null ? EventPriority.NORMAL : priority,
receiveCancelled,
owner,
orderCounter.incrementAndGet()
);
listeners.computeIfAbsent(eventType, ignored -> new CopyOnWriteArrayList<>()).add(registered);
listenerCache.clear();
return new EventSubscription(this, eventType, registered);
}
void unsubscribe(Class<? extends Event> eventType, RegisteredListener<?> listener) {
CopyOnWriteArrayList<RegisteredListener<?>> registeredListeners = listeners.get(eventType);
if (registeredListeners == null) {
return;
}
registeredListeners.remove(listener);
if (registeredListeners.isEmpty()) {
listeners.remove(eventType);
}
listenerCache.clear();
}
@SuppressWarnings("unchecked")
private EventSubscription registerMethod(Object owner, Method method, SubscribeEvent annotation) {
if (method.getParameterCount() != 1) {
throw new EventRegistrationException("@SubscribeEvent method must have exactly one event parameter: " + method);
}
Class<?> parameterType = method.getParameterTypes()[0];
if (!Event.class.isAssignableFrom(parameterType)) {
throw new EventRegistrationException("@SubscribeEvent parameter must implement Event: " + method);
}
method.setAccessible(true);
Class<? extends Event> eventType = (Class<? extends Event>) parameterType;
MethodHandle handle;
try {
handle = MethodHandles.privateLookupIn(owner.getClass(), MethodHandles.lookup()).unreflect(method).bindTo(owner);
} catch (IllegalAccessException exception) {
throw new EventRegistrationException("Cannot access @SubscribeEvent method: " + method, exception);
}
EventListener<Event> reflectedListener = event -> {
try {
handle.invoke(event);
} catch (Throwable throwable) {
if (throwable instanceof Exception exception) {
throw exception;
}
if (throwable instanceof Error error) {
throw error;
}
throw new EventDispatchException(event, throwable);
}
};
return subscribe((Class<Event>) eventType, annotation.priority(), annotation.receiveCancelled(), reflectedListener, owner);
}
private RegisteredListener<?>[] getListeners(Class<? extends Event> eventType) {
return listenerCache.computeIfAbsent(eventType, this::resolveListeners);
}
private RegisteredListener<?>[] resolveListeners(Class<? extends Event> eventType) {
List<RegisteredListener<?>> resolved = new ArrayList<>();
for (Map.Entry<Class<? extends Event>, CopyOnWriteArrayList<RegisteredListener<?>>> entry : listeners.entrySet()) {
if (entry.getKey().isAssignableFrom(eventType)) {
resolved.addAll(entry.getValue());
}
}
resolved.sort(Comparator
.comparingInt((RegisteredListener<?> listener) -> listener.getPriority().getOrder())
.thenComparingLong(RegisteredListener::getOrder));
return resolved.toArray(RegisteredListener<?>[]::new);
}
@SuppressWarnings({"unchecked", "rawtypes"})
private void invoke(RegisteredListener<?> listener, Event event) {
try {
((RegisteredListener) listener).getListener().invoke(event);
} catch (Throwable throwable) {
throw new EventDispatchException(event, throwable);
}
}
}