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

jcommon.graph.DirectedAcyclicGraph Maven / Gradle / Ivy

Go to download

Java library for simple in-memory graph operations such as iterative and concurrent topological sorts.

The newest version!
/*
  Copyright (C) 2012-2013 the original author or authors.

  See the LICENSE.txt file distributed with this work for additional
  information regarding copyright ownership.

  Licensed 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 jcommon.graph;

import jcommon.graph.impl.AdjacencyList;
import jcommon.graph.impl.Edge;
import jcommon.graph.impl.SimpleTopologicalSort;

import java.lang.reflect.Constructor;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * Factory and implementation of a dependency graph that can topologically sort its vertices.
 *
 * @param  Type of {@link IVertex} vertices that this graph contains.
 *
 * @see IGraph
 */
public class DirectedAcyclicGraph, TValue extends Object, TProcessedValue extends Object> implements Cloneable, IGraph {
  private Set vertices = new LinkedHashSet(5, 0.8f);
  private Set> edges = new LinkedHashSet>(8, 0.8f);

  /**
   * Protected constructor to prevent public instantiation.
   */
  protected DirectedAcyclicGraph() {
  }

  /**
   * @see IGraph#getVertices()
   */
  @Override
  public Set getVertices() {
    return Collections.unmodifiableSet(vertices);
  }

  /**
   * @see IGraph#getEdges()
   */
  @Override
  public Set> getEdges() {
    return Collections.unmodifiableSet(edges);
  }

  /**
   * @see IGraph#clone()
   */
  @Override
  protected Object clone() throws CloneNotSupportedException {
    return copyGraph(this);
  }

  /**
   * Copies the {@link DirectedAcyclicGraph} portion of a class that extends {@link DirectedAcyclicGraph}. Anything else that
   * is specific to a superclass should be added to the returned instance.
   *
   * @param graph An instance of {@link DirectedAcyclicGraph} that needs copying.
   * @param  Type of {@link IVertex} of the graph vertices.
   * @param  Type of {@link DirectedAcyclicGraph} of the graph being copied.
   * @return A new instance of type {@link TGraph} with {@link DirectedAcyclicGraph} specific fields already filled in.
   */
  @SuppressWarnings("unchecked")
  protected static , TVertex extends IVertex, TValue extends Object, TProcessedValue extends Object> TGraph copyGraph(final TGraph graph) {
    try {
      final Class g_class = graph.getClass();
      final Constructor> construct = g_class.getDeclaredConstructor();
      construct.setAccessible(true);

      final TGraph g = (TGraph)construct.newInstance();

      g.vertices = new LinkedHashSet(graph.vertices);
      g.edges = new LinkedHashSet>(graph.edges);
      return g;
    } catch(Throwable t) {
      return null;
    }
  }

  /**
   * @see IGraph#copy()
   */
  public IGraph copy() {
    return copyGraph(this);
  }

  /**
   * Convenience method for easily constructing an instance of {@link IGraph} with the provided vertices.
   *
   * @param vertices A list of {@link IVertex} vertices to be added to a new {@link IGraph}.
   * @param  Type of {@link IVertex} of the vertices in the new {@link IGraph}.
   * @return A new instance of {@link IGraph} with the provided vertices already added.
   */
  public static , TValue extends Object, TProcessedValue extends Object> IGraph build(final TVertex...vertices) {
    final DirectedAcyclicGraph g = new DirectedAcyclicGraph();
    if (vertices != null) {
      for(TVertex d : vertices) {
        g.addVertex(d);
      }
    }
    return g;
  }

  /**
   * Convenience method for easily constructing an instance of {@link IGraph} with an empty set of vertices.
   *
   * @param  Type of {@link IVertex} of the vertices in the new {@link IGraph}.
   * @return A new instance of {@link IGraph} with an empty set of vertices.
   */
  public static , TValue extends Object, TProcessedValue extends Object> IGraph create() {
    return build((TVertex[])null);
  }

  /**
   * @see IGraph#addVertex(IVertex)
   */
  @Override
  public IGraph addVertex(final TVertex vertex) {
    if (vertex == null)
      throw new IllegalArgumentException("vertex must not be null");
    vertices.add(vertex);
    return this;
  }

  /**
   * @see IGraph#removeVertex(IVertex)
   */
  @Override
  public IGraph removeVertex(final TVertex vertex) {
    if (vertex == null)
      throw new IllegalArgumentException("vertex must not be null");
    vertices.remove(vertex);
    return this;
  }

  /**
   * @see IGraph#addEdge(IVertex, IVertex)
   */
  @Override
  public IGraph addEdge(final TVertex from, final TVertex to) {
    edges.add(new Edge(from, to));
    return this;
  }

  /**
   * @see IGraph#removeEdge(IVertex, IVertex)
   */
  @Override
  public IGraph removeEdge(final TVertex from, final TVertex to) {
    edges.remove(new Edge(from, to));
    return this;
  }

