package xapi.event.api;
import xapi.collect.X_Collect;
import xapi.collect.api.IntTo;
import xapi.collect.api.StringTo;
import xapi.event.impl.EventTypes;
import xapi.inject.X_Inject;
import xapi.log.X_Log;
import xapi.util.api.RemovalHandler;
import static xapi.collect.X_Collect.newStringMultiMap;
import javax.validation.constraints.NotNull;
import java.io.Serializable;
/**
* Created by James X. Nelson (james @wetheinter.net) on 7/16/16.
*/
public class EventManager implements Serializable {
protected final StringTo.Many<EventHandler<?, ?>> handlers;
protected final EventService eventService;
public EventManager() {
this(X_Inject.singleton(EventService.class));
}
public EventManager(EventService service) {
this.eventService = service;
handlers = newStringMultiMap(EventHandler.class, X_Collect.MUTABLE_INSERTION_ORDERED_SET);
}
public <Source, E extends IsEvent<Source>> RemovalHandler addHandler(IsEventType type, EventHandler<Source, E> lambda) {
// method references, by default, do NOT conform to object equality semantics.
// very bad things will happen if you add a non-serializable lambda and then try to use .removeHandler later.
final EventHandler<Source, E> handler = eventService.normalizeHandler(lambda);
final IntTo<EventHandler<?, ?>> handles = handlers.get(type.getEventType());
boolean added = handles.add(handler);
if (!added) {
X_Log.warn(getClass(), "Added duplicate handler?", handler, handles.get(handles.indexOf(handler)));
}
final RemovalHandler remover = () -> handles.remove(handler);
lambda.storeRemover(remover);
return remover;
}
public <Source, E extends IsEvent<Source>> boolean removeHandler(IsEventType type, EventHandler<Source, E> lambda) {
final IntTo<EventHandler<?, ?>> handles = handlers.get(type.getEventType());
if (handles.isEmpty()) {
return false;
}
boolean removed = handles.removeValue(lambda);
if (!removed) {
final EventHandler<Source, E> normalized = eventService.normalizeHandler(lambda);
if (normalized != lambda) {
removed = handles.removeValue(normalized);
}
if (!removed) {
assert !eventService.isLambda(lambda) : "CANNOT REMOVE A LAMBDA HANDLER CORRECTLY! " + lambda + " is a raw lambda,\n" +
"which are created as ad-hoc anonymous classes that do NOT conform to object identity semantics;\nif you want " +
"to be able to use EventManager.removeHandler correctly,\nyou MUST make sure any instance or closed-over references " +
"are Serializable AND immutable\n(and your EventService is configured to transform serializable lambdas into instances of " +
"EventHandlerWithIdentity),\nOR you must create your own EventHandler *class* which implements equals and " +
"hashCode correctly.\nIf you ignore this assertion, your application WILL leak memory / be unable to deregister " +
"event handlers (unless you store the RemovalHandler created in addHandler).\nYou have been warned!";
}
}
return removed;
}
public boolean handlesEvent(IsEventType type) {
return handlers.containsKey(type.getEventType());
}
public boolean fireEvent(@NotNull IsEvent<?> event) {
IntTo<EventHandler<?, ?>> handles = handlers.get(event.getTypeString());
if (handles.isEmpty()) {
handles = handlers.get(EventTypes.Unhandled.getEventType());
}
boolean allow = true;
for (EventHandler handle : handles.forEach()) {
allow = handle.handleEvent(event);
if (!allow) {
return false;
}
}
return true;
}
}