/* * GeoTools - The Open Source Java GIS Toolkit * http://geotools.org * * (C) 2002-2008, Open Source Geospatial Foundation (OSGeo) * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. */ package org.geotools.graph.path; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Iterator; import java.util.List; import org.geotools.graph.structure.Edge; import org.geotools.graph.structure.Node; /** * Represents a walk in a graph. A <B>walk</B> W is defined as an ordered set * of nodes that two adjacenct nodes in the set share * an edge. More precisley: <BR> * <BR> * G = {N,E} * W = { n(i) in N | (n(i-1),n(i)) in E } * * @author Justin Deoliveira, Refractions Research Inc, jdeolive@refractions.net * * * @source $URL$ */ public class Walk extends ArrayList implements NodeSequence { private List m_edges; //TODO: DOCUMENT ME! public Walk() { } //TODO: DOCUMENT ME! public Walk(Collection nodes) { super(nodes); } /** * A valid walk is one in which each pair of adjacent nodes in the sequence * share an edge. Note, */ public boolean isValid() { //if edges were calculated successfly it is a valid walk return(getEdges() != null); } /** * Calculates the edges in the walk. If the edges of the walk cannot be * calculated (due to an invalid walk), null is returned, otherwise the * list of edges is returned. * * @return The edges of the walk, otherwise null if the edges cannot be * calculated. */ public List getEdges() { //calculate edges if (m_edges == null) { m_edges = buildEdges(); } return(m_edges); } /** * Adds a node to the walk. Adding a node clears the edge list which will be * recalculated on the next call to getEdges(). * * @param node Node to add to the walk. */ public boolean add(Node node) { m_edges = null; return(super.add(node)); } //TODO DOCUMENT ME! public void add(int index, Object element) { super.add(index, element); m_edges = null; } //TODO DOCUMENT ME! public boolean add(Object o) { return(add((Node)o)); } //TODO DOCUMENT ME! public boolean addAll(Collection c) { m_edges = null; return(super.addAll(c)); } public boolean addAll(int index, Collection c) { m_edges = null; return(super.addAll(index, c)); } public boolean addEdge(Edge e) { //append edge to end of path, path must be empty, or last node in path // must be a node of the edge //save current edge list List edges = getEdges(); if (isEmpty()) { //add both nodes add(e.getNodeA()); add(e.getNodeB()); } else { //walk is not empty, check to see if the last node is related to the edge Node last = getLast(); if (last.equals(e.getNodeA())) { add(e.getNodeB()); } else if (last.equals(e.getNodeB())) { add(e.getNodeA()); } else return(false); } //the addition of nodes resets the internal edge list so it must be rebuilt. // In the case that an edge shares both of its nodes with another edge // it is possible for the list to be rebuilt properly (ie. not contain // the edge being added). To rectify this situation, a backup copy of the // edge list is saved before the addition, the addition performed, the // edge explicitly added to the backup edge list, and the internal // edge list replaced by the modified backup edges.add(e); m_edges = edges; return(true); } public void addEdges(Collection edges) { for (Iterator itr = edges.iterator(); itr.hasNext();) { Edge e = (Edge)itr.next(); addEdge(e); } } /** * Removes a node from the walk. Removing a node clears the edge list which * will be recalculated on the next call to getEdges(). * * @param node Node to remove from the walk. */ public void remove(Node node) { super.remove(node); m_edges = null; } public Object remove(int index) { m_edges = null; return(super.remove(index)); } public boolean remove(Object o) { m_edges = null; return(super.remove(o)); } public boolean removeAll(Collection c) { m_edges = null; return(super.removeAll(c)); } /** * Determines if the walk is closed. A closed walk is one in which the * first and last nodes are the same. * * @return True if closed, otherwise false. */ public boolean isClosed() { if (isEmpty() || !isValid()) return(false); return(get(0).equals(get(size()-1))); } /** * @see NodeSequence#getFirst() */ public Node getFirst() { return((Node)get(0)); } /** * @see NodeSequence#getLast() */ public Node getLast() { return((Node)get(size()-1)); } /** * Internal method for building the edge set of the walk. This method * calculated the edges upon every call. * * @return The list of edges for the walk, or null if the edge set could * not be calculated due to an invalid walk. */ protected List buildEdges() { ArrayList edges = new ArrayList(); for (int i = 1; i < size(); i++) { Node prev = (Node)get(i-1); Node curr = (Node)get(i); Edge e = curr.getEdge(prev); if (e != null) edges.add(e); else return(null); } return(edges); } /** * Reverses the path. */ public void reverse() { Collections.reverse(this); m_edges = null; } /** * Truncates the path at the specified index. Nodes in the path whose * index is >= the specified index are removed. * * @param index The index of first node to be removed. */ public void truncate(int index) { removeRange(index, size()); m_edges = null; } /** * Returns an iterator that iterates over the path in reverse. The iterator * does not support the remove operation. * @return the reverse iterator. */ public Iterator riterator() { return( new Iterator() { int m_index = size()-1; public void remove() { throw new UnsupportedOperationException( "Path iterator does not support remove()" ); } public boolean hasNext() { return(m_index > -1); } public Object next() { return(get(m_index--)); } } ); } //TODO: DOCUMENT ME!!! public Path duplicate() { return(new Path(this)); } public boolean equals(Object other) { if (other instanceof Walk) return(equals((Walk)other)); return(false); } public boolean equals(Walk other) { if (other.size() == size()) { //make a node by node comparision Iterator thisnodes = iterator(); Iterator othernodes = other.iterator(); while(thisnodes.hasNext()) { Node thisnode = (Node)thisnodes.next(); Node othernode = (Node)othernodes.next(); if (!thisnode.equals(othernode)) return(false); } return(true); } return(false); } public int hashCode() { int hash = 7; hash = 31 * hash + getFirst().hashCode(); hash = 31 * hash + getLast().hashCode(); return(hash); } }