class PluginLoader {

  @SuppressWarnings("unchecked")
  public boolean unload(Plugin plugin) {
    if (!loadedPlugins.contains(plugin)) {
      // It's not loaded, nothing to unload
      return false;
    }
    // Unload any plugins that depends on the one currently being unloaded
    knownPlugins.stream()
        .filter(p -> requires(p.getClass(), plugin.getClass()))
        .forEach(this::unload);

    log.info("Unloading plugin " + plugin.fullIdString());
    components.getActiveWidgets().stream()
        .filter(w -> w.getSources().stream().anyMatch(s -> !(s instanceof DestroyedSource)))
        .filter(w -> {
          return w.getSources().stream().anyMatch(s -> plugin.getDataTypes().contains(s.getDataType()))
              || w.getSources().stream().anyMatch(s -> plugin.getSourceTypes().contains(s.getType()));
        })
        .forEach(w -> {
          if (w instanceof SingleSourceWidget) {
            SingleSourceWidget singleSource = (SingleSourceWidget) w;
            singleSource.setSource(new DestroyedSource<>(singleSource.getSource()));
          } else {
            w.getSources().replaceAll(s -> {
              if (!(s instanceof DestroyedSource)
                  && (plugin.getDataTypes().contains(s.getDataType())
                  || plugin.getSourceTypes().contains(s.getType()))) {
                return new DestroyedSource<>(s);
              }
              return s;
            });
          }
        });
    components.unregisterAll(plugin.getComponents());
    sourceTypes.unregisterAll(plugin.getSourceTypes());
    plugin.getTypeAdapters().forEach(Serializers::remove);
    dataTypes.unregisterAll(plugin.getDataTypes());
    // TODO figure out a good way to remember the theme & reapply it when reloading the plugin
    themes.unregisterAll(plugin.getThemes());
    tabInfoRegistry.unregisterAll(plugin.getDefaultTabInfo());
    converters.unregisterAll(plugin.getRecordingConverters());
    propertyParsers.unregisterAll(plugin.getPropertyParsers());

    plugin.onUnload();
    plugin.setLoaded(false);
    loadedPlugins.remove(plugin);
    return true;
  }

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

}