  /**
   * @see IGraph#validate()
   */
  @Override
  public boolean validate() {
    //Ensure that every from/to in an edge is present in our set of vertices.
    //If we refer to one that isn't in there, then we've got a problem.
    for(IEdge r : edges) {
      if (!vertices.contains(r.getFrom()) || !vertices.contains(r.getTo()))
        return false;
    }
    return true;
  }

  /**
   * @see IGraph#sort()
   */
  @Override
  public List sort() throws CyclicGraphException {
    return sort(new SimpleTopologicalSort());
  }

  /**
   * @see IGraph#sort(ITopologicalSortStrategy)
   */
  @Override
  public List sort(final ITopologicalSortStrategy strategy) throws CyclicGraphException {
    if (strategy == null)
      throw new IllegalArgumentException("strategy cannot be null");
    if (!validate())
      throw new IllegalStateException("The graph is invalid. Please confirm that all vertices are present for every relationship.");
    return strategy.sort(new AdjacencyList(vertices, edges));
  }

  /**
   * @see IGraph#sortAsync(ITopologicalSortCallback)
   */
  @Override
  public ITopologicalSortAsyncResult sortAsync(final ITopologicalSortCallback callback) {
    return sortAsync(new SimpleTopologicalSort(), callback, null);
  }

  /**
   * @see IGraph#sortAsync(ITopologicalSortCallback, ITopologicalSortErrorCallback)
   */
  @Override
  public ITopologicalSortAsyncResult sortAsync(final ITopologicalSortCallback callback, final ITopologicalSortErrorCallback errorCallback) {
    return sortAsync(new SimpleTopologicalSort(), callback, errorCallback);
  }

  /**
   * @see IGraph#sortAsync(ITopologicalSortStrategy, ITopologicalSortCallback)
   */
  @Override
  public ITopologicalSortAsyncResult sortAsync(final ITopologicalSortStrategy strategy, final ITopologicalSortCallback callback) {
    return sortAsync(strategy, callback, null);
  }

  /**
   * @see IGraph#sortAsync(ITopologicalSortStrategy, ITopologicalSortCallback, ITopologicalSortErrorCallback)
   */
  @Override
  public ITopologicalSortAsyncResult sortAsync(final ITopologicalSortStrategy strategy, final ITopologicalSortCallback callback, final ITopologicalSortErrorCallback errorCallback) {
    final ExecutorService executor = Executors.newFixedThreadPool(Math.max(2, Runtime.getRuntime().availableProcessors() + 1));
    final ITopologicalSortAsyncResult result = sortAsync(executor, strategy, callback, errorCallback);

    //We don't explicity shutdown b/c we may not be done processing
    //at this point. If need to abort prematurely, the caller can
    //access the executor service from the .getExecutorService()
    //method.
    return result;
  }

  /**
   * @see IGraph#sortAsync(java.util.concurrent.ExecutorService, ITopologicalSortCallback)
   */
  @Override
  public ITopologicalSortAsyncResult sortAsync(final ExecutorService executor, final ITopologicalSortCallback callback) {
    return sortAsync(executor, new SimpleTopologicalSort(), callback, null);
  }

  /**
   * @see IGraph#sortAsync(java.util.concurrent.ExecutorService, ITopologicalSortCallback, ITopologicalSortErrorCallback)
   */
  @Override
  public ITopologicalSortAsyncResult sortAsync(final ExecutorService executor, final ITopologicalSortCallback callback, final ITopologicalSortErrorCallback errorCallback) {
    return sortAsync(executor, new SimpleTopologicalSort(), callback, errorCallback);
  }

  /**
   * @see IGraph#sortAsync(java.util.concurrent.ExecutorService, ITopologicalSortStrategy, ITopologicalSortCallback)
   */
  @Override
  public ITopologicalSortAsyncResult sortAsync(final ExecutorService executor, final ITopologicalSortStrategy strategy, final ITopologicalSortCallback callback) {
    return sortAsync(executor, strategy, callback, null);
  }

  /**
   * @see IGraph#sortAsync(java.util.concurrent.ExecutorService, ITopologicalSortStrategy, ITopologicalSortCallback, ITopologicalSortErrorCallback)
   */
  @Override
  public ITopologicalSortAsyncResult sortAsync(final ExecutorService executor, final ITopologicalSortStrategy strategy, final ITopologicalSortCallback callback, final ITopologicalSortErrorCallback errorCallback) {
    if (strategy == null)
      throw new IllegalArgumentException("strategy cannot be null");
    if (callback == null)
      throw new IllegalArgumentException("callback cannot be null");
    if (!validate())
      throw new IllegalStateException("The graph is invalid. Please confirm that all vertices are present for every relationship.");

    return strategy.sortAsync(executor, new AdjacencyList(vertices, edges), callback, errorCallback);
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy