| /* |
| * |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| * |
| */ |
| |
| package org.apache.royale.compiler.internal.graph; |
| |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Collection; |
| import java.util.Comparator; |
| import java.util.HashSet; |
| import java.util.LinkedHashSet; |
| import java.util.List; |
| import java.util.Set; |
| |
| import org.apache.royale.compiler.exceptions.CircularDependencyException; |
| |
| /** |
| * Class with static methods for topologically sorting nodes in an |
| * {@link IGraph}. |
| */ |
| public final class TopologicalSort |
| { |
| /** |
| * IVisitor interface implemented by callers of the |
| * {@link TopologicalSort#sort} method. |
| * |
| * @param <V> Vertex type parameter of {@link IGraph} |
| * @param <E> Edge type parameter of {@link IGraph} |
| */ |
| public interface IVisitor<V, E extends IGraphEdge<V>> extends Comparator<V> |
| { |
| /** |
| * Called by the |
| * {@link TopologicalSort#sort(IGraph, Collection, IVisitor)} |
| * method for each vertex in topological order. |
| * |
| * @param v The vertex that is currently being visited. |
| */ |
| public void visit(V v); |
| |
| /** |
| * Called by the |
| * {@link TopologicalSort#sort(IGraph, Collection, IVisitor)} |
| * to determine if an edge is a topological edge. |
| * |
| * @param e An edge in the {@link IGraph}. |
| * @return true, if the specified edge is a topological edge, otherwise |
| * false. |
| */ |
| public boolean isTopologicalEdge(E e); |
| } |
| |
| @SuppressWarnings("unchecked") |
| private static <V, E extends IGraphEdge<V>> void depthFirstTraverse(IGraph<V, E> graph, |
| Collection<V> vertices, |
| IVisitor<V, E> visitor, |
| Set<V> visitedSet, |
| Set<V> referencedNonTopologicalVertices, |
| Set<V> currentStackVisitedNodes) |
| throws CircularDependencyException |
| { |
| // Sorting vertices so that we can get stable results across different runs |
| // of this algorithm. |
| Object[] referencedVerticesArr = vertices.toArray(); |
| Arrays.sort(referencedVerticesArr, (Comparator<Object>)visitor); |
| for (Object referencedVertexObj : referencedVerticesArr) |
| { |
| V referencedVertex = (V)referencedVertexObj; |
| currentStackVisitedNodes.add(referencedVertex); |
| depthFirstTraverse(graph, referencedVertex, visitor, visitedSet, referencedNonTopologicalVertices, |
| currentStackVisitedNodes); |
| currentStackVisitedNodes.remove(referencedVertex); |
| } |
| } |
| |
| private static <V, E extends IGraphEdge<V>> void depthFirstTraverse(IGraph<V, E> graph, |
| V v, |
| IVisitor<V, E> visitor, |
| Set<V> visitedSet, |
| Set<V> referencedNonTopologicalVertices, |
| Set<V> currentStackVisitedNodes) |
| throws CircularDependencyException |
| { |
| if (!visitedSet.add(v)) |
| return; |
| |
| Set<E> outgoingEdges = graph.getOutgoingEdges(v); |
| ArrayList<V> referencedTopologicalVertices = new ArrayList<V>(outgoingEdges.size()); |
| for (E e : outgoingEdges) |
| { |
| V referencedVertex = e.getTo(); |
| if (visitor.isTopologicalEdge(e)) |
| { |
| if (currentStackVisitedNodes.contains(referencedVertex)) |
| { |
| // Turn the set into a list so we can append the repeating node. |
| List<V> orderedNodes = new ArrayList<V>(currentStackVisitedNodes.size() + 1); |
| orderedNodes.addAll(currentStackVisitedNodes); |
| orderedNodes.add(referencedVertex); |
| throw new CircularDependencyException(orderedNodes); |
| } |
| |
| referencedTopologicalVertices.add(referencedVertex); |
| } |
| else |
| { |
| referencedNonTopologicalVertices.add(referencedVertex); |
| } |
| } |
| |
| // First sort and visit all the vertices pointed at by topological edges |
| depthFirstTraverse(graph, referencedTopologicalVertices, visitor, visitedSet, referencedNonTopologicalVertices, |
| currentStackVisitedNodes); |
| |
| // Second visit the current vertex. |
| visitor.visit(v); |
| |
| referencedNonTopologicalVertices.remove(v); |
| } |
| |
| /** |
| * Topologically sorts nodes in an {@link IGraph}. This method assumes the |
| * elimination of all non-topological edges in the graph would make the |
| * graph a directed acyclic graph ( DAG ). Topological edges are those edges |
| * for which the isTopologicalEdge method of the specified visitor returned |
| * true. This method will call the visit method of the specified visitor for |
| * each vertex, v, after all vertices pointed at by v with topological edges |
| * have been visited. After a vertex, v, has been visited any other vertices |
| * pointed at by v with non-topological edges are visited. The specified |
| * comparator is used to ensure the order in which vertices are visited is |
| * stable for a given topology. |
| * <p> |
| * The ordering generated by this method will be stable if the ordering of |
| * all vertices in the graph using the specified comparator is stable. |
| * <p> |
| * Callers should detect and eliminate cycles in the graph of vertices |
| * connected by topological edges before calling this method. If there are |
| * cycles in the graph of vertices connected by topological edges, this |
| * method will produce a potentially invalid ordering. Cycles created by |
| * edges that are not topological edges are harmless. |
| * |
| * @param <V> Vertex type parameter of {@link IGraph} |
| * @param <E> Edge type parameter of {@link IGraph} |
| * @param graph {@link IGraph} whose vertexes will be topologically sorted. |
| * @param rootedVertices Collection of vertices and whose topologically |
| * connected vertices should be visited. |
| * @param visitor IVisitor whose visit method is called for each vertex in |
| * topological ordering. |
| */ |
| public static <V, E extends IGraphEdge<V>> void sort(IGraph<V, E> graph, |
| Collection<V> rootedVertices, |
| IVisitor<V, E> visitor) |
| throws CircularDependencyException |
| { |
| Collection<V> verticesToVisit = rootedVertices; |
| int totalNumVertices = graph.getVertices().size(); |
| Set<V> visitedSet = new HashSet<V>(totalNumVertices); |
| Set<V> referencedNonTopologicalVertices = new HashSet<V>(totalNumVertices); |
| Set<V> currentStackVisitedNodes = new LinkedHashSet<V>(); |
| do |
| { |
| depthFirstTraverse(graph, verticesToVisit, visitor, visitedSet, referencedNonTopologicalVertices, |
| currentStackVisitedNodes); |
| verticesToVisit = referencedNonTopologicalVertices; |
| referencedNonTopologicalVertices = new HashSet<V>(); |
| } |
| while (verticesToVisit.size() > 0); |
| } |
| |
| /** |
| * Convenience wrapper for |
| * {@link #sort(IGraph, Collection, IVisitor)} that |
| * topologically sorts all vertices in a graph and returns a list of all the |
| * vertices in topological order. |
| * |
| * @param <V> Vertex type parameter of {@link IGraph} |
| * @param <E> Edge type parameter of {@link IGraph} |
| * @param graph {@link IGraph} whose vertexes will be topologically sorted. |
| * @param vertexComparator Comparator to compare two vertexes, such that the |
| * topological order will be stable. |
| * @return List of all vertices in the graph in topological order. |
| */ |
| public static <V, E extends IGraphEdge<V>> List<V> sort(final IGraph<V, E> graph, final Comparator<V> vertexComparator) |
| throws CircularDependencyException |
| { |
| final ArrayList<V> result = new ArrayList<V>(graph.getVertices().size()); |
| sort(graph, graph.getVertices(), new IVisitor<V, E>() |
| { |
| @Override |
| public void visit(V v) |
| { |
| result.add(v); |
| } |
| |
| @Override |
| public boolean isTopologicalEdge(E e) |
| { |
| return true; |
| } |
| |
| @Override |
| public int compare(V a, V b) |
| { |
| return vertexComparator.compare(a, b); |
| } |
| }); |
| return result; |
| } |
| } |