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 }