/**
* Copyright 2013-2014 Recruit Technologies Co., Ltd. and contributors
* (see CONTRIBUTORS.md)
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License. A copy of the
* License is distributed with this work in the LICENSE.md file. You may
* also obtain a copy of the License from
*
* 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 org.gennai.gungnir.graph;
import java.util.ArrayDeque;
import java.util.Collections;
import java.util.Deque;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import org.jgrapht.Graph;
import org.jgrapht.Graphs;
import org.jgrapht.graph.DefaultDirectedGraph;
import org.jgrapht.traverse.AbstractGraphIterator;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
public class DepthFirstIterator<V, E> extends AbstractGraphIterator<V, E> {
private enum VisitColor {
WHITE, GRAY, BLACK
}
public static final Object SENTINEL = new Object();
private final DefaultDirectedGraph<V, E> graph;
private Iterator<V> vertexIterator = null;
private V startVertex;
private Deque<Object> stack = new ArrayDeque<Object>();
private Map<V, VisitColor> seen = Maps.newHashMap();
public DepthFirstIterator(DefaultDirectedGraph<V, E> graph) {
super();
this.graph = graph;
vertexIterator = graph.vertexSet().iterator();
setCrossComponentTraversal(startVertex == null);
if (vertexIterator.hasNext()) {
startVertex = vertexIterator.next();
} else {
startVertex = null;
}
}
public Graph<V, E> getGraph() {
return graph;
}
private boolean isConnectedComponentExhausted() {
for (;;) {
if (stack.isEmpty()) {
return true;
}
if (stack.getLast() != SENTINEL) {
return false;
}
stack.removeLast();
@SuppressWarnings("unchecked")
V v = (V) stack.removeLast();
seen.put(v, VisitColor.BLACK);
}
}
public boolean hasNext() {
if (startVertex != null) {
seen.put(startVertex, VisitColor.WHITE);
stack.addLast(startVertex);
startVertex = null;
}
if (isConnectedComponentExhausted()) {
if (isCrossComponentTraversal()) {
while (vertexIterator.hasNext()) {
V v = vertexIterator.next();
if (!seen.containsKey(v)) {
seen.put(v, VisitColor.WHITE);
stack.addLast(v);
return true;
}
}
return false;
} else {
return false;
}
} else {
return true;
}
}
@SuppressWarnings("unchecked")
private V provideNextVertex() {
V v;
for (;;) {
Object o = stack.removeLast();
if (o == SENTINEL) {
v = (V) stack.removeLast();
seen.put(v, VisitColor.BLACK);
} else {
v = (V) o;
break;
}
}
stack.addLast(v);
stack.addLast(SENTINEL);
seen.put(v, VisitColor.GRAY);
return v;
}
public V next() {
if (startVertex != null) {
seen.put(startVertex, VisitColor.WHITE);
stack.addLast(startVertex);
startVertex = null;
}
if (hasNext()) {
V nextVertex = provideNextVertex();
List<E> edges = Lists.newArrayList(graph.outgoingEdgesOf(nextVertex));
Collections.reverse(edges);
for (E edge : edges) {
V vertex = Graphs.getOppositeVertex(graph, edge, nextVertex);
if (seen.containsKey(vertex)) {
VisitColor color = seen.get(vertex);
if (color == VisitColor.WHITE) {
stack.removeLastOccurrence(vertex);
stack.addLast(vertex);
}
} else {
seen.put(vertex, VisitColor.WHITE);
stack.addLast(vertex);
}
}
return nextVertex;
} else {
throw new NoSuchElementException();
}
}
}