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

sf.util.DirectedGraph Maven / Gradle / Ivy

Go to download

SchemaCrawler is an open-source Java API that makes working with database metadata as easy as working with plain old Java objects. SchemaCrawler is also a database schema discovery and comprehension, and schema documentation tool. You can search for database schema objects using regular expressions, and output the schema and data in a readable text format. The output is designed to be diff-ed against other database schemas.

There is a newer version: 16.22.2
Show newest version
/*
 *
 * SchemaCrawler
 * http://sourceforge.net/projects/schemacrawler
 * Copyright (c) 2000-2014, 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>
{

  /**
   * Directed edge in a graph.
   */
  public class DirectedEdge
  {

    private final Vertex from;
    private final Vertex to;

    DirectedEdge(final Vertex from, final Vertex to)
    {
      this.from = from;
      this.to = to;
    }

    @Override
    public boolean equals(final Object obj)
    {
      if (this == obj)
      {
        return true;
      }
      if (obj == null)
      {
        return false;
      }
      final DirectedEdge other = (DirectedEdge) obj;
      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;
    }

    public Vertex getFrom()
    {
      return from;
    }

    public Vertex getTo()
    {
      return to;
    }

    @Override
    public int hashCode()
    {
      final int prime = 31;
      int result = 1;
      result = prime * result + (from == null? 0: from.hashCode());
      result = prime * result + (to == null? 0: to.hashCode());
      return result;
    }

    public boolean isFrom(final Vertex vertex)
    {
      return vertex != null && vertex.equals(from);
    }

    public boolean isTo(final Vertex vertex)
    {
      return vertex != null && vertex.equals(to);
    }

    @Override
    public String toString()
    {
      return "(" + from + " --> " + to + ")";
    }

  }

  /**
   * Vertex in a graph.
   */
  public class Vertex
  {

    private final T value;
    private TraversalState traversalState = TraversalState.notStarted;

    Vertex(final T value)
    {
      this.value = value;
    }

    @Override
    public boolean equals(final Object obj)
    {
      if (this == obj)
      {
        return true;
      }
      if (obj == null)
      {
        return false;
      }
      final Vertex other = (Vertex) obj;
      if (value == null)
      {
        if (other.value != null)
        {
          return false;
        }
      }
      else if (!value.equals(other.value))
      {
        return false;
      }
      return true;
    }

    public T getValue()
    {
      return value;
    }

    @Override
    public int hashCode()
    {
      final int prime = 31;
      int result = 1;
      result = prime * result + (value == null? 0: value.hashCode());
      return result;
    }

    @Override
    public String toString()
    {
      return value.toString();
    }

    TraversalState getTraversalState()
    {
      return traversalState;
    }

    void setTraversalState(final TraversalState traversalState)
    {
      this.traversalState = traversalState;
    }

  }

  /**
   * Traversal state when detecting cycle.
   */
  private enum TraversalState
  {

    notStarted,
    inProgress,
    complete;
  }

  private final Map verticesMap;
  private final Set edges;

  public DirectedGraph()
  {
    verticesMap = new HashMap<>();
    edges = new HashSet<>();
  }

  /**
   * 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)
  {
    if (!from.equals(to))
    {
      edges.add(new DirectedEdge(addVertex(from), addVertex(to)));
    }
  }

  /**
   * Adds a vertex.
   *
   * @param value
   *        Vertex value
   * @return The newly added vertex
   */
  public Vertex addVertex(final T value)
  {
    final Vertex vertex;
    if (verticesMap.containsKey(value))
    {
      vertex = verticesMap.get(value);
    }
    else
    {
      vertex = new Vertex(value);
      verticesMap.put(value, vertex);
    }
    return vertex;
  }

  /**
   * Checks if the graph contains a cycle.
   *
   * @return true if the graph contains a cycle, false otherwise
   */
  public boolean containsCycle()
  {
    final Collection vertices = clearTraversalStates();
    for (final Vertex vertex: vertices)
    {
      if (vertex.getTraversalState() == TraversalState.notStarted)
      {
        if (visitForCyles(vertex))
        {
          return true;
        }
      }
    }

    return false;
  }

  public DirectedGraph subGraph(final T value)
  {
    return subGraph(value, -1);
  }

  public DirectedGraph subGraph(final T value, final int depth)
  {
    final Collection vertices = clearTraversalStates();
    visitForSubGraph(verticesMap.get(value), depth);

    final Set subGraphVertices = new HashSet<>();
    for (final Vertex currentVertex: vertices)
    {
      if (currentVertex.getTraversalState() == TraversalState.complete)
      {
        subGraphVertices.add(currentVertex);
      }
    }

    final DirectedGraph subGraph = new DirectedGraph<>();
    for (final DirectedEdge edge: edges)
    {
      final Vertex from = edge.getFrom();
      final Vertex to = edge.getTo();
      if (subGraphVertices.contains(from) && subGraphVertices.contains(to))
      {
        subGraph.addDirectedEdge(from.getValue(), to.getValue());
      }
    }
    // In case this is an isolated node
    subGraph.addVertex(value);

    return subGraph;
  }

  public List topologicalSort()
    throws GraphException
  {
    if (containsCycle())
    {
      throw new GraphException("Graph contains a cycle, so cannot be topologically sorted");
    }

    final int collectionSize = verticesMap.size();

    final Collection vertices = new ArrayList<>(verticesMap.values());
    final Collection edges = new ArrayList<>(this.edges);
    final List sortedValues = new ArrayList<>(collectionSize);

    while (!vertices.isEmpty())
    {

      final List nodesAtLevel = new ArrayList<>(collectionSize);

      // Remove unattached nodes
      for (final Iterator iterator = vertices.iterator(); iterator
        .hasNext();)
      {
        final Vertex vertex = iterator.next();
        if (isUnattachedNode(vertex, edges))
        {
          nodesAtLevel.add(vertex.getValue());
          iterator.remove();
        }
      }

      // Find all nodes at the current level
      final List startNodes = new ArrayList<>(collectionSize);
      for (final Vertex vertex: vertices)
      {
        if (isStartNode(vertex, edges))
        {
          startNodes.add(vertex);
        }
      }

      for (final Vertex vertex: startNodes)
      {
        // Save the vertex value
        nodesAtLevel.add(vertex.getValue());
        // Remove all out edges
        dropOutEdges(vertex, edges);
        // Remove the vertex itself
        vertices.remove(vertex);
      }

      Collections.sort(nodesAtLevel);
      sortedValues.addAll(nodesAtLevel);
    }

    return sortedValues;
  }

  private Collection clearTraversalStates()
  {
    final Collection vertices = verticesMap.values();
    for (final Vertex vertex: vertices)
    {
      vertex.setTraversalState(TraversalState.notStarted);
    }
    return vertices;
  }

  private void dropOutEdges(final Vertex vertex,
                            final Collection edges)
  {
    for (final Iterator iterator = edges.iterator(); iterator
      .hasNext();)
    {
      final DirectedEdge edge = iterator.next();
      if (edge.isFrom(vertex))
      {
        iterator.remove();
      }
    }
  }

  private boolean isStartNode(final Vertex vertex,
                              final Collection edges)
  {
    for (final DirectedEdge edge: edges)
    {
      if (edge.isTo(vertex))
      {
        return false;
      }
    }
    return true;
  }

  private boolean isUnattachedNode(final Vertex vertex,
                                   final Collection edges)
  {
    for (final DirectedEdge edge: edges)
    {
      if (edge.isTo(vertex) || edge.isFrom(vertex))
      {
        return false;
      }
    }
    return true;
  }

  private boolean visitForCyles(final Vertex vertex)
  {
    vertex.setTraversalState(TraversalState.inProgress);

    for (final DirectedEdge edge: edges)
    {
      if (edge.isFrom(vertex))
      {
        final Vertex to = edge.getTo();
        if (to.getTraversalState() == TraversalState.inProgress)
        {
          return true;
        }
        else if (to.getTraversalState() == TraversalState.notStarted)
        {
          if (visitForCyles(to))
          {
            return true;
          }
        }
      }
    }

    vertex.setTraversalState(TraversalState.complete);

    return false;
  }

  private void visitForSubGraph(final Vertex vertex, final int depth)
  {
    vertex.setTraversalState(TraversalState.complete);
    if (depth == 0)
    {
      return;
    }
    for (final DirectedEdge edge: edges)
    {
      if (edge.isFrom(vertex))
      {
        visitForSubGraph(edge.getTo(), depth - 1);
      }
    }

  }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy