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    }