/**
   * Loads the plugin in the specified file
   *
   * <p>File must be valid according to the current enabled Plugin interfaces
   *
   * @param file File containing the plugin to load
   * @return The Plugin loaded, or null if it was invalid
   * @throws InvalidPluginException Thrown when the specified file is not a valid plugin
   * @throws UnknownDependencyException If a required dependency could not be found
   */
  public synchronized Plugin loadPlugin(File file)
      throws InvalidPluginException, UnknownDependencyException {
    Validate.notNull(file, "File cannot be null");

    checkUpdate(file);

    Set<Pattern> filters = fileAssociations.keySet();
    Plugin result = null;

    for (Pattern filter : filters) {
      String name = file.getName();
      Matcher match = filter.matcher(name);

      if (match.find()) {
        PluginLoader loader = fileAssociations.get(filter);

        result = loader.loadPlugin(file);
      }
    }

    if (result != null) {
      plugins.add(result);
      lookupNames.put(result.getDescription().getName(), result);
    }

    return result;
  }
  /** Loads all plugins, calling onLoad, &c. */
  private void loadPlugins() {
    // clear the map
    commandMap.clearCommands();
    commandMap.register("glowstone", new ColorCommand("colors"));
    commandMap.register("glowstone", new TellrawCommand());

    File folder = new File(config.getString(ServerConfig.Key.PLUGIN_FOLDER));
    if (!folder.isDirectory() && !folder.mkdirs()) {
      logger.log(Level.SEVERE, "Could not create plugins directory: " + folder);
    }

    // clear plugins and prepare to load
    pluginManager.clearPlugins();
    pluginManager.registerInterface(JavaPluginLoader.class);
    Plugin[] plugins = pluginManager.loadPlugins(folder);

    // call onLoad methods
    for (Plugin plugin : plugins) {
      try {
        plugin.onLoad();
      } catch (Exception ex) {
        logger.log(Level.SEVERE, "Error loading " + plugin.getDescription().getFullName(), ex);
      }
    }
  }
  public void enablePlugin(final Plugin plugin) {
    if (!(plugin instanceof JavaPlugin)) {
      throw new IllegalArgumentException("Plugin is not associated with this PluginLoader");
    }

    if (!plugin.isEnabled()) {
      JavaPlugin jPlugin = (JavaPlugin) plugin;

      String pluginName = jPlugin.getDescription().getName();

      if (!loaders.containsKey(pluginName)) {
        loaders.put(pluginName, (PluginClassLoader) jPlugin.getClassLoader());
      }

      try {
        jPlugin.setEnabled(true);
      } catch (Throwable ex) {
        server
            .getLogger()
            .log(
                Level.SEVERE,
                "Error occurred while enabling "
                    + plugin.getDescription().getFullName()
                    + " (Is it up to date?): "
                    + ex.getMessage(),
                ex);
      }

      // Perhaps abort here, rather than continue going, but as it stands,
      // an abort is not possible the way it's currently written
      server.getPluginManager().callEvent(new PluginEnableEvent(plugin));
    }
  }
  @UnsafeMethod
  public synchronized void enablePlugin(Plugin plugin) {
    if (!CommonPlugin.class.isAssignableFrom(plugin.getClass())) {
      throw new IllegalArgumentException(
          "Cannot enable plugin with this PluginLoader as it is of the wrong type!");
    }
    if (!plugin.isEnabled()) {
      CommonPlugin cp = (CommonPlugin) plugin;
      String name = cp.getDescription().getName();

      if (!loaders.containsKey(name)) {
        loaders.put(name, (CommonClassLoader) cp.getClassLoader());
      }

      try {
        cp.setEnabled(true);
        cp.onEnable();
      } catch (Throwable e) {
        engine
            .getLogger()
            .log(
                Level.SEVERE,
                "An error occured when enabling '"
                    + plugin.getDescription().getFullName()
                    + "': "
                    + e.getMessage(),
                e);
      }

      engine.getEventManager().callEvent(new PluginEnableEvent(cp));
    }
  }
  public void enablePlugin(final Plugin plugin) {
    if (!plugin.isEnabled()) {
      List<Command> pluginCommands = PluginCommandYamlParser.parse(plugin);

      if (!pluginCommands.isEmpty()) {
        commandMap.registerAll(plugin.getDescription().getName(), pluginCommands);
      }

      try {
        plugin.getPluginLoader().enablePlugin(plugin);
      } catch (Throwable ex) {
        server
            .getLogger()
            .log(
                Level.SEVERE,
                "Error occurred (in the plugin loader) while enabling "
                    + plugin.getDescription().getFullName()
                    + " (Is it up to date?)",
                ex);
      }

      HandlerList.bakeAll();
    }
  }
  private void fireEvent(Event event) {
    HandlerList handlers = event.getHandlers();
    RegisteredListener[] listeners = handlers.getRegisteredListeners();

    for (RegisteredListener registration : listeners) {
      if (!registration.getPlugin().isEnabled()) {
        continue;
      }

      try {
        registration.callEvent(event);
      } catch (AuthorNagException ex) {
        Plugin plugin = registration.getPlugin();

        if (plugin.isNaggable()) {
          plugin.setNaggable(false);

          server
              .getLogger()
              .log(
                  Level.SEVERE,
                  String.format(
                      "Nag author(s): '%s' of '%s' about the following: %s",
                      plugin.getDescription().getAuthors(),
                      plugin.getDescription().getFullName(),
                      ex.getMessage()));
        }
      } catch (Throwable ex) {
        server
            .getLogger()
            .log(
                Level.SEVERE,
                "Could not pass event "
                    + event.getEventName()
                    + " to "
                    + registration.getPlugin().getDescription().getFullName(),
                ex);
      }
    }
  }
  public void disablePlugin(Plugin plugin) {
    if (!(plugin instanceof JavaPlugin)) {
      throw new IllegalArgumentException("Plugin is not associated with this PluginLoader");
    }

    if (plugin.isEnabled()) {
      JavaPlugin jPlugin = (JavaPlugin) plugin;
      ClassLoader cloader = jPlugin.getClassLoader();

      try {
        jPlugin.setEnabled(false);
      } catch (Throwable ex) {
        server
            .getLogger()
            .log(
                Level.SEVERE,
                "Error occurred while disabling "
                    + plugin.getDescription().getFullName()
                    + " (Is it up to date?): "
                    + ex.getMessage(),
                ex);
      }

      server.getPluginManager().callEvent(new PluginDisableEvent(plugin));

      loaders.remove(jPlugin.getDescription().getName());

      if (cloader instanceof PluginClassLoader) {
        PluginClassLoader loader = (PluginClassLoader) cloader;
        Set<String> names = loader.getClasses();

        for (String name : names) {
          removeClass(name);
        }
      }
    }
  }
  public void disablePlugin(final Plugin plugin) {
    if (plugin.isEnabled()) {
      try {
        plugin.getPluginLoader().disablePlugin(plugin);
      } catch (Throwable ex) {
        server
            .getLogger()
            .log(
                Level.SEVERE,
                "Error occurred (in the plugin loader) while disabling "
                    + plugin.getDescription().getFullName()
                    + " (Is it up to date?)",
                ex);
      }

      try {
        server.getScheduler().cancelTasks(plugin);
      } catch (Throwable ex) {
        server
            .getLogger()
            .log(
                Level.SEVERE,
                "Error occurred (in the plugin loader) while cancelling tasks for "
                    + plugin.getDescription().getFullName()
                    + " (Is it up to date?)",
                ex);
      }

      try {
        server.getServicesManager().unregisterAll(plugin);
      } catch (Throwable ex) {
        server
            .getLogger()
            .log(
                Level.SEVERE,
                "Error occurred (in the plugin loader) while unregistering services for "
                    + plugin.getDescription().getFullName()
                    + " (Is it up to date?)",
                ex);
      }

      try {
        HandlerList.unregisterAll(plugin);
      } catch (Throwable ex) {
        server
            .getLogger()
            .log(
                Level.SEVERE,
                "Error occurred (in the plugin loader) while unregistering events for "
                    + plugin.getDescription().getFullName()
                    + " (Is it up to date?)",
                ex);
      }

      try {
        server.getMessenger().unregisterIncomingPluginChannel(plugin);
        server.getMessenger().unregisterOutgoingPluginChannel(plugin);
      } catch (Throwable ex) {
        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);
      }
    }
  }
  /**
   * Enable all plugins of the given load order type.
   *
   * @param type The type of plugin to enable.
   */
  private void enablePlugins(PluginLoadOrder type) {
    if (type == PluginLoadOrder.STARTUP) {
      helpMap.clear();
      helpMap.initializeGeneralTopics();
    }

    // load all the plugins
    Plugin[] plugins = pluginManager.getPlugins();
    for (Plugin plugin : plugins) {
      if (!plugin.isEnabled() && plugin.getDescription().getLoad() == type) {
        List<Permission> perms = plugin.getDescription().getPermissions();
        for (Permission perm : perms) {
          try {
            pluginManager.addPermission(perm);
          } catch (IllegalArgumentException ex) {
            getLogger()
                .log(
                    Level.WARNING,
                    "Plugin "
                        + plugin.getDescription().getFullName()
                        + " tried to register permission '"
                        + perm.getName()
                        + "' but it's already registered",
                    ex);
          }
        }

        try {
          pluginManager.enablePlugin(plugin);
        } catch (Throwable ex) {
          logger.log(Level.SEVERE, "Error loading " + plugin.getDescription().getFullName(), ex);
        }
      }
    }

    if (type == PluginLoadOrder.POSTWORLD) {
      commandMap.setFallbackCommands();
      commandMap.registerServerAliases();
      DefaultPermissions.registerCorePermissions();
      helpMap.initializeCommands();

      // load permissions.yml
      ConfigurationSection permConfig = config.getConfigFile(ServerConfig.Key.PERMISSIONS_FILE);
      List<Permission> perms =
          Permission.loadPermissions(
              permConfig.getValues(false),
              "Permission node '%s' in permissions config is invalid",
              PermissionDefault.OP);
      for (Permission perm : perms) {
        try {
          pluginManager.addPermission(perm);
        } catch (IllegalArgumentException ex) {
          getLogger()
              .log(
                  Level.WARNING,
                  "Permission config tried to register '"
                      + perm.getName()
                      + "' but it's already registered",
                  ex);
        }
      }
    }
  }