package rpi.goldsd.graph;
import java.util.Enumeration;
import java.util.Vector;
/**
* The Path class is an iterator class used to
* represent a path or walk through an associated graph.
*
* @see GraphBase
* @version 2.0, 4/19/98
* @author David Goldschmidt
*/
public class Path implements Cloneable, Drawable
{
/* ****************** CONSTRUCTOR METHODS ****************** */
/**
* Constructs an empty path with the initial path capacity.
* If used, this value should indicate the number of vertices
* and edges making up this path, including the start and
* end vertices.
* @param capacity the initial capacity for the vector used to represent
* the path of alternating vertices and edges.
*/
public Path( int capacity ) { path = new Vector( capacity ); }
/**
* Constructs a path with a given start vertex.
* @param startVertex the initial start vertex of this path.
* @exception IllegalArgumentException if the start vertex argument
* is not in a graph.
*/
public Path( Vertex startVertex ) throws IllegalArgumentException
{
this();
if ( ! startVertex.isInGraph() ) throw
new IllegalArgumentException( "Vertex must be in a graph." );
path.addElement( startVertex );
}
/**
* Constructs a path by copying another path.
* @param P the path to be copied.
*/
public Path( Path P )
{
this( P.path.size() );
pathCopyHelper( this, P );
}
/**
* Clones this path.
* @return a new path that is a clone of this path.
*/
public Object clone()
{
Path P = new Path( this.path.size() );
pathCopyHelper( P, this );
return P;
}
/**
* This protected static method performs a deep copy of all edges
* and vertices in the path source to the path target. Note
* that the target path is assumed to be constructed.
* @param target the target path object (should be an empty path).
* @param source the source path object.
*/
protected static final void pathCopyHelper( Path target, Path source )
{
Enumeration e = source.elements();
while ( e.hasMoreElements() )
target.path.addElement( e.nextElement() );
}
/** Constructs an empty path. */
public Path() { path = new Vector(); }
/* ****************** ACCESSOR METHODS ****************** */
/**
* Tests if a given edge exists in this path.
* @param E the edge to test.
* @return true if the given edge E is in this path;
* false otherwise.
*/
public boolean contains( Edge E ) { return ( path.contains( E ) ); }
/**
* Tests if a given vertex exists in this path.
* @param V the vertex to test.
* @return true if the given vertex V is in this path;
* false otherwise.
*/
public boolean contains( Vertex V ) { return ( path.contains( V ) ); }
/**
* Returns an enumeration of the edges in this path.
* @return an enumeration of the edges in this path.
*/
public Enumeration edges()
{
return new Enumeration()
{
int index = 1;
public boolean hasMoreElements()
{
return ( index < length() * 2 );
}
public Object nextElement()
{
Object E = path.elementAt( index );
index += 2;
return E;
}
};
}
/**
* Returns an enumeration of the vertices and edges in this path.
* Note that the nextElement() method of the enumeration returns
* alternating vertex and edge objects.
* @return an enumeration of the vertices and edges in this path.
*/
public Enumeration elements()
{
return new Enumeration()
{
int index = 0;
public boolean hasMoreElements()
{
return ( index <= length() * 2 );
}
public Object nextElement()
{
return ( path.elementAt( index++ ) );
}
};
}
/**
* Returns a reference to the first vertex in this path.
* @return a reference to the first vertex in this path.
*/
public Vertex firstVertex() { return ( (Vertex)path.firstElement() ); }
/**
* Returns a reference to the graph associated with this path.
* @return a reference to the graph associated with this path; or
* null if this path is not associated with
* a graph (which implies that the path is empty).
*/
public GraphBase graph() { return ( firstVertex().graph() ); }
/**
* Tests if this path is a cycle.
* @return true if the first and last vertices are the
* same; false otherwise.
*/
public boolean isCycle() { return ( firstVertex() == lastVertex() ); }
/**
* Tests if the given endVertex is reachable from the
* startVertex in this path. Note that if this path is a
* cycle, then if the two given vertices exist in this path, then this
* method returns true. Also note that if duplicate vertices
* appear in the path, then the first occurrence of the startVertex
* is used as a starting place.
* @param startVertex the vertex to start the traversal from.
* @param endVertex the destination vertex.
* @return true if endVertex is reachable from
* startVertex; false otherwise.
*/
public boolean isReachable( Vertex startVertex, Vertex endVertex )
{
int startPosition = path.indexOf( startVertex );
if ( startPosition == -1 )
return false;
if ( isCycle() )
return ( path.contains( endVertex ) );
int endPosition = path.indexOf( endVertex, startPosition );
return ( endPosition != -1 );
}
/**
* Returns a reference to the last vertex in this path.
* @return a reference to the last vertex in this path.
*/
public Vertex lastVertex() { return ( (Vertex)path.lastElement() ); }
/**
* Returns the number of edges in this path.
* @return the number of edges in this path.
*/
public int length() { return ( path.size() / 2 ); }
/**
* Returns the name of the graph this path is associated with.
* @return the name of the graph this path is associated with.
*/
public String name() { return ( graph().name() ); }
/**
* Returns the number of occurrences of a given edge in this path.
* @return the number of occurrences of the given edge E in this
* path.
*/
public int occurrences( Edge E )
{
int count = 0;
Enumeration e = edges();
while ( e.hasMoreElements() )
if ( e.nextElement() == E )
count++;
return count;
}
/**
* Returns the number of occurrences of a given vertex in this path.
* @return the number of occurrences of the given vertex V in
* this path.
*/
public int occurrences( Vertex V )
{
int count = 0;
Enumeration e = vertices();
while ( e.hasMoreElements() )
if ( e.nextElement() == V )
count++;
return count;
}
/**
* Returns a String representation of this path. The
* String begins with "Path: " and then makes use of the
* toString() method of each of the vertices of this path,
* separating each vertex with "->" to represent an edge.
* @return a String representation of this path.
*/
public String toString()
{
Enumeration e = vertices();
if ( ! e.hasMoreElements() )
return ( "." );
String s = "";
while ( e.hasMoreElements() )
{
s = s + (Vertex)e.nextElement();
if ( e.hasMoreElements() )
s = s + "->";
}
return ( s + "." );
}
/**
* Returns an enumeration of the vertices of this path.
* @return an enumeration of the vertices of this path.
*/
public Enumeration vertices()
{
return new Enumeration()
{
int index = 0;
public boolean hasMoreElements()
{
return ( index <= length() * 2 );
}
public Object nextElement()
{
Object E = path.elementAt( index );
index += 2;
return E;
}
};
}
/**
* Returns the weight of this path, which is simply the sum of all
* weighted edges of this path.
* @return the weight of this path.
*/
public double weight()
{
double weight = 0.0;
Enumeration e = edges();
while ( e.hasMoreElements() )
weight += ((Edge)e.nextElement()).weight();
return weight;
}
/* ****************** MUTATOR METHODS ****************** */
/**
* Adds an edge and its corresponding destination vertex to the
* end of this path. The edge must be associated with the last
* vertex of this path. Note that this method actually adds two
* references to the end of this path: first the given edge E,
* and second the destination vertex found by traversing E from
* the last vertex of this path.
* @param E the edge to be added.
* @exception IllegalArgumentException if the edge is not associated with
* the last vertex of this path.
*/
public void add( Edge E ) throws IllegalArgumentException
{
Vertex V = lastVertex();
if ( ( V != E.startVertex() && V != E.endVertex() ) ||
( V == E.endVertex() && E instanceof DirectedEdge ) )
throw new IllegalArgumentException( "Edge not part of path." );
path.addElement( E );
path.addElement( E.traverseFrom( V ) );
}
/**
* Adds an edge and its corresponding destination vertex to the
* end of this path. The edge must be associated with the last
* vertex of this path. Note that this method actually adds two
* references to the end of this path: first the given edge,
* and second the given destination vertex.
* @param V the vertex to be added.
* @exception IllegalArgumentException if there is no edge connecting
* the last vertex of this path and the given vertex argument.
*/
public void add( Vertex V ) throws IllegalArgumentException
{
Vertex lastV = lastVertex();
Enumeration e = lastV.incidentEdges();
while ( e.hasMoreElements() )
{
Edge E = (Edge)e.nextElement();
if ( E.traverseFrom( lastV ) == V )
{
add( E );
return;
}
}
throw new IllegalArgumentException( "No such edge to given vertex." );
}
/**
* Initializes this path and adds the starting vertex to this path. Note
* that this method removes all elements from the existing path, if any.
* @param V the starting vertex of this path.
* @exception IllegalArgumentException if the Vertex argument is
* not in a graph object.
*/
public void addStartVertex( Vertex V ) throws IllegalArgumentException
{
if ( ! V.isInGraph() )
throw new IllegalArgumentException( "Vertex must be in a graph." );
path.removeAllElements();
path.addElement( V );
}
/**
* Removes the last vertex and edge from this path.
* @return the edge removed from this path; or null if
* the path contains no edges.
*/
public Edge backtrack()
{
if ( length() < 1 )
return null;
path.removeElementAt( path.size() - 1 );
Edge E = (Edge)path.lastElement();
path.removeElementAt( path.size() - 1 );
return E;
}
/**
* Concatenates the given path with this path by adding all
* elements of the given path to the end of this path. Note that
* the last vertex of this path must be the same as the first
* vertex of the given path P.
* @param P the path to be added to this path.
* @exception IllegalArgumentException if the last vertex of this path
* is not the first vertex of the given Path object.
*/
public void concat( Path P ) throws IllegalArgumentException
{
if ( lastVertex() != P.firstVertex() )
throw new IllegalArgumentException();
Enumeration e = P.edges();
while ( e.hasMoreElements() )
add( (Edge)e.nextElement() );
}
/**
* Inserts a given path into this path after the first occurrence of the
* given vertex in this path. The vertex V must exist in this
* path and be the first and last vertex in the given path P
* (i.e. the given path P must be a cycle).
* @param P the path to be inserted.
* @param V the vertex after which the path P is to be inserted.
* @exception IllegalArgumentException if the given Path object
* is not a cycle, or if the given Vertex object does
* not begin the given Path object.
* @exception VertexNotFoundException if the given Vertex object
* is not in this path.
*/
public void insertAfter( Path P, Vertex V )
throws IllegalArgumentException, VertexNotFoundException
{
if ( ! P.isCycle() ) throw
new IllegalArgumentException( "Path argument is not a cycle." );
if ( V != P.firstVertex() ) throw
new IllegalArgumentException( "Vertex does not begin path argument." );
int position = path.indexOf( V );
if ( position == -1 ) throw
new VertexNotFoundException();
position++;
Enumeration e = P.elements();
e.nextElement(); // skip initial vertex ...
while ( e.hasMoreElements() )
path.insertElementAt( e.nextElement(), position++ );
}
/* ****************** CLASS ATTRIBUTES ****************** */
/**
* A Vector representing this path. This Vector
* contains an alternating series of vertices and edges. Note that
* this Vector must begin and end with a Vertex
* object, thus the size() of the Vector will always
* be odd (unless it is zero).
*/
protected Vector path;
}