blob: bb343d00adb4a015a495a8ee5b0f2b7c17a0ea5f [file] [log] [blame]
package org.apache.commons.graph.visit;
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
import static org.apache.commons.graph.utils.Assertions.checkNotNull;
import static org.apache.commons.graph.visit.VisitState.ABORT;
import static org.apache.commons.graph.visit.VisitState.CONTINUE;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Set;
import org.apache.commons.graph.DirectedGraph;
import org.apache.commons.graph.Graph;
import org.apache.commons.graph.VertexPair;
/**
* {@link VisitAlgorithmsSelector} implementation.
*
* @param <V> the Graph vertices type
* @param <E> the Graph edges type
* @param <G> the Graph type
*/
final class DefaultVisitAlgorithmsSelector<V, E, G extends Graph<V, E>>
implements VisitAlgorithmsSelector<V, E, G>
{
/** The graph. */
private final G graph;
/** The start vertex for the search. */
private final V source;
/**
* Create a default {@link VisitAlgorithmsSelector} for the given {@link Graph} and start vertex.
*
* @param graph the {@link Graph} to be used.
* @param source the start vertex.
*/
public DefaultVisitAlgorithmsSelector( final G graph, final V source )
{
this.graph = graph;
this.source = source;
}
/**
* {@inheritDoc}
*/
public Graph<V, E> applyingBreadthFirstSearch()
{
return applyingBreadthFirstSearch( new VisitGraphBuilder<V, E, G>() );
}
/**
* {@inheritDoc}
*/
public Graph<V, E> applyingDepthFirstSearch()
{
return applyingDepthFirstSearch( new VisitGraphBuilder<V, E, G>() );
}
/**
* {@inheritDoc}
*/
public <O> O applyingBreadthFirstSearch( GraphVisitHandler<V, E, G, O> handler )
{
return applyingSearch( handler, true );
}
/**
* {@inheritDoc}
*/
public <O> O applyingDepthFirstSearch( GraphVisitHandler<V, E, G, O> handler )
{
return applyingSearch( handler, false );
}
/**
* A generalized graph search algorithm to be used to implement depth-first and breadth-first searches. Depending on
* the used collection, the algorithm traverses the graph in a different way:
* <ul>
* <li>Queue (FIFO): breadth-first</li>
* <li>Stack (LIFO): depth-first</li>
* </ul>
*
* @param handler the handler intercepts visits
* @param enqueue defines the collection behavior used to traverse the graph: true is a Queue, false is a Stack
* @return the result of {@link GraphVisitHandler#onCompleted()}
*/
private <O> O applyingSearch( GraphVisitHandler<V, E, G, O> handler, boolean enqueue )
{
handler = checkNotNull( handler, "Graph visitor handler can not be null." );
handler.discoverGraph( graph );
final LinkedList<VertexPair<V>> vertexList = new LinkedList<VertexPair<V>>();
vertexList.addLast( new VertexPair<V>( source, source ) );
final Set<V> visitedVertices = new HashSet<V>();
visitedVertices.add( source );
boolean visitingGraph = true;
while ( visitingGraph && !vertexList.isEmpty() )
{
// if dequeue, remove the first element, otherwise the last
final VertexPair<V> pair = enqueue ? vertexList.removeFirst() : vertexList.removeLast();
final V v = pair.getHead();
final V prevHead = pair.getTail();
final E e = prevHead.equals( v ) ? null : graph.getEdge( prevHead, v );
boolean skipVertex = false;
if ( e != null )
{
// if the vertex was already visited, do not discover
// another edge leading to the same vertex
if ( visitedVertices.contains( v ) )
{
skipVertex = true;
}
else
{
VisitState stateAfterEdgeDiscovery = handler.discoverEdge( prevHead, e, v );
if ( CONTINUE != stateAfterEdgeDiscovery )
{
skipVertex = true;
if ( ABORT == stateAfterEdgeDiscovery )
{
visitingGraph = false;
}
}
if ( ABORT == handler.finishEdge( prevHead, e, v ) )
{
skipVertex = true;
visitingGraph = false;
}
}
}
// only mark the current vertex as visited, if the
// edge leading to it should be expanded
boolean vertexWasDiscovered = false;
if ( !skipVertex )
{
visitedVertices.add( v );
VisitState stateAfterVertexDiscovery = handler.discoverVertex( v );
vertexWasDiscovered = true;
if ( CONTINUE != stateAfterVertexDiscovery )
{
skipVertex = true;
if ( ABORT == stateAfterVertexDiscovery )
{
visitingGraph = false;
}
}
}
if ( !skipVertex )
{
Iterator<V> connected =
( graph instanceof DirectedGraph ) ? ( (DirectedGraph<V, E>) graph ).getOutbound( v ).iterator()
: graph.getConnectedVertices( v ).iterator();
while ( connected.hasNext() )
{
V w = connected.next();
if ( !visitedVertices.contains( w ) )
{
vertexList.addLast( new VertexPair<V>( w, v ) );
}
}
}
if ( vertexWasDiscovered && ABORT == handler.finishVertex( v ) )
{
visitingGraph = false;
}
}
handler.finishGraph( graph );
return handler.onCompleted();
}
}