package spimedb.graph.travel; import org.eclipse.collections.api.tuple.Pair; import org.eclipse.collections.api.tuple.primitive.ObjectIntPair; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import spimedb.graph.MapGraph; import java.util.Iterator; import java.util.Map; import java.util.NoSuchElementException; import static org.eclipse.collections.impl.tuple.primitive.PrimitiveTuples.pair; /** * Provides a cross-connected-component traversal functionality for iterator subclasses. * * @param <V> vertex type * @param <E> edge type * @param <D> type of data associated to seen vertices * @author Barak Naveh * @since Jan 31, 2004 */ public abstract class CrossComponentTravel<V, E, D> extends AbstractTravel<V, E> { private static final int CCS_BEFORE_COMPONENT = 1; private static final int CCS_WITHIN_COMPONENT = 2; private static final int CCS_AFTER_COMPONENT = 3; /** * true = in, false = out */ private final boolean inOrOut = true; public CrossComponentTravel(MapGraph<V, E> g, Map<V, D> seen) { this(g, null, seen); } /** * Standard vertex visit state enumeration. */ protected enum VisitColor { /** * Vertex has not been returned via iterator yet. */ WHITE, /** * Vertex has been returned via iterator, but we're not done with all of its out-edges yet. */ GRAY, /** * Vertex has been returned via iterator, and we're done with all of its out-edges. */ BLACK } /** * Connected component traversal started event. */ public static final int CONNECTED_COMPONENT_ENTER = 31; private final ObjectIntPair ccEnterEvent = pair(this, CONNECTED_COMPONENT_ENTER); /** * Connected component traversal finished event. */ public static final int CONNECTED_COMPONENT_EXIT = 32; private final ObjectIntPair ccExitEvent = pair(this, CONNECTED_COMPONENT_EXIT); private final Iterator<V> vertexIterator; /** * Stores the vertices that have been seen during iteration and (optionally) some additional * traversal info regarding each vertex. */ public final Map<V, D> seen; private V startVertex; public final MapGraph<V, E> graph; /** * The connected component state */ private int state = CCS_BEFORE_COMPONENT; /** * Creates a new iterator for the specified graph. Iteration will start at the specified start * vertex. If the specified start vertex is <code> * null</code>, Iteration will start at an arbitrary graph vertex. * * @param g the graph to be iterated. * @param startVertex the vertex iteration to be started. * @param seen * @throws IllegalArgumentException if <code>g==null</code> or does not contain * <code>startVertex</code> */ public CrossComponentTravel(MapGraph<V, E> g, @Nullable V startVertex, @NotNull Map<V, D> seen) { super(); this.seen = seen; this.graph = g; // specifics = createGraphSpecifics(g); vertexIterator = g.vertexSet().iterator(); setCrossComponentTraversal(startVertex == null); // reusableEdgeEvent = new FlyweightEdgeEvent<>(this, null); // reusableVertexEvent = new FlyweightVertexEvent<>(this, null); if (startVertex == null) { // pick a start vertex if graph not empty if (vertexIterator.hasNext()) { this.startVertex = vertexIterator.next(); } else { this.startVertex = null; } } else if (g.containsVertex(startVertex)) { this.startVertex = startVertex; } else { throw new IllegalArgumentException("graph must contain the start vertex"); } } /** * @see java.util.Iterator#hasNext() */ @Override public boolean hasNext() { if (startVertex != null) { encounterStartVertex(); } if (isConnectedComponentExhausted()) { if (state == CCS_WITHIN_COMPONENT) { state = CCS_AFTER_COMPONENT; fireConnectedComponentFinished(ccExitEvent); } if (isCrossComponentTraversal()) { while (vertexIterator.hasNext()) { V v = vertexIterator.next(); if (!isSeenVertex(v)) { encounterVertex(v, null); state = CCS_BEFORE_COMPONENT; return true; } } return false; } else { return false; } } else { return true; } } /** * @see java.util.Iterator#next() */ @Override public V next() { if (startVertex != null) { encounterStartVertex(); } if (hasNext()) { if (state == CCS_BEFORE_COMPONENT) { state = CCS_WITHIN_COMPONENT; fireConnectedComponentStarted(ccEnterEvent); } V nextVertex = provideNextVertex(); //Pair<V, E> incoming = Tuples.pair(currentVertex, ); fireVertexEnter(null, nextVertex); addIncidentEdges(nextVertex); return nextVertex; } else { throw new NoSuchElementException(); } } /** * Returns <tt>true</tt> if there are no more uniterated vertices in the currently iterated * connected component; <tt>false</tt> otherwise. * * @return <tt>true</tt> if there are no more uniterated vertices in the currently iterated * connected component; <tt>false</tt> otherwise. */ protected abstract boolean isConnectedComponentExhausted(); /** * Update data structures the first time we see a vertex. * * @param vertex the vertex encountered * @param edge the edge via which the vertex was encountered, or null if the vertex is a * starting point */ protected abstract void encounterVertex(V vertex, E edge); /** * Returns the vertex to be returned in the following call to the iterator <code>next</code> * method. * * @return the next vertex to be returned by this iterator. */ protected abstract V provideNextVertex(); /** * Access the data stored for a seen vertex. * * @param vertex a vertex which has already been seen. * @return data associated with the seen vertex or <code>null</code> if no data was associated * with the vertex. A <code>null</code> return can also indicate that the vertex was * explicitly associated with <code> * null</code>. */ protected D saw(V vertex) { return seen.get(vertex); } /** * Determines whether a vertex has been seen yet by this traversal. * * @param vertex vertex in question * @return <tt>true</tt> if vertex has already been seen */ protected boolean isSeenVertex(V vertex) { return seen.containsKey(vertex); } /** * Called whenever we re-encounter a vertex. The default implementation does nothing. * * @param vertex the vertex re-encountered * @param edge the edge via which the vertex was re-encountered */ protected abstract void encounterVertexAgain(V vertex, E edge); /** * Stores iterator-dependent data for a vertex that has been seen. * * @param vertex a vertex which has been seen. * @param data data to be associated with the seen vertex. * @return previous value associated with specified vertex or <code> * null</code> if no data was associated with the vertex. A <code> * null</code> return can also indicate that the vertex was explicitly associated with * <code>null</code>. */ protected D putSeenData(V vertex, D data) { return seen.put(vertex, data); } /** * Called when a vertex has been finished (meaning is dependent on traversal represented by * subclass). * * @param vertex vertex which has been finished */ protected void finishVertex(V vertex) { fireVertexFinished(vertex); } private void addIncidentEdges(V s) { boolean firesEdges = wantsEdges(); if (inOrOut) { for (Pair<V, E> p : graph.incomingEdgesOf(s)) { go(s, firesEdges, p.getOne(), p.getTwo()); } } else { for (Pair<E, V> p : graph.outgoingEdgesOf(s)) { go(s, firesEdges, p.getTwo(), p.getOne()); } } } private void go(V s, boolean firesEdges, V t, E edge) { if (firesEdges) fireEdgeTraversed(s, edge, t); if (isSeenVertex(t)) { encounterVertexAgain(t, edge); } else { encounterVertex(t, edge); } } @Override public Iterable<Pair<E, V>> edgesOut(V vertex) { return graph.outgoingEdgesOf(vertex); } // private EdgeTraversalEvent<E> createEdgeTraversalEvent(E edge) { // if (isReuseEvents()) { // reusableEdgeEvent.setEdge(edge); // // return reusableEdgeEvent; // } else { // return new EdgeTraversalEvent<>(this, edge); // } // } // // private ObjectIntPair createVertexTraversalEvent(V vertex) { // if (isReuseEvents()) { // reusableVertexEvent.setVertex(vertex); // // return reusableVertexEvent; // } else { // return new VertexTraversalEvent<>(this, vertex); // } // } private void encounterStartVertex() { encounterVertex(startVertex, null); startVertex = null; } // static interface SimpleContainer<T> { // /** // * Tests if this container is empty. // * // * @return <code>true</code> if empty, otherwise <code>false</code>. // */ // public boolean isEmpty(); // // /** // * Adds the specified object to this container. // * // * @param o the object to be added. // */ // public void add(T o); // // /** // * Remove an object from this container and return it. // * // * @return the object removed from this container. // */ // public T remove(); // } }