package wecui.fevents;
import java.util.ArrayList;
import java.util.EnumMap;
import java.util.Map.Entry;
/**
* @author lahwran
* @param <TEvent> Event type
*
*/
@SuppressWarnings("unchecked")
public class HandlerList<TEvent extends Event<TEvent>> {
/**
* handler array. this field being an array is the key to this system's speed.
*
* is initialized in bake().
*/
public Listener<TEvent>[][] handlers;
/**
* Int array same length as handlers. each value in this array is the index
* of an Order slot, corossponding to the equivalent value in handlers.
*
* is initialized in bake().
*/
public int[] handlerids;
/**
* Dynamic handler lists. These are changed using register() and
* unregister() and are automatically baked to the handlers array any
* time they have changed.
*/
private final EnumMap<Order, ArrayList<Listener<TEvent>>> handlerslots;
/**
* Whether the current handlerslist has been fully baked. When this is set
* to false, the Map<Order, List<Listener>> will be baked to Listener[][]
* next time the event is called.
*
* @see EventManager.callEvent
*/
private boolean baked = false;
/**
* List of all handlerlists which have been created, for use in bakeall()
*/
private static ArrayList<HandlerList> alllists = new ArrayList<HandlerList>();
/**
* Bake all handler lists. Best used just after all normal event
* registration is complete, ie just after all plugins are loaded if
* you're using fevents in a plugin system.
*/
public static void bakeall() {
for (HandlerList h : alllists) {
h.bake();
}
}
/**
* Create a new handler list and initialize using EventManager.Order
* handlerlist is then added to meta-list for use in bakeall()
*/
public HandlerList() {
handlerslots = new EnumMap<Order, ArrayList<Listener<TEvent>>>(Order.class);
for (Order o : Order.values()) {
handlerslots.put(o, new ArrayList<Listener<TEvent>>());
}
alllists.add(this);
}
/**
* Register a new listener in this handler list
* @param listener listener to register
* @param order order location at which to call provided listener
*/
public void register(Listener<TEvent> listener, Order order) {
if (handlerslots.get(order).contains(listener)) {
throw new IllegalStateException("This listener is already registered to order " + order.toString());
}
baked = false;
handlerslots.get(order).add(listener);
}
/**
* Remove a listener from all order slots
* @param listener listener to purge
*/
public void unregister(Listener<TEvent> listener) {
for (Order o : Order.values()) {
unregister(listener, o);
}
}
/**
* Remove a listener from a specific order slot
* @param listener listener to remove
* @param order order from which to remove listener
*/
public void unregister(Listener<TEvent> listener, Order order) {
if (handlerslots.get(order).contains(listener)) {
baked = false;
handlerslots.get(order).remove(listener);
}
}
/**
* Bake HashMap and ArrayLists to 2d array - does nothing if not necessary
*/
void bake() {
if (baked) {
return; // don't re-bake when still valid
}
ArrayList<Listener[]> handlerslist = new ArrayList<Listener[]>();
ArrayList<Integer> handleridslist = new ArrayList<Integer>();
for (Entry<Order, ArrayList<Listener<TEvent>>> entry : handlerslots.entrySet()) {
Order orderslot = entry.getKey();
ArrayList<Listener<TEvent>> list = entry.getValue();
int ord = orderslot.getIndex();
handlerslist.add(list.toArray(new Listener[list.size()]));
handleridslist.add(ord);
}
handlers = handlerslist.toArray(new Listener[handlerslist.size()][]);
handlerids = new int[handleridslist.size()];
for (int i = 0; i < handleridslist.size(); i++) {
handlerids[i] = handleridslist.get(i);
}
baked = true;
}
}