001 package org.bukkit.event; 002 003 import org.bukkit.plugin.Plugin; 004 import org.bukkit.plugin.RegisteredListener; 005 006 import java.util.*; 007 import java.util.Map.Entry; 008 009 /** 010 * A list of event handlers, stored per-event. Based on lahwran's fevents. 011 */ 012 public class HandlerList { 013 014 /** 015 * Handler array. This field being an array is the key to this system's 016 * speed. 017 */ 018 private volatile RegisteredListener[] handlers = null; 019 020 /** 021 * Dynamic handler lists. These are changed using register() and 022 * unregister() and are automatically baked to the handlers array any time 023 * they have changed. 024 */ 025 private final EnumMap<EventPriority, ArrayList<RegisteredListener>> handlerslots; 026 027 /** 028 * List of all HandlerLists which have been created, for use in bakeAll() 029 */ 030 private static ArrayList<HandlerList> allLists = new ArrayList<HandlerList>(); 031 032 /** 033 * Bake all handler lists. Best used just after all normal event 034 * registration is complete, ie just after all plugins are loaded if 035 * you're using fevents in a plugin system. 036 */ 037 public static void bakeAll() { 038 synchronized (allLists) { 039 for (HandlerList h : allLists) { 040 h.bake(); 041 } 042 } 043 } 044 045 /** 046 * Unregister all listeners from all handler lists. 047 */ 048 public static void unregisterAll() { 049 synchronized (allLists) { 050 for (HandlerList h : allLists) { 051 synchronized (h) { 052 for (List<RegisteredListener> list : h.handlerslots.values()) { 053 list.clear(); 054 } 055 h.handlers = null; 056 } 057 } 058 } 059 } 060 061 /** 062 * Unregister a specific plugin's listeners from all handler lists. 063 * 064 * @param plugin plugin to unregister 065 */ 066 public static void unregisterAll(Plugin plugin) { 067 synchronized (allLists) { 068 for (HandlerList h : allLists) { 069 h.unregister(plugin); 070 } 071 } 072 } 073 074 /** 075 * Unregister a specific listener from all handler lists. 076 * 077 * @param listener listener to unregister 078 */ 079 public static void unregisterAll(Listener listener) { 080 synchronized (allLists) { 081 for (HandlerList h : allLists) { 082 h.unregister(listener); 083 } 084 } 085 } 086 087 /** 088 * Create a new handler list and initialize using EventPriority. 089 * <p> 090 * The HandlerList is then added to meta-list for use in bakeAll() 091 */ 092 public HandlerList() { 093 handlerslots = new EnumMap<EventPriority, ArrayList<RegisteredListener>>(EventPriority.class); 094 for (EventPriority o : EventPriority.values()) { 095 handlerslots.put(o, new ArrayList<RegisteredListener>()); 096 } 097 synchronized (allLists) { 098 allLists.add(this); 099 } 100 } 101 102 /** 103 * Register a new listener in this handler list 104 * 105 * @param listener listener to register 106 */ 107 public synchronized void register(RegisteredListener listener) { 108 if (handlerslots.get(listener.getPriority()).contains(listener)) 109 throw new IllegalStateException("This listener is already registered to priority " + listener.getPriority().toString()); 110 handlers = null; 111 handlerslots.get(listener.getPriority()).add(listener); 112 } 113 114 /** 115 * Register a collection of new listeners in this handler list 116 * 117 * @param listeners listeners to register 118 */ 119 public void registerAll(Collection<RegisteredListener> listeners) { 120 for (RegisteredListener listener : listeners) { 121 register(listener); 122 } 123 } 124 125 /** 126 * Remove a listener from a specific order slot 127 * 128 * @param listener listener to remove 129 */ 130 public synchronized void unregister(RegisteredListener listener) { 131 if (handlerslots.get(listener.getPriority()).remove(listener)) { 132 handlers = null; 133 } 134 } 135 136 /** 137 * Remove a specific plugin's listeners from this handler 138 * 139 * @param plugin plugin to remove 140 */ 141 public synchronized void unregister(Plugin plugin) { 142 boolean changed = false; 143 for (List<RegisteredListener> list : handlerslots.values()) { 144 for (ListIterator<RegisteredListener> i = list.listIterator(); i.hasNext();) { 145 if (i.next().getPlugin().equals(plugin)) { 146 i.remove(); 147 changed = true; 148 } 149 } 150 } 151 if (changed) handlers = null; 152 } 153 154 /** 155 * Remove a specific listener from this handler 156 * 157 * @param listener listener to remove 158 */ 159 public synchronized void unregister(Listener listener) { 160 boolean changed = false; 161 for (List<RegisteredListener> list : handlerslots.values()) { 162 for (ListIterator<RegisteredListener> i = list.listIterator(); i.hasNext();) { 163 if (i.next().getListener().equals(listener)) { 164 i.remove(); 165 changed = true; 166 } 167 } 168 } 169 if (changed) handlers = null; 170 } 171 172 /** 173 * Bake HashMap and ArrayLists to 2d array - does nothing if not necessary 174 */ 175 public synchronized void bake() { 176 if (handlers != null) return; // don't re-bake when still valid 177 List<RegisteredListener> entries = new ArrayList<RegisteredListener>(); 178 for (Entry<EventPriority, ArrayList<RegisteredListener>> entry : handlerslots.entrySet()) { 179 entries.addAll(entry.getValue()); 180 } 181 handlers = entries.toArray(new RegisteredListener[entries.size()]); 182 } 183 184 /** 185 * Get the baked registered listeners associated with this handler list 186 * 187 * @return the array of registered listeners 188 */ 189 public RegisteredListener[] getRegisteredListeners() { 190 RegisteredListener[] handlers; 191 while ((handlers = this.handlers) == null) bake(); // This prevents fringe cases of returning null 192 return handlers; 193 } 194 195 /** 196 * Get a specific plugin's registered listeners associated with this 197 * handler list 198 * 199 * @param plugin the plugin to get the listeners of 200 * @return the list of registered listeners 201 */ 202 public static ArrayList<RegisteredListener> getRegisteredListeners(Plugin plugin) { 203 ArrayList<RegisteredListener> listeners = new ArrayList<RegisteredListener>(); 204 synchronized (allLists) { 205 for (HandlerList h : allLists) { 206 synchronized (h) { 207 for (List<RegisteredListener> list : h.handlerslots.values()) { 208 for (RegisteredListener listener : list) { 209 if (listener.getPlugin().equals(plugin)) { 210 listeners.add(listener); 211 } 212 } 213 } 214 } 215 } 216 } 217 return listeners; 218 } 219 220 /** 221 * Get a list of all handler lists for every event type 222 * 223 * @return the list of all handler lists 224 */ 225 @SuppressWarnings("unchecked") 226 public static ArrayList<HandlerList> getHandlerLists() { 227 synchronized (allLists) { 228 return (ArrayList<HandlerList>) allLists.clone(); 229 } 230 } 231 }