package nl.tudelft.lifetiles.graph.model.jgrapht; import java.util.ArrayList; import java.util.Comparator; import java.util.List; import java.util.Set; import java.util.SortedSet; import java.util.TreeSet; import nl.tudelft.lifetiles.graph.model.Edge; import nl.tudelft.lifetiles.graph.model.Graph; import org.jgrapht.DirectedGraph; import org.jgrapht.graph.DefaultEdge; import org.jgrapht.graph.SimpleDirectedGraph; /** * @author Rutger van den Berg * * @param <V> * The type of vertex to use. */ public class JGraphTGraphAdapter<V extends Comparable<V>> implements Graph<V> { /** * The edgefactory to use to create the edges for this graph. */ private final JGraphTEdgeFactory<V> edgeFact; /** * This is the actual graph. */ private final DirectedGraph<V, DefaultEdge> internalGraph; /** * Keep track of all vertices that have no incoming edges. */ private SortedSet<V> sources; /** * Keep track of all vertices that have no outgoing edges. */ private SortedSet<V> sinks; /** * List of vertices. Used to be able to identify nodes by ids. */ private final List<V> vertexIdentifiers; /** * Creates a new Graph. * * @param edgeFact * The edgefactory to use for this graph. */ public JGraphTGraphAdapter(final JGraphTEdgeFactory<V> edgeFact) { internalGraph = new SimpleDirectedGraph<V, DefaultEdge>( DefaultEdge.class); this.edgeFact = edgeFact; sources = new TreeSet<>(); sinks = new TreeSet<>(); vertexIdentifiers = new ArrayList<>(); } /** * Create a new Subgraph, based on the JGraphT library. * * @param jgraptGraph * original JGraphT grah * @param edgeFact * The edgefactory to use for this graph. * @param vertexIds * Vertex identifiers */ protected JGraphTGraphAdapter( final DirectedGraph<V, DefaultEdge> jgraptGraph, final JGraphTEdgeFactory<V> edgeFact, final List<V> vertexIds) { internalGraph = jgraptGraph; this.edgeFact = edgeFact; vertexIdentifiers = vertexIds; } /** * {@inheritDoc} */ @Override public void addEdge(final int source, final int destination) { addEdge(vertexIdentifiers.get(source), vertexIdentifiers.get(destination)); } /** * @param source * The source vertex to use. * @param destination * The destination vertex to use. * @throws IllegalArgumentException * When the source or destination is not in the graph. * @throws IllegalArgumentException * When edge would create a loop in the graph. * @return <code>true</code> iff adding succeeded. */ @Override public boolean addEdge(final V source, final V destination) throws IllegalArgumentException { if (internalGraph.containsVertex(source) && internalGraph.containsVertex(destination)) { internalGraph.addEdge(source, destination); sources.remove(destination); sinks.remove(source); return true; } else { throw new IllegalArgumentException( "Source or destination not in graph."); } } /** * @param vertex * The vertex to add. */ @Override public void addVertex(final V vertex) { internalGraph.addVertex(vertex); vertexIdentifiers.add(vertex); if (sources == null) { getSources(); } sources.add(vertex); if (sinks == null) { getSinks(); } sinks.add(vertex); } /** * Helper for getIncoming and getOutgoing. * * @param input * the set to convert. * @return The converted set. */ private SortedSet<Edge<V>> convertEdges(final Set<DefaultEdge> input) { SortedSet<Edge<V>> output = new TreeSet<>(new EdgeComparatorByVertex()); for (DefaultEdge e : input) { output.add(edgeFact.getEdge(e)); } return output; } /** * @return All edges. */ @Override public SortedSet<Edge<V>> getAllEdges() { return convertEdges(internalGraph.edgeSet()); } /** * @return All vertices. */ @Override public SortedSet<V> getAllVertices() { return new TreeSet<V>(internalGraph.vertexSet()); } /** * @param edge * The edge to use. * @return The destination <code>vertex</code> for <code>e</code>. */ @Override public V getDestination(final Edge<V> edge) { return internalGraph.getEdgeTarget(unpackEdge(edge)); } /** * @param vertex * The vertex to use. * @return The edges incoming to <code>vertex</code>. */ @Override public SortedSet<Edge<V>> getIncoming(final V vertex) { return convertEdges(internalGraph.incomingEdgesOf(vertex)); } /** * @param vertex * The vertex to use. * @return The edges outgoing from <code>vertex</code>. */ @Override public SortedSet<Edge<V>> getOutgoing(final V vertex) { return convertEdges(internalGraph.outgoingEdgesOf(vertex)); } /** * @return All vertices that have no incoming edges. */ @Override public SortedSet<V> getSources() { if (sources == null) { SortedSet<V> foundSources = new TreeSet<V>(); for (V vertice : getAllVertices()) { if (!getIncoming(vertice).isEmpty()) { foundSources.add(vertice); } } sources = foundSources; } return sources; } /** * @param edge * The edge to use. * @return The source <code>Vertex</code> for <code>e</code>. */ @Override public V getSource(final Edge<V> edge) { return internalGraph.getEdgeSource(unpackEdge(edge)); } /** * Helper method. * * @param input * An edge. * @return the internal edge. */ private DefaultEdge unpackEdge(final Edge<V> input) { if (!(input instanceof JGraphTEdgeAdapter<?>)) { throw new IllegalArgumentException("Wrong edge type."); } JGraphTEdgeAdapter<V> edge = (JGraphTEdgeAdapter<V>) input; return edge.getInternalEdge(); } /** * @return All vertices that have no outgoing edges. */ @Override public SortedSet<V> getSinks() { if (sinks == null) { SortedSet<V> foundSinks = new TreeSet<V>(); for (V vertice : getAllVertices()) { if (!getOutgoing(vertice).isEmpty()) { foundSinks.add(vertice); } } sinks = foundSinks; } return sinks; } /** * Splits an edge into two edges with an inserted vertex in the middle. * * @param edge * Edge to be divided. * @param vertex * Vertex to be inserted. */ @Override public void splitEdge(final Edge<V> edge, final V vertex) { removeEdge(edge); addVertex(vertex); addEdge(getSource(edge), vertex); addEdge(vertex, getDestination(edge)); } /** * @param edge * Edge to be removed. */ private void removeEdge(final Edge<V> edge) { internalGraph.removeEdge(unpackEdge(edge)); } /** * * @return get the JGraphT graph */ protected final DirectedGraph<V, DefaultEdge> getInternalGraph() { return internalGraph; } /** * * @return the vertexIdentifiers */ protected final List<V> getVertexIdentifiers() { return vertexIdentifiers; } /** * @author Rutger van den Berg * Compares two edges by their target vertex. */ class EdgeComparatorByVertex implements Comparator<Edge<V>> { /** * {@inheritDoc} */ @Override public int compare(final Edge<V> left, final Edge<V> right) { int candidate = getDestination(left).compareTo( getDestination(right)); if (candidate == 0) { candidate = getSource(left).compareTo(getSource(right)); } return candidate; } } }