/*
* LibGodshawk - General-purpose Java library
* Copyright (C) 2014-2015 godshawk
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.godshawk.lib.event.handlers;
import com.godshawk.lib.annotations.reflection.event.EventListener;
import com.godshawk.lib.event.EventBase;
import com.godshawk.lib.event.EventObject;
import com.godshawk.lib.interfaces.util.updaters.IToggleable;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.*;
@SuppressWarnings("unused")
public class EventManager {
private static EventManager instance;
private final Vector<EventObject> listeners;
private EventManager() {
listeners = new Vector<>();
}
public static EventManager getInstance() {
return instance == null ? instance = new EventManager() : instance;
}
public void registerListener(Object listener) {
if (exists(listener)) {
return;
}
Class<?> listenerClass = listener.getClass();
List<Method> methodsWithAnnotation = new ArrayList<>();
Arrays.stream(listenerClass.getDeclaredMethods()).
filter(m -> m.isAnnotationPresent(EventListener.class)).sequential().forEach(methodsWithAnnotation::add);
Set<Class<? extends EventBase>> eventClasses = new HashSet<>();
for (Method method : methodsWithAnnotation) {
EventListener annotation = method.getDeclaredAnnotation(EventListener.class);
Class<? extends EventBase> eventClass = annotation.event();
if (!eventClasses.contains(eventClass)) {
eventClasses.add(eventClass);
}
}
for (Class<? extends EventBase> eventClass : eventClasses) {
synchronized (listeners) {
listeners.add(new EventObject(listener, eventClass,
methodsWithAnnotation.stream().
filter(m -> m.getDeclaredAnnotation(EventListener.class).event().equals(eventClass)).sequential().
toArray(Method[]::new)));
}
}
}
public void unregisterListener(Object listener) {
if (!exists(listener)) {
return;
}
synchronized (listeners) {
Arrays.stream(getExisting(listener)).forEach(listeners::remove);
}
}
private boolean exists(Object o) {
return listeners.parallelStream().filter(l -> l.getInvokee().equals(o)).toArray(Object[]::new).length > 0;
}
private Object[] getExisting(Object o) {
return listeners.parallelStream().filter(l -> l.getInvokee().equals(o)).toArray(Object[]::new);
}
public Vector<EventObject> getListeners() {
synchronized (listeners) {
return listeners;
}
}
public <T extends EventBase> T fire(T event) {
synchronized (listeners) {
for (EventObject eo : listeners) {
if (!eo.getEventClass().equals(event.getClass())) {
continue;
}
if (eo.getInvokee() instanceof IToggleable) {
if (!((IToggleable) eo.getInvokee()).isState()) {
continue;
}
}
for (EventObject.MethodObject m : eo.getMethods()) {
try {
if (m.getParamCount() == 0) {
m.getMethod().invoke(eo.getInvokee());
} else if (m.getParamCount() == 1) {
m.getMethod().invoke(eo.getInvokee(), event);
} else {
throw new IllegalArgumentException(
String.format("Method %s in class %s (%s) has the wrong number of parameters (%s)!",
m.getMethod().getName(), eo.getInvokee().getClass().getSimpleName(),
eo.getInvokee().toString(), m.getParamCount())
);
}
} catch (IllegalAccessException | InvocationTargetException ex) {
ex.printStackTrace();
}
}
}
}
return event;
}
}