geforkt von Mirrors/Velocity
Clean up and comment PluginDependencyUtils#sortCandidates
Dieser Commit ist enthalten in:
Ursprung
cb8781b3c9
Commit
895eb1a424
@ -30,6 +30,7 @@ import java.util.Deque;
|
|||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
public class PluginDependencyUtils {
|
public class PluginDependencyUtils {
|
||||||
|
|
||||||
@ -38,7 +39,7 @@ public class PluginDependencyUtils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Attempts to topographically sort all plugins for the proxy to load by dependencies using
|
* Attempts to topographically sort all plugins for the proxy to load in dependency order using
|
||||||
* a depth-first search.
|
* a depth-first search.
|
||||||
*
|
*
|
||||||
* @param candidates the plugins to sort
|
* @param candidates the plugins to sort
|
||||||
@ -71,7 +72,10 @@ public class PluginDependencyUtils {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now we do the depth-first search.
|
// Now we do the depth-first search. The most accessible description of the algorithm is on
|
||||||
|
// Wikipedia: https://en.wikipedia.org/w/index.php?title=Topological_sorting&oldid=1036420482,
|
||||||
|
// section "Depth-first search." Apparently this algorithm originates from "Introduction to
|
||||||
|
// Algorithms" (2nd ed.)
|
||||||
List<PluginDescription> sorted = new ArrayList<>();
|
List<PluginDescription> sorted = new ArrayList<>();
|
||||||
Map<PluginDescription, Mark> marks = new HashMap<>();
|
Map<PluginDescription, Mark> marks = new HashMap<>();
|
||||||
|
|
||||||
@ -82,38 +86,41 @@ public class PluginDependencyUtils {
|
|||||||
return sorted;
|
return sorted;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void visitNode(Graph<PluginDescription> dependencyGraph, PluginDescription node,
|
private static void visitNode(Graph<PluginDescription> dependencyGraph, PluginDescription current,
|
||||||
Map<PluginDescription, Mark> marks, List<PluginDescription> sorted,
|
Map<PluginDescription, Mark> visited, List<PluginDescription> sorted,
|
||||||
Deque<PluginDescription> currentIteration) {
|
Deque<PluginDescription> currentDependencyScanStack) {
|
||||||
Mark mark = marks.getOrDefault(node, Mark.NOT_VISITED);
|
Mark mark = visited.getOrDefault(current, Mark.NOT_VISITED);
|
||||||
if (mark == Mark.PERMANENT) {
|
if (mark == Mark.VISITED) {
|
||||||
|
// Visited this node already, nothing to do.
|
||||||
return;
|
return;
|
||||||
} else if (mark == Mark.TEMPORARY) {
|
} else if (mark == Mark.VISITING) {
|
||||||
// A circular dependency has been detected.
|
// A circular dependency has been detected. (Specifically, if we are visiting any dependency
|
||||||
currentIteration.addLast(node);
|
// and a dependency we are looking at depends on any dependency being visited, we have a
|
||||||
StringBuilder loopGraph = new StringBuilder();
|
// circular dependency, thus we do not have a directed acyclic graph and therefore no
|
||||||
for (PluginDescription description : currentIteration) {
|
// topological sort is possible.)
|
||||||
loopGraph.append(description.getId());
|
currentDependencyScanStack.addLast(current);
|
||||||
loopGraph.append(" -> ");
|
final String loop = currentDependencyScanStack.stream().map(PluginDescription::getId)
|
||||||
}
|
.collect(Collectors.joining(" -> "));
|
||||||
loopGraph.setLength(loopGraph.length() - 4);
|
throw new IllegalStateException("Circular dependency detected: " + loop);
|
||||||
throw new IllegalStateException("Circular dependency detected: " + loopGraph.toString());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
currentIteration.addLast(node);
|
// Visiting this node. Mark this node as having a visit in progress and scan its edges.
|
||||||
marks.put(node, Mark.TEMPORARY);
|
currentDependencyScanStack.addLast(current);
|
||||||
for (PluginDescription edge : dependencyGraph.successors(node)) {
|
visited.put(current, Mark.VISITING);
|
||||||
visitNode(dependencyGraph, edge, marks, sorted, currentIteration);
|
for (PluginDescription edge : dependencyGraph.successors(current)) {
|
||||||
|
visitNode(dependencyGraph, edge, visited, sorted, currentDependencyScanStack);
|
||||||
}
|
}
|
||||||
|
|
||||||
marks.put(node, Mark.PERMANENT);
|
// All other dependency nodes were visited. We are clear to mark as visited and add to the
|
||||||
currentIteration.removeLast();
|
// sorted list.
|
||||||
sorted.add(node);
|
visited.put(current, Mark.VISITED);
|
||||||
|
currentDependencyScanStack.removeLast();
|
||||||
|
sorted.add(current);
|
||||||
}
|
}
|
||||||
|
|
||||||
private enum Mark {
|
private enum Mark {
|
||||||
NOT_VISITED,
|
NOT_VISITED,
|
||||||
TEMPORARY,
|
VISITING,
|
||||||
PERMANENT
|
VISITED
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Laden…
In neuem Issue referenzieren
Einen Benutzer sperren