package eu.bibl.cfide.eventbus; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; public class EventBus { protected HashMap<Class<? extends Event>, List<EventListenerData>> registeredListeners; public EventBus() { registeredListeners = new HashMap<Class<? extends Event>, List<EventListenerData>>(); } /** * Registers all the methods marked with the {@link EventTarget} annotation as listeners. * @param src Source object */ public void register(Object src) { if (src == null) return; for (Method method : src.getClass().getDeclaredMethods()) { if (!isValid(method)) continue; @SuppressWarnings("unchecked") Class<? extends Event> eventClass = (Class<? extends Event>) method.getParameterTypes()[0]; EventListenerData data = new EventListenerData(method.getAnnotation(EventTarget.class).priority(), src, method); putMap(eventClass, data); } } /** * Registers all the methods marked with the {@link EventTarget} annotation that uses the appropriate event type. * @param src Source object. * @param eventClass Appropriate event type. */ public void register(Object src, Class<? extends Event> eventClass) { if (src == null) return; for (Method method : src.getClass().getDeclaredMethods()) { if (!isValid(method)) continue; if (!method.getParameterTypes()[0].equals(eventClass)) continue; EventListenerData data = new EventListenerData(method.getAnnotation(EventTarget.class).priority(), src, method); putMap(eventClass, data); } } /** * Unregisters all of the methods that have been registered as listeners. <br> * <b>NOTE: it is faster to use the {@link #unregister(Object, Class)} method to remove specific listener types. * @param src Source object.</b> */ public void unregister(Object src) { if (src == null) return; for (Class<? extends Event> eventClass : registeredListeners.keySet()) { List<EventListenerData> dataList = registeredListeners.get(eventClass); if (dataList == null) continue; ArrayList<EventListenerData> safeList = new ArrayList<EventListenerData>(dataList); for (EventListenerData data : safeList) { if (data.src.equals(src)) dataList.remove(data); } } } /** * Unregisters the methods that have been registered as listeners of the appropriate event type. * @param src Source object * @param eventClass Appropriate event type. */ public void unregister(Object src, Class<? extends Event> eventClass) { if (src == null) return; List<EventListenerData> dataList = registeredListeners.get(eventClass); if (dataList == null) return; ArrayList<EventListenerData> safeList = new ArrayList<EventListenerData>(dataList); for (EventListenerData data : safeList) { if (data.src.equals(src)) dataList.remove(data); } } /** * Sends event to all of the registered listeners of the appropriate type. * @param event Event to send. */ public void dispatch(Event event) { Class<? extends Event> eventClass = event.getClass(); List<EventListenerData> dataList = registeredListeners.get(eventClass); if (dataList == null) return; if (event instanceof EventStoppable) { EventStoppable stoppable = (EventStoppable) event; for (EventListenerData data : dataList) { try { data.method.invoke(data.src, event); } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { } if (stoppable.isStopped()) break; } } else { for (EventListenerData data : dataList) { try { data.method.invoke(data.src, event); } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { } } } } protected void putMap(Class<? extends Event> eventClasss, EventListenerData data) { List<EventListenerData> dataList = registeredListeners.get(eventClasss); if (dataList == null) dataList = new CopyOnWriteArrayList<EventListenerData>(); dataList.add(data); if (!registeredListeners.containsKey(eventClasss)) registeredListeners.put(eventClasss, dataList); prioritise(eventClasss); } protected void prioritise(Class<? extends Event> eventClass) { List<EventListenerData> dataList = registeredListeners.get(eventClass); List<EventListenerData> newList = new CopyOnWriteArrayList<EventListenerData>(); if (dataList != null) { for (EventPriority priority : EventPriority.values()) { for (EventListenerData data : dataList) { if (data.priority == priority) newList.add(data); } } registeredListeners.put(eventClass, newList); } } /** * Checks whether the method is valid to be registered as a listener method. * @param method Method to check. * @return Whether it is valid. */ protected boolean isValid(Method method) { return (method.getParameterTypes().length == 1) && method.isAnnotationPresent(EventTarget.class) && Event.class.isAssignableFrom(method.getParameterTypes()[0]); } }