@Nonnull
	Collection<PluginContainer<?>> apply(@Nonnull List<PreparedPlugin> preparedPlugins) throws PluginException {
		Map<String, LoadedPlugin> temp = LinkedHashMap.newLinkedHashMap(preparedPlugins.size());
		var plugins = this.plugins;
		for (var preparedPlugin : preparedPlugins) {
			String id = preparedPlugin.info().id();
			if (plugins.containsKey(id)) {
				throw new PluginException("Plugin %s is already loaded".formatted(id));
			}
			var threadContextClassLoader = Thread.currentThread().getContextClassLoader();
			var parentLoader = threadContextClassLoader != null ? threadContextClassLoader : ClassLoader.getSystemClassLoader();
			var classLoader = new PluginClassLoaderImpl(parentLoader, this, preparedPlugin.pluginSource(), id);
			LoadedPlugin loadedPlugin = new LoadedPlugin(new PluginContainerImpl<>(preparedPlugin, classLoader));

			if (temp.putIfAbsent(id, loadedPlugin) != null) {
				throw new PluginException("Duplicate plugin %s".formatted(id));
			}
		}
		for (LoadedPlugin plugin : temp.values()) {
			PluginInfo info = plugin.getContainer().info();
			for (String dependencyId : info.dependencies()) {
				LoadedPlugin dep = temp.get(dependencyId);
				if (dep == null) {
					dep = plugins.get(dependencyId);
				}
				if (dep == null) {
					throw new PluginException("Plugin %s is missing dependency %s".formatted(info.id(), dependencyId));
				}
				plugin.getDependencies().add(dep);
			}
			for (String dependencyId : info.softDependencies()) {
				LoadedPlugin dep = temp.get(dependencyId);
				if (dep == null && (dep = plugins.get(dependencyId)) == null) {
					continue;
				}
				plugin.getDependencies().add(dep);
			}
		}
		for (LoadedPlugin loadedPlugin : temp.values()) {
			try {
				enable(loadedPlugin);
			} catch (PluginException ex) {
				for (LoadedPlugin pl : temp.values()) {
					PluginContainerImpl<?> container = pl.getContainer();
					try {
						try {
							Plugin maybeEnabled = container.plugin;
							if (maybeEnabled != null) {
								maybeEnabled.onDisable();
							}
						} finally {
							container.preparedPlugin.reject();
						}
					} catch (Exception ex1) {
						ex.addSuppressed(ex1);
					}
				}
				throw ex;
			}
		}
		plugins.putAll(temp);
		return Collections2.transform(temp.values(), input -> input.getContainer());
	}
Primary plugin load action.

@param preparedPlugins
		Intermediate plugin data to load.

@return Collection of loaded plugin containers.

@throws PluginException
		If the plugins could not be loaded for any reason.
recaf-core/src/main/java/software/coley/recaf/services/plugin/PluginGraph.java