/** * EasyBeans * Copyright (C) 2012 Bull S.A.S. * Contact: easybeans@ow2.org * * 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; either * version 2.1 of the License, or any later version. * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * USA * * -------------------------------------------------------------------------- * $Id$ * -------------------------------------------------------------------------- */ package org.ow2.easybeans.util.topological; import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.Stack; /** * Implementation of the topological sort. * @param <T> the interface extending Node * @author Florent Benoit */ public class TopologicalSort<T extends Node> { /** * Apply a topological sort on the given set of nodes. * @param nodes the nodes to sort * @param <T> the interface extending Node * @return a sorted list of the nodes. */ public static <T extends Node> List<T> sort(final Collection<T> nodes) { TopologicalSort<T> topologicalSort = new TopologicalSort<T>(nodes); topologicalSort.sort(); return topologicalSort.getSortedList(); } /** * Nodes that we will sort. */ private Collection<T> nodes = null; /** * Sorted List. */ private List<T> sortedList = null; /** * Global visited List of nodes. */ private Set<T> visitedNodes = null; /** * Sort has been made. */ private boolean sorted = false; /** * Constructor with the given set of nodes to sort. * @param nodes the list of nodes on which topological sort will be applied */ protected TopologicalSort(final Collection<T> nodes) { this.nodes = nodes; this.sortedList = new ArrayList<T>(); this.visitedNodes = new HashSet<T>(); } /** * Sort the current nodes. */ protected void sort() { if (this.sorted) { return; } for (T node : this.nodes) { // Visit a node and empty stack visit(node, new Stack<T>()); } // It's now sorted this.sorted = true; } /** * @return a sorted list */ protected List<T> getSortedList() { if (!this.sorted) { throw new IllegalStateException("Unable to get a sorted list as sort() has not been called"); } return this.sortedList; } /** * Here is the algorithm for the topological sort. * L <-- Empty list that will contain the sorted nodes <br/> * S <-- Set of all nodes with no outgoing edges<br/> * for each node n in S do<br/> * visit(n) <br/> * <br/> * function visit(node n)<br/> * if n has not been visited yet then<br/> * mark n as visited<br/> * for each node m with an edge from m to n do<br/> * visit(m)<br/> * add n to L<br/> * * * @param analyzingNode node that is being analyzed * @param stackVisitedNodes the local stack of visited nodes to detect cycles */ public void visit(final T analyzingNode, final Stack<T> stackVisitedNodes) { // Cycle detected if (stackVisitedNodes.contains(analyzingNode)) { StringBuilder sb = new StringBuilder(); sb.append("There is a cycle error for the node '"); sb.append(analyzingNode.getName()); sb.append("' and cycle is made between the nodes '"); sb.append(stackVisitedNodes); sb.append("'. Dependencies are : \n"); for (Node stackNode : stackVisitedNodes) { sb.append(" ( "); sb.append(stackNode.getName()); sb.append("--->"); sb.append(stackNode.getDependencies()); sb.append(" )"); } throw new GraphCycleException(sb.toString()); } // Not yet visited, so visit the node if (!this.visitedNodes.contains(analyzingNode)) { this.visitedNodes.add(analyzingNode); for (T node : this.nodes) { if (analyzingNode.getDependencies().contains(node)) { stackVisitedNodes.push(analyzingNode); visit(node, stackVisitedNodes); stackVisitedNodes.pop(); } } // Add element to the sorted list this.sortedList.add(analyzingNode); } } }