com.mahanaroad.toposort.GraphNodeSorter Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of mahana-topo-sort Show documentation
Show all versions of mahana-topo-sort Show documentation
A Java library for performing topological sorting.
package com.mahanaroad.toposort;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
/**
* A utility class to sort a Directed Acyclic Graph (DAG) of graph nodes in
* dependency order. After sorting, each node in the list will have no
* dependencies on any of the nodes that come after it in the list. This type
* of sort is called a topological sort.
*
* Instances of this class are not reusable.
*
*/
public class GraphNodeSorter {
private final Set> graphNodes = new HashSet<>();
private final List> sortedGraphNodes = new LinkedList<>();
public GraphNodeSorter() {
// do nothing
}
/**
* @param graphNode Must not be null.
* @throws IllegalArgumentException if any argument is null.
*/
public void addNode(GraphNode graphNode) {
if (graphNode == null) {
throw new IllegalArgumentException("graphNode must not be null");
}
this.graphNodes.add(graphNode);
}
/**
* Returns a list of nodes where each node in the list has no dependency
* on any subsequent node.
*
* @return Never null.
* @throws MissingNodeException
* @throws CyclicDependencyException
*/
public List> topologicalSort() {
checkForMissingNodes();
sortNodes();
return this.sortedGraphNodes;
}
private void checkForMissingNodes() {
Set preRequisiteNodes = getMissingPreRequisiteNodes();
if (!preRequisiteNodes.isEmpty()) {
throw new MissingNodeException(preRequisiteNodes);
}
}
private void sortNodes() {
while (this.graphNodes.size() > 0) {
GraphNode currentNode = findNodeWithNoDependencies();
this.sortedGraphNodes.add(currentNode);
deleteNode(currentNode);
}
}
private Set getMissingPreRequisiteNodes() {
Set nodeIdSet = new HashSet<>();
Set preRequisiteNodeIdSet = new HashSet<>();
for (GraphNode graphNode : this.graphNodes) {
nodeIdSet.add(graphNode.getNodeName());
preRequisiteNodeIdSet.addAll(graphNode.getDependsOnNodes());
}
preRequisiteNodeIdSet.removeAll(nodeIdSet);
return preRequisiteNodeIdSet;
}
private GraphNode findNodeWithNoDependencies() {
for (GraphNode graphNode : this.graphNodes) {
if (graphNode.hasNoDependencies()) {
return graphNode;
}
}
String message = "Nodes = " + this.graphNodes;
throw new CyclicDependencyException(message);
}
private void deleteNode(GraphNode nodeToBeDeleted) {
Iterator> nodesItr = this.graphNodes.iterator();
while (nodesItr.hasNext()) {
GraphNode graphNode = nodesItr.next();
if (graphNode.equals(nodeToBeDeleted)) {
nodesItr.remove();
} else {
graphNode.removeDependencies(nodeToBeDeleted);
}
}
}
}