use of org.spongepowered.plugin.metadata.model.PluginDependency in project SpongeCommon by SpongePowered.
the class DependencyResolver method resolveAndSortCandidates.
public static <T extends PluginResource> ResolutionResult<T> resolveAndSortCandidates(final Collection<PluginCandidate<T>> candidates, final Logger logger) {
final Map<String, Node<T>> nodes = new HashMap<>();
final ResolutionResult<T> resolutionResult = new ResolutionResult<>();
for (final PluginCandidate<T> candidate : candidates) {
final String id = candidate.metadata().id();
// If we already have an entry, this is now a duplicate ID situation.
if (nodes.containsKey(id)) {
resolutionResult.duplicateIds().add(id);
} else {
nodes.put(id, new Node<>(candidate));
}
}
for (final Map.Entry<String, Node<T>> entry : nodes.entrySet()) {
// Attach deps, invalid deps will appear at this point.
final Node<T> node = entry.getValue();
for (final PluginDependency pd : node.candidate.metadata().dependencies()) {
final boolean isOptional = pd.optional();
final Node<T> dep = nodes.get(pd.id());
if (dep == null) {
if (isOptional) {
// just move on to the next dep
continue;
}
node.invalid = true;
final String failure;
if (pd.version() != null) {
failure = String.format("%s version %s", pd.id(), pd.version());
} else {
failure = pd.id();
}
resolutionResult.missingDependencies().computeIfAbsent(entry.getValue().candidate, k -> new ArrayList<>()).add(failure);
// no need to process this further
node.checked = true;
continue;
}
if (!DependencyResolver.checkVersion(pd.version(), dep.candidate.metadata().version())) {
if (isOptional) {
// just move on to the next dep
continue;
}
resolutionResult.versionMismatch().computeIfAbsent(entry.getValue().candidate, k -> new ArrayList<>()).add(Tuple.of(pd.version().toString(), dep.candidate));
node.invalid = true;
// no need to process this further.
node.checked = true;
}
if (pd.loadOrder() == PluginDependency.LoadOrder.BEFORE) {
if (!pd.optional()) {
// Because of what we're about to do, we need to just make sure that
// if the "before" dep fails within here, then we still throw it out.
// Note, we can only do this for sorting, once sorted, if this loads
// but the before dep doesn't, well, it's on the plugin to solve, not
// us.
node.beforeRequiredDependency.add(node);
}
// don't bomb out the dep if this doesn't load - so set it to be optional.
// however, we otherwise treat it as an AFTER dep on the target dep
DependencyResolver.setDependency(dep, node, true);
} else {
DependencyResolver.setDependency(node, dep, pd.optional());
}
}
}
// Check for invalid deps
DependencyResolver.checkCyclic(nodes.values(), resolutionResult);
for (final Node<T> node : nodes.values()) {
DependencyResolver.calculateSecondaryFailures(node, resolutionResult);
}
// Now to sort them.
final List<Node<T>> original = nodes.values().stream().filter(x -> !x.invalid).collect(Collectors.toCollection(ArrayList::new));
final List<Node<T>> toLoad = new ArrayList<>(original);
final LinkedHashSet<Node<T>> sorted = new LinkedHashSet<>();
toLoad.stream().filter(x -> x.dependencies.isEmpty() && x.optionalDependencies.isEmpty()).forEach(sorted::add);
toLoad.removeIf(sorted::contains);
int size = toLoad.size();
boolean excludeOptionals = false;
while (!toLoad.isEmpty()) {
boolean containsOptionalDeps = false;
for (final Node<T> node : toLoad) {
if (sorted.containsAll(node.dependencies) && DependencyResolver.checkOptionalDependencies(excludeOptionals, sorted, node)) {
final boolean hasOptionalDeps = !node.optionalDependencies.isEmpty();
containsOptionalDeps |= hasOptionalDeps;
sorted.add(node);
if (excludeOptionals && hasOptionalDeps) {
logger.warn("Plugin {} will be loaded before its optional dependencies: [ {} ]", node.candidate.metadata().id(), node.optionalDependencies.stream().map(x -> x.candidate.metadata().id()).collect(Collectors.joining(", ")));
}
}
}
toLoad.removeIf(sorted::contains);
if (toLoad.size() == size) {
// without them.
if (excludeOptionals || !containsOptionalDeps) {
// We have a problem
throw new IllegalStateException(String.format("Dependency resolver could not resolve order of all plugins.\n\n" + "Attempted to sort %d plugins: [ %s ]\n" + "Could not sort %d plugins: [ %s ]", original.size(), original.stream().map(x -> x.candidate.metadata().id()).collect(Collectors.joining(", ")), toLoad.size(), toLoad.stream().map(x -> x.candidate.metadata().id()).collect(Collectors.joining(", "))));
}
logger.warn("Failed to resolve plugin load order due to failed dependency resolution, attempting to resolve order ignoring optional" + " dependencies.");
excludeOptionals = true;
} else {
size = toLoad.size();
excludeOptionals = false;
}
}
final Collection<PluginCandidate<T>> sortedSuccesses = resolutionResult.sortedSuccesses();
for (final Node<T> x : sorted) {
sortedSuccesses.add(x.candidate);
}
return resolutionResult;
}
use of org.spongepowered.plugin.metadata.model.PluginDependency in project SpongeCommon by SpongePowered.
the class PluginFileConfigurable method getConfigList.
@Override
public List<? extends IConfigurable> getConfigList(final String... key) {
if (key.length < 1) {
return Collections.emptyList();
}
final String query = key[0];
if (key.length != this.requiredConfigElements(query)) {
return Collections.emptyList();
}
if ("mods".equals(query)) {
final Set<PluginMetadata> metadataById = this.container.metadata();
if (metadataById.isEmpty()) {
return Collections.emptyList();
}
final List<IConfigurable> metadataConfigurables = new ArrayList<>();
metadataById.forEach((metadata) -> metadataConfigurables.add(new PluginMetadataConfigurable(metadata)));
return metadataConfigurables;
}
if (key.length != 2) {
return Collections.emptyList();
}
final String plugin = key[1];
final PluginMetadata metadata = this.container.metadata(plugin).orElse(null);
if (metadata == null) {
return Collections.emptyList();
}
if ("dependencies".equals(query)) {
// TODO Should we inject a dependency on SpongeForge?
final Set<PluginDependency> dependencies = metadata.dependencies();
if (dependencies.isEmpty()) {
return Collections.emptyList();
}
final List<IConfigurable> depConfigurables = new ArrayList<>();
for (final PluginDependency dependency : dependencies) {
depConfigurables.add(new PluginDependencyConfigurable(metadata, dependency));
}
return depConfigurables;
}
return Collections.emptyList();
}
Aggregations