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 }