001    package org.bukkit.plugin;
002    
003    import java.io.File;
004    import java.lang.reflect.Constructor;
005    import java.lang.reflect.Method;
006    import java.util.ArrayList;
007    import java.util.Collection;
008    import java.util.HashMap;
009    import java.util.HashSet;
010    import java.util.Iterator;
011    import java.util.LinkedHashMap;
012    import java.util.LinkedList;
013    import java.util.List;
014    import java.util.Map;
015    import java.util.Set;
016    import java.util.WeakHashMap;
017    import java.util.logging.Level;
018    import java.util.regex.Matcher;
019    import java.util.regex.Pattern;
020    
021    import org.apache.commons.lang.Validate;
022    import org.bukkit.Server;
023    import org.bukkit.command.Command;
024    import org.bukkit.command.PluginCommandYamlParser;
025    import org.bukkit.command.SimpleCommandMap;
026    import org.bukkit.event.Event;
027    import org.bukkit.event.EventPriority;
028    import org.bukkit.event.HandlerList;
029    import org.bukkit.event.Listener;
030    import org.bukkit.permissions.Permissible;
031    import org.bukkit.permissions.Permission;
032    import org.bukkit.permissions.PermissionDefault;
033    import org.bukkit.util.FileUtil;
034    
035    import com.google.common.collect.ImmutableSet;
036    
037    /**
038     * Handles all plugin management from the Server
039     */
040    public final class SimplePluginManager implements PluginManager {
041        private final Server server;
042        private final Map<Pattern, PluginLoader> fileAssociations = new HashMap<Pattern, PluginLoader>();
043        private final List<Plugin> plugins = new ArrayList<Plugin>();
044        private final Map<String, Plugin> lookupNames = new HashMap<String, Plugin>();
045        private static File updateDirectory = null;
046        private final SimpleCommandMap commandMap;
047        private final Map<String, Permission> permissions = new HashMap<String, Permission>();
048        private final Map<Boolean, Set<Permission>> defaultPerms = new LinkedHashMap<Boolean, Set<Permission>>();
049        private final Map<String, Map<Permissible, Boolean>> permSubs = new HashMap<String, Map<Permissible, Boolean>>();
050        private final Map<Boolean, Map<Permissible, Boolean>> defSubs = new HashMap<Boolean, Map<Permissible, Boolean>>();
051        private boolean useTimings = false;
052    
053        public SimplePluginManager(Server instance, SimpleCommandMap commandMap) {
054            server = instance;
055            this.commandMap = commandMap;
056    
057            defaultPerms.put(true, new HashSet<Permission>());
058            defaultPerms.put(false, new HashSet<Permission>());
059        }
060    
061        /**
062         * Registers the specified plugin loader
063         *
064         * @param loader Class name of the PluginLoader to register
065         * @throws IllegalArgumentException Thrown when the given Class is not a
066         *     valid PluginLoader
067         */
068        public void registerInterface(Class<? extends PluginLoader> loader) throws IllegalArgumentException {
069            PluginLoader instance;
070    
071            if (PluginLoader.class.isAssignableFrom(loader)) {
072                Constructor<? extends PluginLoader> constructor;
073    
074                try {
075                    constructor = loader.getConstructor(Server.class);
076                    instance = constructor.newInstance(server);
077                } catch (NoSuchMethodException ex) {
078                    String className = loader.getName();
079    
080                    throw new IllegalArgumentException(String.format("Class %s does not have a public %s(Server) constructor", className, className), ex);
081                } catch (Exception ex) {
082                    throw new IllegalArgumentException(String.format("Unexpected exception %s while attempting to construct a new instance of %s", ex.getClass().getName(), loader.getName()), ex);
083                }
084            } else {
085                throw new IllegalArgumentException(String.format("Class %s does not implement interface PluginLoader", loader.getName()));
086            }
087    
088            Pattern[] patterns = instance.getPluginFileFilters();
089    
090            synchronized (this) {
091                for (Pattern pattern : patterns) {
092                    fileAssociations.put(pattern, instance);
093                }
094            }
095        }
096    
097        /**
098         * Loads the plugins contained within the specified directory
099         *
100         * @param directory Directory to check for plugins
101         * @return A list of all plugins loaded
102         */
103        public Plugin[] loadPlugins(File directory) {
104            Validate.notNull(directory, "Directory cannot be null");
105            Validate.isTrue(directory.isDirectory(), "Directory must be a directory");
106    
107            List<Plugin> result = new ArrayList<Plugin>();
108            Set<Pattern> filters = fileAssociations.keySet();
109    
110            if (!(server.getUpdateFolder().equals(""))) {
111                updateDirectory = new File(directory, server.getUpdateFolder());
112            }
113    
114            Map<String, File> plugins = new HashMap<String, File>();
115            Set<String> loadedPlugins = new HashSet<String>();
116            Map<String, Collection<String>> dependencies = new HashMap<String, Collection<String>>();
117            Map<String, Collection<String>> softDependencies = new HashMap<String, Collection<String>>();
118    
119            // This is where it figures out all possible plugins
120            for (File file : directory.listFiles()) {
121                PluginLoader loader = null;
122                for (Pattern filter : filters) {
123                    Matcher match = filter.matcher(file.getName());
124                    if (match.find()) {
125                        loader = fileAssociations.get(filter);
126                    }
127                }
128    
129                if (loader == null) continue;
130    
131                PluginDescriptionFile description = null;
132                try {
133                    description = loader.getPluginDescription(file);
134                    String name = description.getName();
135                    if (name.equalsIgnoreCase("bukkit") || name.equalsIgnoreCase("minecraft") || name.equalsIgnoreCase("mojang")) {
136                        server.getLogger().log(Level.SEVERE, "Could not load '" + file.getPath() + "' in folder '" + directory.getPath() + "': Restricted Name");
137                        continue;
138                    } else if (description.rawName.indexOf(' ') != -1) {
139                        server.getLogger().warning(String.format(
140                            "Plugin `%s' uses the space-character (0x20) in its name `%s' - this is discouraged",
141                            description.getFullName(),
142                            description.rawName
143                            ));
144                    }
145                } catch (InvalidDescriptionException ex) {
146                    server.getLogger().log(Level.SEVERE, "Could not load '" + file.getPath() + "' in folder '" + directory.getPath() + "'", ex);
147                    continue;
148                }
149    
150                File replacedFile = plugins.put(description.getName(), file);
151                if (replacedFile != null) {
152                    server.getLogger().severe(String.format(
153                        "Ambiguous plugin name `%s' for files `%s' and `%s' in `%s'",
154                        description.getName(),
155                        file.getPath(),
156                        replacedFile.getPath(),
157                        directory.getPath()
158                        ));
159                }
160    
161                Collection<String> softDependencySet = description.getSoftDepend();
162                if (softDependencySet != null && !softDependencySet.isEmpty()) {
163                    if (softDependencies.containsKey(description.getName())) {
164                        // Duplicates do not matter, they will be removed together if applicable
165                        softDependencies.get(description.getName()).addAll(softDependencySet);
166                    } else {
167                        softDependencies.put(description.getName(), new LinkedList<String>(softDependencySet));
168                    }
169                }
170    
171                Collection<String> dependencySet = description.getDepend();
172                if (dependencySet != null && !dependencySet.isEmpty()) {
173                    dependencies.put(description.getName(), new LinkedList<String>(dependencySet));
174                }
175    
176                Collection<String> loadBeforeSet = description.getLoadBefore();
177                if (loadBeforeSet != null && !loadBeforeSet.isEmpty()) {
178                    for (String loadBeforeTarget : loadBeforeSet) {
179                        if (softDependencies.containsKey(loadBeforeTarget)) {
180                            softDependencies.get(loadBeforeTarget).add(description.getName());
181                        } else {
182                            // softDependencies is never iterated, so 'ghost' plugins aren't an issue
183                            Collection<String> shortSoftDependency = new LinkedList<String>();
184                            shortSoftDependency.add(description.getName());
185                            softDependencies.put(loadBeforeTarget, shortSoftDependency);
186                        }
187                    }
188                }
189            }
190    
191            while (!plugins.isEmpty()) {
192                boolean missingDependency = true;
193                Iterator<String> pluginIterator = plugins.keySet().iterator();
194    
195                while (pluginIterator.hasNext()) {
196                    String plugin = pluginIterator.next();
197    
198                    if (dependencies.containsKey(plugin)) {
199                        Iterator<String> dependencyIterator = dependencies.get(plugin).iterator();
200    
201                        while (dependencyIterator.hasNext()) {
202                            String dependency = dependencyIterator.next();
203    
204                            // Dependency loaded
205                            if (loadedPlugins.contains(dependency)) {
206                                dependencyIterator.remove();
207    
208                            // We have a dependency not found
209                            } else if (!plugins.containsKey(dependency)) {
210                                missingDependency = false;
211                                File file = plugins.get(plugin);
212                                pluginIterator.remove();
213                                softDependencies.remove(plugin);
214                                dependencies.remove(plugin);
215    
216                                server.getLogger().log(
217                                    Level.SEVERE,
218                                    "Could not load '" + file.getPath() + "' in folder '" + directory.getPath() + "'",
219                                    new UnknownDependencyException(dependency));
220                                break;
221                            }
222                        }
223    
224                        if (dependencies.containsKey(plugin) && dependencies.get(plugin).isEmpty()) {
225                            dependencies.remove(plugin);
226                        }
227                    }
228                    if (softDependencies.containsKey(plugin)) {
229                        Iterator<String> softDependencyIterator = softDependencies.get(plugin).iterator();
230    
231                        while (softDependencyIterator.hasNext()) {
232                            String softDependency = softDependencyIterator.next();
233    
234                            // Soft depend is no longer around
235                            if (!plugins.containsKey(softDependency)) {
236                                softDependencyIterator.remove();
237                            }
238                        }
239    
240                        if (softDependencies.get(plugin).isEmpty()) {
241                            softDependencies.remove(plugin);
242                        }
243                    }
244                    if (!(dependencies.containsKey(plugin) || softDependencies.containsKey(plugin)) && plugins.containsKey(plugin)) {
245                        // We're clear to load, no more soft or hard dependencies left
246                        File file = plugins.get(plugin);
247                        pluginIterator.remove();
248                        missingDependency = false;
249    
250                        try {
251                            result.add(loadPlugin(file));
252                            loadedPlugins.add(plugin);
253                            continue;
254                        } catch (InvalidPluginException ex) {
255                            server.getLogger().log(Level.SEVERE, "Could not load '" + file.getPath() + "' in folder '" + directory.getPath() + "'", ex);
256                        }
257                    }
258                }
259    
260                if (missingDependency) {
261                    // We now iterate over plugins until something loads
262                    // This loop will ignore soft dependencies
263                    pluginIterator = plugins.keySet().iterator();
264    
265                    while (pluginIterator.hasNext()) {
266                        String plugin = pluginIterator.next();
267    
268                        if (!dependencies.containsKey(plugin)) {
269                            softDependencies.remove(plugin);
270                            missingDependency = false;
271                            File file = plugins.get(plugin);
272                            pluginIterator.remove();
273    
274                            try {
275                                result.add(loadPlugin(file));
276                                loadedPlugins.add(plugin);
277                                break;
278                            } catch (InvalidPluginException ex) {
279                                server.getLogger().log(Level.SEVERE, "Could not load '" + file.getPath() + "' in folder '" + directory.getPath() + "'", ex);
280                            }
281                        }
282                    }
283                    // We have no plugins left without a depend
284                    if (missingDependency) {
285                        softDependencies.clear();
286                        dependencies.clear();
287                        Iterator<File> failedPluginIterator = plugins.values().iterator();
288    
289                        while (failedPluginIterator.hasNext()) {
290                            File file = failedPluginIterator.next();
291                            failedPluginIterator.remove();
292                            server.getLogger().log(Level.SEVERE, "Could not load '" + file.getPath() + "' in folder '" + directory.getPath() + "': circular dependency detected");
293                        }
294                    }
295                }
296            }
297    
298            return result.toArray(new Plugin[result.size()]);
299        }
300    
301        /**
302         * Loads the plugin in the specified file
303         * <p>
304         * File must be valid according to the current enabled Plugin interfaces
305         *
306         * @param file File containing the plugin to load
307         * @return The Plugin loaded, or null if it was invalid
308         * @throws InvalidPluginException Thrown when the specified file is not a
309         *     valid plugin
310         * @throws UnknownDependencyException If a required dependency could not
311         *     be found
312         */
313        public synchronized Plugin loadPlugin(File file) throws InvalidPluginException, UnknownDependencyException {
314            Validate.notNull(file, "File cannot be null");
315    
316            checkUpdate(file);
317    
318            Set<Pattern> filters = fileAssociations.keySet();
319            Plugin result = null;
320    
321            for (Pattern filter : filters) {
322                String name = file.getName();
323                Matcher match = filter.matcher(name);
324    
325                if (match.find()) {
326                    PluginLoader loader = fileAssociations.get(filter);
327    
328                    result = loader.loadPlugin(file);
329                }
330            }
331    
332            if (result != null) {
333                plugins.add(result);
334                lookupNames.put(result.getDescription().getName(), result);
335            }
336    
337            return result;
338        }
339    
340        private void checkUpdate(File file) {
341            if (updateDirectory == null || !updateDirectory.isDirectory()) {
342                return;
343            }
344    
345            File updateFile = new File(updateDirectory, file.getName());
346            if (updateFile.isFile() && FileUtil.copy(updateFile, file)) {
347                updateFile.delete();
348            }
349        }
350    
351        /**
352         * Checks if the given plugin is loaded and returns it when applicable
353         * <p>
354         * Please note that the name of the plugin is case-sensitive
355         *
356         * @param name Name of the plugin to check
357         * @return Plugin if it exists, otherwise null
358         */
359        public synchronized Plugin getPlugin(String name) {
360            return lookupNames.get(name.replace(' ', '_'));
361        }
362    
363        public synchronized Plugin[] getPlugins() {
364            return plugins.toArray(new Plugin[0]);
365        }
366    
367        /**
368         * Checks if the given plugin is enabled or not
369         * <p>
370         * Please note that the name of the plugin is case-sensitive.
371         *
372         * @param name Name of the plugin to check
373         * @return true if the plugin is enabled, otherwise false
374         */
375        public boolean isPluginEnabled(String name) {
376            Plugin plugin = getPlugin(name);
377    
378            return isPluginEnabled(plugin);
379        }
380    
381        /**
382         * Checks if the given plugin is enabled or not
383         *
384         * @param plugin Plugin to check
385         * @return true if the plugin is enabled, otherwise false
386         */
387        public boolean isPluginEnabled(Plugin plugin) {
388            if ((plugin != null) && (plugins.contains(plugin))) {
389                return plugin.isEnabled();
390            } else {
391                return false;
392            }
393        }
394    
395        public void enablePlugin(final Plugin plugin) {
396            if (!plugin.isEnabled()) {
397                List<Command> pluginCommands = PluginCommandYamlParser.parse(plugin);
398    
399                if (!pluginCommands.isEmpty()) {
400                    commandMap.registerAll(plugin.getDescription().getName(), pluginCommands);
401                }
402    
403                try {
404                    plugin.getPluginLoader().enablePlugin(plugin);
405                } catch (Throwable ex) {
406                    server.getLogger().log(Level.SEVERE, "Error occurred (in the plugin loader) while enabling " + plugin.getDescription().getFullName() + " (Is it up to date?)", ex);
407                }
408    
409                HandlerList.bakeAll();
410            }
411        }
412    
413        public void disablePlugins() {
414            Plugin[] plugins = getPlugins();
415            for (int i = plugins.length - 1; i >= 0; i--) {
416                disablePlugin(plugins[i]);
417            }
418        }
419    
420        public void disablePlugin(final Plugin plugin) {
421            if (plugin.isEnabled()) {
422                try {
423                    plugin.getPluginLoader().disablePlugin(plugin);
424                } catch (Throwable ex) {
425                    server.getLogger().log(Level.SEVERE, "Error occurred (in the plugin loader) while disabling " + plugin.getDescription().getFullName() + " (Is it up to date?)", ex);
426                }
427    
428                try {
429                    server.getScheduler().cancelTasks(plugin);
430                } catch (Throwable ex) {
431                    server.getLogger().log(Level.SEVERE, "Error occurred (in the plugin loader) while cancelling tasks for " + plugin.getDescription().getFullName() + " (Is it up to date?)", ex);
432                }
433    
434                try {
435                    server.getServicesManager().unregisterAll(plugin);
436                } catch (Throwable ex) {
437                    server.getLogger().log(Level.SEVERE, "Error occurred (in the plugin loader) while unregistering services for " + plugin.getDescription().getFullName() + " (Is it up to date?)", ex);
438                }
439    
440                try {
441                    HandlerList.unregisterAll(plugin);
442                } catch (Throwable ex) {
443                    server.getLogger().log(Level.SEVERE, "Error occurred (in the plugin loader) while unregistering events for " + plugin.getDescription().getFullName() + " (Is it up to date?)", ex);
444                }
445    
446                try {
447                    server.getMessenger().unregisterIncomingPluginChannel(plugin);
448                    server.getMessenger().unregisterOutgoingPluginChannel(plugin);
449                } catch(Throwable ex) {
450                    server.getLogger().log(Level.SEVERE, "Error occurred (in the plugin loader) while unregistering plugin channels for " + plugin.getDescription().getFullName() + " (Is it up to date?)", ex);
451                }
452            }
453        }
454    
455        public void clearPlugins() {
456            synchronized (this) {
457                disablePlugins();
458                plugins.clear();
459                lookupNames.clear();
460                HandlerList.unregisterAll();
461                fileAssociations.clear();
462                permissions.clear();
463                defaultPerms.get(true).clear();
464                defaultPerms.get(false).clear();
465            }
466        }
467    
468        /**
469         * Calls an event with the given details.
470         * <p>
471         * This method only synchronizes when the event is not asynchronous.
472         *
473         * @param event Event details
474         */
475        public void callEvent(Event event) {
476            if (event.isAsynchronous()) {
477                if (Thread.holdsLock(this)) {
478                    throw new IllegalStateException(event.getEventName() + " cannot be triggered asynchronously from inside synchronized code.");
479                }
480                if (server.isPrimaryThread()) {
481                    throw new IllegalStateException(event.getEventName() + " cannot be triggered asynchronously from primary server thread.");
482                }
483                fireEvent(event);
484            } else {
485                synchronized (this) {
486                    fireEvent(event);
487                }
488            }
489        }
490    
491        private void fireEvent(Event event) {
492            HandlerList handlers = event.getHandlers();
493            RegisteredListener[] listeners = handlers.getRegisteredListeners();
494    
495            for (RegisteredListener registration : listeners) {
496                if (!registration.getPlugin().isEnabled()) {
497                    continue;
498                }
499    
500                try {
501                    registration.callEvent(event);
502                } catch (AuthorNagException ex) {
503                    Plugin plugin = registration.getPlugin();
504    
505                    if (plugin.isNaggable()) {
506                        plugin.setNaggable(false);
507    
508                        server.getLogger().log(Level.SEVERE, String.format(
509                                "Nag author(s): '%s' of '%s' about the following: %s",
510                                plugin.getDescription().getAuthors(),
511                                plugin.getDescription().getFullName(),
512                                ex.getMessage()
513                                ));
514                    }
515                } catch (Throwable ex) {
516                    server.getLogger().log(Level.SEVERE, "Could not pass event " + event.getEventName() + " to " + registration.getPlugin().getDescription().getFullName(), ex);
517                }
518            }
519        }
520    
521        public void registerEvents(Listener listener, Plugin plugin) {
522            if (!plugin.isEnabled()) {
523                throw new IllegalPluginAccessException("Plugin attempted to register " + listener + " while not enabled");
524            }
525    
526            for (Map.Entry<Class<? extends Event>, Set<RegisteredListener>> entry : plugin.getPluginLoader().createRegisteredListeners(listener, plugin).entrySet()) {
527                getEventListeners(getRegistrationClass(entry.getKey())).registerAll(entry.getValue());
528            }
529    
530        }
531    
532        public void registerEvent(Class<? extends Event> event, Listener listener, EventPriority priority, EventExecutor executor, Plugin plugin) {
533            registerEvent(event, listener, priority, executor, plugin, false);
534        }
535    
536        /**
537         * Registers the given event to the specified listener using a directly
538         * passed EventExecutor
539         *
540         * @param event Event class to register
541         * @param listener PlayerListener to register
542         * @param priority Priority of this event
543         * @param executor EventExecutor to register
544         * @param plugin Plugin to register
545         * @param ignoreCancelled Do not call executor if event was already
546         *     cancelled
547         */
548        public void registerEvent(Class<? extends Event> event, Listener listener, EventPriority priority, EventExecutor executor, Plugin plugin, boolean ignoreCancelled) {
549            Validate.notNull(listener, "Listener cannot be null");
550            Validate.notNull(priority, "Priority cannot be null");
551            Validate.notNull(executor, "Executor cannot be null");
552            Validate.notNull(plugin, "Plugin cannot be null");
553    
554            if (!plugin.isEnabled()) {
555                throw new IllegalPluginAccessException("Plugin attempted to register " + event + " while not enabled");
556            }
557    
558            if (useTimings) {
559                getEventListeners(event).register(new TimedRegisteredListener(listener, executor, priority, plugin, ignoreCancelled));
560            } else {
561                getEventListeners(event).register(new RegisteredListener(listener, executor, priority, plugin, ignoreCancelled));
562            }
563        }
564    
565        private HandlerList getEventListeners(Class<? extends Event> type) {
566            try {
567                Method method = getRegistrationClass(type).getDeclaredMethod("getHandlerList");
568                method.setAccessible(true);
569                return (HandlerList) method.invoke(null);
570            } catch (Exception e) {
571                throw new IllegalPluginAccessException(e.toString());
572            }
573        }
574    
575        private Class<? extends Event> getRegistrationClass(Class<? extends Event> clazz) {
576            try {
577                clazz.getDeclaredMethod("getHandlerList");
578                return clazz;
579            } catch (NoSuchMethodException e) {
580                if (clazz.getSuperclass() != null
581                        && !clazz.getSuperclass().equals(Event.class)
582                        && Event.class.isAssignableFrom(clazz.getSuperclass())) {
583                    return getRegistrationClass(clazz.getSuperclass().asSubclass(Event.class));
584                } else {
585                    throw new IllegalPluginAccessException("Unable to find handler list for event " + clazz.getName());
586                }
587            }
588        }
589    
590        public Permission getPermission(String name) {
591            return permissions.get(name.toLowerCase());
592        }
593    
594        public void addPermission(Permission perm) {
595            String name = perm.getName().toLowerCase();
596    
597            if (permissions.containsKey(name)) {
598                throw new IllegalArgumentException("The permission " + name + " is already defined!");
599            }
600    
601            permissions.put(name, perm);
602            calculatePermissionDefault(perm);
603        }
604    
605        public Set<Permission> getDefaultPermissions(boolean op) {
606            return ImmutableSet.copyOf(defaultPerms.get(op));
607        }
608    
609        public void removePermission(Permission perm) {
610            removePermission(perm.getName());
611        }
612    
613        public void removePermission(String name) {
614            permissions.remove(name.toLowerCase());
615        }
616    
617        public void recalculatePermissionDefaults(Permission perm) {
618            if (permissions.containsValue(perm)) {
619                defaultPerms.get(true).remove(perm);
620                defaultPerms.get(false).remove(perm);
621    
622                calculatePermissionDefault(perm);
623            }
624        }
625    
626        private void calculatePermissionDefault(Permission perm) {
627            if ((perm.getDefault() == PermissionDefault.OP) || (perm.getDefault() == PermissionDefault.TRUE)) {
628                defaultPerms.get(true).add(perm);
629                dirtyPermissibles(true);
630            }
631            if ((perm.getDefault() == PermissionDefault.NOT_OP) || (perm.getDefault() == PermissionDefault.TRUE)) {
632                defaultPerms.get(false).add(perm);
633                dirtyPermissibles(false);
634            }
635        }
636    
637        private void dirtyPermissibles(boolean op) {
638            Set<Permissible> permissibles = getDefaultPermSubscriptions(op);
639    
640            for (Permissible p : permissibles) {
641                p.recalculatePermissions();
642            }
643        }
644    
645        public void subscribeToPermission(String permission, Permissible permissible) {
646            String name = permission.toLowerCase();
647            Map<Permissible, Boolean> map = permSubs.get(name);
648    
649            if (map == null) {
650                map = new WeakHashMap<Permissible, Boolean>();
651                permSubs.put(name, map);
652            }
653    
654            map.put(permissible, true);
655        }
656    
657        public void unsubscribeFromPermission(String permission, Permissible permissible) {
658            String name = permission.toLowerCase();
659            Map<Permissible, Boolean> map = permSubs.get(name);
660    
661            if (map != null) {
662                map.remove(permissible);
663    
664                if (map.isEmpty()) {
665                    permSubs.remove(name);
666                }
667            }
668        }
669    
670        public Set<Permissible> getPermissionSubscriptions(String permission) {
671            String name = permission.toLowerCase();
672            Map<Permissible, Boolean> map = permSubs.get(name);
673    
674            if (map == null) {
675                return ImmutableSet.of();
676            } else {
677                return ImmutableSet.copyOf(map.keySet());
678            }
679        }
680    
681        public void subscribeToDefaultPerms(boolean op, Permissible permissible) {
682            Map<Permissible, Boolean> map = defSubs.get(op);
683    
684            if (map == null) {
685                map = new WeakHashMap<Permissible, Boolean>();
686                defSubs.put(op, map);
687            }
688    
689            map.put(permissible, true);
690        }
691    
692        public void unsubscribeFromDefaultPerms(boolean op, Permissible permissible) {
693            Map<Permissible, Boolean> map = defSubs.get(op);
694    
695            if (map != null) {
696                map.remove(permissible);
697    
698                if (map.isEmpty()) {
699                    defSubs.remove(op);
700                }
701            }
702        }
703    
704        public Set<Permissible> getDefaultPermSubscriptions(boolean op) {
705            Map<Permissible, Boolean> map = defSubs.get(op);
706    
707            if (map == null) {
708                return ImmutableSet.of();
709            } else {
710                return ImmutableSet.copyOf(map.keySet());
711            }
712        }
713    
714        public Set<Permission> getPermissions() {
715            return new HashSet<Permission>(permissions.values());
716        }
717    
718        public boolean useTimings() {
719            return useTimings;
720        }
721    
722        /**
723         * Sets whether or not per event timing code should be used
724         *
725         * @param use True if per event timing code should be used
726         */
727        public void useTimings(boolean use) {
728            useTimings = use;
729        }
730    }