All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.mahanaroad.toposort.GraphNodeSorter Maven / Gradle / Ivy

There is a newer version: 1.0.1
Show newest version
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);
            }

        }

    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy