001    package org.bukkit.plugin;
002    
003    import org.bukkit.Bukkit;
004    import org.bukkit.event.server.ServiceRegisterEvent;
005    import org.bukkit.event.server.ServiceUnregisterEvent;
006    
007    import com.google.common.collect.ImmutableList;
008    import com.google.common.collect.ImmutableSet;
009    
010    import java.util.ArrayList;
011    import java.util.Collections;
012    import java.util.HashMap;
013    import java.util.Iterator;
014    import java.util.Map;
015    import java.util.List;
016    import java.util.NoSuchElementException;
017    import java.util.Set;
018    
019    /**
020     * A simple services manager.
021     */
022    public class SimpleServicesManager implements ServicesManager {
023    
024        /**
025         * Map of providers.
026         */
027        private final Map<Class<?>, List<RegisteredServiceProvider<?>>> providers = new HashMap<Class<?>, List<RegisteredServiceProvider<?>>>();
028    
029        /**
030         * Register a provider of a service.
031         *
032         * @param <T> Provider
033         * @param service service class
034         * @param provider provider to register
035         * @param plugin plugin with the provider
036         * @param priority priority of the provider
037         */
038        public <T> void register(Class<T> service, T provider, Plugin plugin, ServicePriority priority) {
039            RegisteredServiceProvider<T> registeredProvider = null;
040            synchronized (providers) {
041                List<RegisteredServiceProvider<?>> registered = providers.get(service);
042                if (registered == null) {
043                    registered = new ArrayList<RegisteredServiceProvider<?>>();
044                    providers.put(service, registered);
045                }
046    
047                registeredProvider = new RegisteredServiceProvider<T>(service, provider, priority, plugin);
048    
049                // Insert the provider into the collection, much more efficient big O than sort
050                int position = Collections.binarySearch(registered, registeredProvider);
051                if (position < 0) {
052                    registered.add(-(position + 1), registeredProvider);
053                } else {
054                    registered.add(position, registeredProvider);
055                }
056    
057            }
058            Bukkit.getServer().getPluginManager().callEvent(new ServiceRegisterEvent(registeredProvider));
059        }
060    
061        /**
062         * Unregister all the providers registered by a particular plugin.
063         *
064         * @param plugin The plugin
065         */
066        public void unregisterAll(Plugin plugin) {
067            ArrayList<ServiceUnregisterEvent> unregisteredEvents = new ArrayList<ServiceUnregisterEvent>();
068            synchronized (providers) {
069                Iterator<Map.Entry<Class<?>, List<RegisteredServiceProvider<?>>>> it = providers.entrySet().iterator();
070    
071                try {
072                    while (it.hasNext()) {
073                        Map.Entry<Class<?>, List<RegisteredServiceProvider<?>>> entry = it.next();
074                        Iterator<RegisteredServiceProvider<?>> it2 = entry.getValue().iterator();
075    
076                        try {
077                            // Removed entries that are from this plugin
078    
079                            while (it2.hasNext()) {
080                                RegisteredServiceProvider<?> registered = it2.next();
081    
082                                if (registered.getPlugin().equals(plugin)) {
083                                    it2.remove();
084                                    unregisteredEvents.add(new ServiceUnregisterEvent(registered));
085                                }
086                            }
087                        } catch (NoSuchElementException e) { // Why does Java suck
088                        }
089    
090                        // Get rid of the empty list
091                        if (entry.getValue().size() == 0) {
092                            it.remove();
093                        }
094                    }
095                } catch (NoSuchElementException e) {}
096            }
097            for (ServiceUnregisterEvent event : unregisteredEvents) {
098                Bukkit.getServer().getPluginManager().callEvent(event);
099            }
100        }
101    
102        /**
103         * Unregister a particular provider for a particular service.
104         *
105         * @param service The service interface
106         * @param provider The service provider implementation
107         */
108        public void unregister(Class<?> service, Object provider) {
109            ArrayList<ServiceUnregisterEvent> unregisteredEvents = new ArrayList<ServiceUnregisterEvent>();
110            synchronized (providers) {
111                Iterator<Map.Entry<Class<?>, List<RegisteredServiceProvider<?>>>> it = providers.entrySet().iterator();
112    
113                try {
114                    while (it.hasNext()) {
115                        Map.Entry<Class<?>, List<RegisteredServiceProvider<?>>> entry = it.next();
116    
117                        // We want a particular service
118                        if (entry.getKey() != service) {
119                            continue;
120                        }
121    
122                        Iterator<RegisteredServiceProvider<?>> it2 = entry.getValue().iterator();
123    
124                        try {
125                            // Removed entries that are from this plugin
126    
127                            while (it2.hasNext()) {
128                                RegisteredServiceProvider<?> registered = it2.next();
129    
130                                if (registered.getProvider() == provider) {
131                                    it2.remove();
132                                    unregisteredEvents.add(new ServiceUnregisterEvent(registered));
133                                }
134                            }
135                        } catch (NoSuchElementException e) { // Why does Java suck
136                        }
137    
138                        // Get rid of the empty list
139                        if (entry.getValue().size() == 0) {
140                            it.remove();
141                        }
142                    }
143                } catch (NoSuchElementException e) {}
144            }
145            for (ServiceUnregisterEvent event : unregisteredEvents) {
146                Bukkit.getServer().getPluginManager().callEvent(event);
147            }
148        }
149    
150        /**
151         * Unregister a particular provider.
152         *
153         * @param provider The service provider implementation
154         */
155        public void unregister(Object provider) {
156            ArrayList<ServiceUnregisterEvent> unregisteredEvents = new ArrayList<ServiceUnregisterEvent>();
157            synchronized (providers) {
158                Iterator<Map.Entry<Class<?>, List<RegisteredServiceProvider<?>>>> it = providers.entrySet().iterator();
159    
160                try {
161                    while (it.hasNext()) {
162                        Map.Entry<Class<?>, List<RegisteredServiceProvider<?>>> entry = it.next();
163                        Iterator<RegisteredServiceProvider<?>> it2 = entry.getValue().iterator();
164    
165                        try {
166                            // Removed entries that are from this plugin
167    
168                            while (it2.hasNext()) {
169                                RegisteredServiceProvider<?> registered = it2.next();
170    
171                                if (registered.getProvider().equals(provider)) {
172                                    it2.remove();
173                                    unregisteredEvents.add(new ServiceUnregisterEvent(registered));
174                                }
175                            }
176                        } catch (NoSuchElementException e) { // Why does Java suck
177                        }
178    
179                        // Get rid of the empty list
180                        if (entry.getValue().size() == 0) {
181                            it.remove();
182                        }
183                    }
184                } catch (NoSuchElementException e) {}
185            }
186            for (ServiceUnregisterEvent event : unregisteredEvents) {
187                Bukkit.getServer().getPluginManager().callEvent(event);
188            }
189        }
190    
191        /**
192         * Queries for a provider. This may return if no provider has been
193         * registered for a service. The highest priority provider is returned.
194         *
195         * @param <T> The service interface
196         * @param service The service interface
197         * @return provider or null
198         */
199        public <T> T load(Class<T> service) {
200            synchronized (providers) {
201                List<RegisteredServiceProvider<?>> registered = providers.get(service);
202    
203                if (registered == null) {
204                    return null;
205                }
206    
207                // This should not be null!
208                return service.cast(registered.get(0).getProvider());
209            }
210        }
211    
212        /**
213         * Queries for a provider registration. This may return if no provider
214         * has been registered for a service.
215         *
216         * @param <T> The service interface
217         * @param service The service interface
218         * @return provider registration or null
219         */
220        @SuppressWarnings("unchecked")
221        public <T> RegisteredServiceProvider<T> getRegistration(Class<T> service) {
222            synchronized (providers) {
223                List<RegisteredServiceProvider<?>> registered = providers.get(service);
224    
225                if (registered == null) {
226                    return null;
227                }
228    
229                // This should not be null!
230                return (RegisteredServiceProvider<T>) registered.get(0);
231            }
232        }
233    
234        /**
235         * Get registrations of providers for a plugin.
236         *
237         * @param plugin The plugin
238         * @return provider registration or null
239         */
240        public List<RegisteredServiceProvider<?>> getRegistrations(Plugin plugin) {
241            ImmutableList.Builder<RegisteredServiceProvider<?>> ret = ImmutableList.<RegisteredServiceProvider<?>>builder();
242            synchronized (providers) {
243                for (List<RegisteredServiceProvider<?>> registered : providers.values()) {
244                    for (RegisteredServiceProvider<?> provider : registered) {
245                        if (provider.getPlugin().equals(plugin)) {
246                            ret.add(provider);
247                        }
248                    }
249                }
250            }
251            return ret.build();
252        }
253    
254        /**
255         * Get registrations of providers for a service. The returned list is
256         * an unmodifiable copy.
257         *
258         * @param <T> The service interface
259         * @param service The service interface
260         * @return a copy of the list of registrations
261         */
262        @SuppressWarnings("unchecked")
263        public <T> List<RegisteredServiceProvider<T>> getRegistrations(Class<T> service) {
264            ImmutableList.Builder<RegisteredServiceProvider<T>> ret;
265            synchronized (providers) {
266                List<RegisteredServiceProvider<?>> registered = providers.get(service);
267    
268                if (registered == null) {
269                    return ImmutableList.<RegisteredServiceProvider<T>>of();
270                }
271    
272                ret = ImmutableList.<RegisteredServiceProvider<T>>builder();
273    
274                for (RegisteredServiceProvider<?> provider : registered) {
275                    ret.add((RegisteredServiceProvider<T>) provider);
276                }
277    
278            }
279            return ret.build();
280        }
281    
282        /**
283         * Get a list of known services. A service is known if it has registered
284         * providers for it.
285         *
286         * @return a copy of the set of known services
287         */
288        public Set<Class<?>> getKnownServices() {
289            synchronized (providers) {
290                return ImmutableSet.<Class<?>>copyOf(providers.keySet());
291            }
292        }
293    
294        /**
295         * Returns whether a provider has been registered for a service.
296         *
297         * @param <T> service
298         * @param service service to check
299         * @return true if and only if there are registered providers
300         */
301        public <T> boolean isProvidedFor(Class<T> service) {
302            synchronized (providers) {
303                return providers.containsKey(service);
304            }
305        }
306    }