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; }