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 }