/* * * SchemaCrawler * http://sourceforge.net/projects/schemacrawler * Copyright (c) 2000-2010, Sualeh Fatehi. * * 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 (at your option) 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. * */ package sf.util; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; public class DirectedGraph<T extends Comparable<? super T>> { /** * Traversal state when detecting cycle. */ private enum TraversalState { notStarted, inProgress, complete; } /** * Directed edge in a graph. * * @param <T> * Type of node object */ class DirectedEdge<T extends Comparable<? super T>> { private final Vertex<T> from; private final Vertex<T> to; private TraversalState traversalState = TraversalState.notStarted; DirectedEdge(final Vertex<T> from, final Vertex<T> to) { this.from = from; this.to = to; } @Override public boolean equals(final Object obj) { if (this == obj) { return true; } if (obj == null) { return false; } if (!(obj instanceof DirectedEdge)) { return false; } final DirectedEdge<T> other = (DirectedEdge<T>) obj; if (!getOuterType().equals(other.getOuterType())) { return false; } if (from == null) { if (other.from != null) { return false; } } else if (!from.equals(other.from)) { return false; } if (to == null) { if (other.to != null) { return false; } } else if (!to.equals(other.to)) { return false; } return true; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + getOuterType().hashCode(); result = prime * result + (from == null? 0: from.hashCode()); result = prime * result + (to == null? 0: to.hashCode()); return result; } @Override public String toString() { return "(" + from + " --> " + to + ")"; } private DirectedGraph<?> getOuterType() { return DirectedGraph.this; } Vertex<T> getFrom() { return from; } Vertex<T> getTo() { return to; } TraversalState getTraversalState() { return traversalState; } boolean isFrom(final Vertex<T> vertex) { return vertex != null && vertex.equals(from); } boolean isTo(final Vertex<T> vertex) { return vertex != null && vertex.equals(to); } void setTraversalState(final TraversalState traversalState) { this.traversalState = traversalState; } } /** * Vertex in a graph. * * @param <T> * Type of node object */ class Vertex<T extends Comparable<? super T>> implements Comparable<Vertex<T>> { private final T value; private TraversalState traversalState = TraversalState.notStarted; Vertex(final T value) { this.value = value; } public int compareTo(final Vertex<T> otherVertex) { if (value == null) { return -1; } return value.compareTo(otherVertex.getValue()); } @Override public boolean equals(final Object obj) { if (this == obj) { return true; } if (obj == null) { return false; } if (!(obj instanceof Vertex)) { return false; } final Vertex<T> other = (Vertex<T>) obj; if (!getOuterType().equals(other.getOuterType())) { return false; } if (value == null) { if (other.value != null) { return false; } } else if (!value.equals(other.value)) { return false; } return true; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + getOuterType().hashCode(); result = prime * result + (value == null? 0: value.hashCode()); return result; } @Override public String toString() { return value.toString(); } private DirectedGraph getOuterType() { return DirectedGraph.this; } TraversalState getTraversalState() { return traversalState; } T getValue() { return value; } void setTraversalState(final TraversalState traversalState) { this.traversalState = traversalState; } } private final Map<T, Vertex<T>> verticesMap; private final Set<DirectedEdge<T>> edges; public DirectedGraph() { verticesMap = new HashMap<T, Vertex<T>>(); edges = new HashSet<DirectedEdge<T>>(); } /** * Adds vertices, and a directed edge between them. * * @param from * Vertex value at the start of the edge * @param to * Vertex value at the end of the edge */ public void addDirectedEdge(final T from, final T to) { edges.add(new DirectedEdge<T>(addVertex(from), addVertex(to))); } /** * Checks if the graph contains a cycle. * * @return true if the graph contains a cycle, false otherwise */ public boolean containsCycle() { final Collection<Vertex<T>> vertices = verticesMap.values(); for (final Vertex<T> vertex: vertices) { vertex.setTraversalState(TraversalState.notStarted); } for (final Vertex<T> vertex: vertices) { if (vertex.getTraversalState() == TraversalState.notStarted) { if (visit(vertex)) { return true; } } } return false; } public List<T> topologicalSort() throws GraphException { if (containsCycle()) { throw new GraphException("Graph contains a cycle, so cannot be topologically sorted"); } final int collectionSize = verticesMap.size(); final Collection<Vertex<T>> vertices = new HashSet<Vertex<T>>(verticesMap .values()); final Set<DirectedEdge<T>> edges = new HashSet<DirectedEdge<T>>(this.edges); final List<T> sortedValues = new ArrayList<T>(collectionSize); while (!vertices.isEmpty()) { final List<Vertex<T>> startNodes = new ArrayList<Vertex<T>>(collectionSize); final List<T> unattachedNodeValues = new ArrayList<T>(collectionSize); for (final Iterator<Vertex<T>> iterator = vertices.iterator(); iterator .hasNext();) { final Vertex<T> vertex = iterator.next(); if (isUnattachedNode(vertex, edges)) { unattachedNodeValues.add(vertex.getValue()); iterator.remove(); } } Collections.sort(unattachedNodeValues); sortedValues.addAll(unattachedNodeValues); for (final Vertex<T> vertex: vertices) { if (isStartNode(vertex, edges)) { startNodes.add(vertex); } } Collections.sort(startNodes); for (final Vertex<T> vertex: startNodes) { // Save the vertex value sortedValues.add(vertex.getValue()); // Remove all out edges dropOutEdges(vertex, edges); // Remove the vertex itself vertices.remove(vertex); } } return sortedValues; } private void dropOutEdges(final Vertex<T> vertex, final Set<DirectedEdge<T>> edges) { for (final Iterator<DirectedEdge<T>> iterator = edges.iterator(); iterator .hasNext();) { final DirectedEdge<T> edge = iterator.next(); if (edge.isFrom(vertex)) { iterator.remove(); } } } private boolean isStartNode(final Vertex<T> vertex, final Set<DirectedEdge<T>> edges) { for (final DirectedEdge<T> edge: edges) { if (edge.isTo(vertex)) { return false; } } return true; } private boolean isUnattachedNode(final Vertex<T> vertex, final Set<DirectedEdge<T>> edges) { for (final DirectedEdge<T> edge: edges) { if (edge.isTo(vertex) || edge.isFrom(vertex)) { return false; } } return true; } private boolean visit(final Vertex<T> vertex) { vertex.setTraversalState(TraversalState.inProgress); for (final DirectedEdge<T> edge: edges) { final Vertex<T> to = edge.getTo(); if (edge.isFrom(vertex)) { if (to.getTraversalState() == TraversalState.inProgress) { return true; } else if (to.getTraversalState() == TraversalState.notStarted) { if (visit(edge.getTo())) { return true; } } } } vertex.setTraversalState(TraversalState.complete); return false; } /** * Adds a vertex. * * @param value * Vertex value * @return The newly added vertex */ protected Vertex<T> addVertex(final T value) { final Vertex<T> vertex; if (verticesMap.containsKey(value)) { vertex = verticesMap.get(value); } else { vertex = new Vertex<T>(value); verticesMap.put(value, vertex); } return vertex; } }