ForgePatch/src/main/java/net/minecraftforge/fml/common/toposort/TopologicalSort.java

209 lines
6.0 KiB
Java
Raw Normal View History

2012-04-03 16:03:21 +00:00
/*
* Minecraft Forge
* Copyright (c) 2016-2018.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation version 2.1
* of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
2012-04-03 16:03:21 +00:00
*/
2014-09-23 05:01:24 +00:00
package net.minecraftforge.fml.common.toposort;
2012-04-03 16:03:21 +00:00
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
2012-04-03 16:03:21 +00:00
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
2012-04-03 16:03:21 +00:00
2014-09-23 05:01:24 +00:00
import net.minecraftforge.fml.common.FMLLog;
import com.google.common.collect.Sets;
import com.google.common.collect.Sets.SetView;
2012-04-03 16:03:21 +00:00
/**
* Topological sort for mod loading
*
2012-04-03 16:03:21 +00:00
* Based on a variety of sources, including http://keithschwarz.com/interesting/code/?dir=topological-sort
* @author cpw
*
*/
public class TopologicalSort
{
public static class DirectedGraph<T> implements Iterable<T>
{
private final Map<T, SortedSet<T>> graph = new HashMap<T, SortedSet<T>>();
private List<T> orderedNodes = new ArrayList<T>();
public boolean addNode(T node)
{
// Ignore nodes already added
if (graph.containsKey(node))
{
return false;
}
orderedNodes.add(node);
graph.put(node, new TreeSet<T>(Comparator.comparingInt(o -> orderedNodes.indexOf(o))));
return true;
}
public void addEdge(T from, T to)
{
if (!(graph.containsKey(from) && graph.containsKey(to)))
{
throw new NoSuchElementException("Missing nodes from graph");
}
graph.get(from).add(to);
}
public void removeEdge(T from, T to)
{
if (!(graph.containsKey(from) && graph.containsKey(to)))
{
throw new NoSuchElementException("Missing nodes from graph");
}
graph.get(from).remove(to);
}
public boolean edgeExists(T from, T to)
{
if (!(graph.containsKey(from) && graph.containsKey(to)))
{
throw new NoSuchElementException("Missing nodes from graph");
}
return graph.get(from).contains(to);
}
public Set<T> edgesFrom(T from)
{
if (!graph.containsKey(from))
{
throw new NoSuchElementException("Missing node from graph");
}
return Collections.unmodifiableSortedSet(graph.get(from));
}
@Override
public Iterator<T> iterator()
{
return orderedNodes.iterator();
}
public int size()
{
return graph.size();
}
public boolean isEmpty()
{
return graph.isEmpty();
}
@Override
public String toString()
{
return graph.toString();
}
2012-04-03 16:03:21 +00:00
}
/**
* Sort the input graph into a topologically sorted list
*
* Uses the reverse depth first search as outlined in ...
* @param graph
* @return The sorted mods list.
*/
public static <T> List<T> topologicalSort(DirectedGraph<T> graph)
{
DirectedGraph<T> rGraph = reverse(graph);
List<T> sortedResult = new ArrayList<T>();
Set<T> visitedNodes = new HashSet<T>();
// A list of "fully explored" nodes. Leftovers in here indicate cycles in the graph
Set<T> expandedNodes = new HashSet<T>();
for (T node : rGraph)
{
explore(node, rGraph, sortedResult, visitedNodes, expandedNodes);
}
return sortedResult;
2012-04-03 16:03:21 +00:00
}
public static <T> DirectedGraph<T> reverse(DirectedGraph<T> graph)
{
DirectedGraph<T> result = new DirectedGraph<T>();
for (T node : graph)
{
result.addNode(node);
}
for (T from : graph)
{
for (T to : graph.edgesFrom(from))
{
result.addEdge(to, from);
}
}
return result;
2012-04-03 16:03:21 +00:00
}
public static <T> void explore(T node, DirectedGraph<T> graph, List<T> sortedResult, Set<T> visitedNodes, Set<T> expandedNodes)
{
// Have we been here before?
if (visitedNodes.contains(node))
{
// And have completed this node before
if (expandedNodes.contains(node))
{
// Then we're fine
return;
}
FMLLog.log.fatal("Mod Sorting failed.");
FMLLog.log.fatal("Visiting node {}", node);
FMLLog.log.fatal("Current sorted list : {}", sortedResult);
FMLLog.log.fatal("Visited set for this node : {}", visitedNodes);
FMLLog.log.fatal("Explored node set : {}", expandedNodes);
SetView<T> cycleList = Sets.difference(visitedNodes, expandedNodes);
FMLLog.log.fatal("Likely cycle is in : {}", cycleList);
throw new ModSortingException("There was a cycle detected in the input graph, sorting is not possible", node, cycleList);
}
// Visit this node
visitedNodes.add(node);
// Recursively explore inbound edges
for (T inbound : graph.edgesFrom(node))
{
explore(inbound, graph, sortedResult, visitedNodes, expandedNodes);
}
// Add ourselves now
sortedResult.add(node);
// And mark ourselves as explored
expandedNodes.add(node);
2012-04-03 16:03:21 +00:00
}
}