class PluginLoader {

  public boolean load(Plugin plugin) {
    if (loadedPlugins.contains(plugin)) {
      // Already loaded
      return false;
    }
    final Stopwatch timer = Stopwatch.createStarted(); // NOPMD -- need to time as much as posible

    // Unload an already-loaded version of this plugin, if it exists
    // This allows us to load
    unloadOldVersion(plugin);
    knownPluginClasses.add(plugin.getClass());

    if (!canLoad(plugin)) {
      log.warning("Not all requirements are present for " + plugin.fullIdString());
      if (!knownPlugins.contains(plugin)) {
        knownPlugins.add(plugin);
      }
      return false;
    }
    log.info("Loading plugin " + plugin.fullIdString());
    dataTypes.registerAll(plugin.getDataTypes());
    sourceTypes.registerAll(plugin.getSourceTypes());
    plugin.getTypeAdapters().forEach(Serializers::add);
    components.registerAll(plugin.getComponents());
    plugin.getDefaultComponents().forEach(components::setDefaultComponent);
    components.getActiveWidgets().stream()
        .filter(w -> w.getSources().stream().anyMatch(s -> s instanceof DestroyedSource))
        .filter(w -> {
          return plugin.getComponents().stream().anyMatch(t -> t.getType().equals(w.getClass()))
              || w.getSources().stream().anyMatch(s -> plugin.getDataTypes().contains(s.getDataType()))
              || w.getSources().stream().anyMatch(s -> plugin.getSourceTypes().contains(s.getType()));
        })
        .filter(w -> w.getSources().stream().anyMatch(s -> sourceTypes.isRegistered(s.getType())))
        .forEach(w -> {
          w.getSources().stream()
              .filter(s -> s instanceof DestroyedSource)
              .forEach(s -> tryRestoreSource(w, (DestroyedSource) s));
        });
    plugin.getThemes().forEach(themes::register);
    tabInfoRegistry.registerAll(plugin.getDefaultTabInfo());
    converters.registerAll(plugin.getRecordingConverters());
    propertyParsers.registerAll(plugin.getPropertyParsers());

    plugin.onLoad();
    plugin.setLoaded(true);

    loadedPlugins.add(plugin);
    if (!knownPlugins.contains(plugin)) {
      knownPlugins.add(plugin);
    }
    log.info("Loaded plugin " + plugin.idString() + " in " + timer.elapsed(TimeUnit.MILLISECONDS) + "ms");
    return true;
  }

  public  PluginLoader(DataTypes dataTypes,
                      SourceTypes sourceTypes,
                      Components components,
                      Themes themes,
                      TabInfoRegistry tabInfoRegistry,
                      Converters converters,
                      PropertyParsers propertyParsers);

}