001    package ps3.graph;
002    
003    import java.util.*;
004    
005    /**
006     * A WeightedNodePath characterizes a path of WeightedNodes.  The cost
007     * for a path is the sum of the costs of the WeightedNodes it contains.<p>
008     *
009     * A WeightedNodePath is immutable. A new WeightedNodePath is returned through
010     * the extend path operation. <p> 
011     *
012     * Specfields inherited from Path:
013     *
014     * @specfield  cost :     double       // cost of traversing this path.
015     * @specfield  elements : sequence     // sequence of nodes in this path.
016     * 
017     **/
018    
019    public class WeightedNodePath implements Path<WeightedNode, WeightedNodePath> {
020    
021        // The representation invariant describes what invariants hold for
022        // the concrete representation. 
023        // 
024        // RepInvariant:
025        //  RI(c) = 
026        //   (c.node != null) &&
027        //   (c.path == null) ==> (c.cost == c.node.cost) &&
028        //   (c.path != null) ==> (c.cost == c.node.cost + c.path.cost)
029        //
030    
031        //
032        // Abstraction Function:
033        //
034        //  The abstract state is given in terms of the specfields of the
035        //  Path interface, namely the cost and elements of a path. 
036        //
037        //  The AF uses two helper functions, which map a concrete state
038        //  'c' to the abstract state.
039        //
040        //  AF(c) = < wnpcost(c), wnpelms(c) > 
041        //  (Maps c to wnp cost and wnp elements abstract fields recursively)
042        //
043        //    wnpcost(c) = c.cost
044        //    wnpelms(c) = / [c.node]                   if (c.path == null)
045        //                 \ wnpelms(c.path) + [c.node] if (c.path != null)
046        //
047        //  (Note that [c.node] appears at the *end* not the *start* of the
048        //   path sequence.)
049        //  
050        //  To make the AF(c) clearer, we could also write the following:
051        //  AF(c) = < cost, elements >  where
052        //   cost     = c.cost
053        //   elements = [c.node]                     if c.path == null 
054        //              [c.node] + c.path.elements   if c.path != null
055        //  
056    
057        /** The WeightedNode at the end of the path. */
058        private final WeightedNode node;
059        /** A WeightedNodePath which, when extended with 'node' at the end,
060         *  is equal to this.  May be null iff this path has only 1 element. */
061        private final WeightedNodePath path;
062        /** The cost of this WeightedNodePath. */
063        private final int cost;
064      
065    
066        /**
067         * Constructs a WeightedNodePath containing one node.
068         * 
069         * @requires node != null
070         * @effects Creates a new WeightedNodePath which originates at
071         * <code>node</code>.
072         **/
073        public WeightedNodePath(WeightedNode node) {
074            this(node, null);
075        }
076    
077        /**
078         * @requires node != null
079         * @effects Creates a new WeightedNodePath 'res' such that
080         * res.elements = path.elements + [ node ]
081         **/
082        private WeightedNodePath(WeightedNode node, WeightedNodePath path) {
083            if (node == null) {
084                throw new IllegalArgumentException();
085            }
086            this.node = node;
087            this.path = path;
088            if (path != null) {
089                this.cost = node.cost + path.cost;
090            } else {
091                this.cost = node.cost;
092            }
093        }
094    
095    
096        // Specified by Path interface.
097        public WeightedNodePath extend(WeightedNode node) {
098            return new WeightedNodePath(node, this);
099        }
100    
101        // Specified by Path interface.
102        public double cost() {
103            return cost;
104        }
105    
106        // Specified by Path interface (which extends Iterable)
107        public Iterator<WeightedNode> iterator() {
108            // reverse the linked list, so that elements are returned in order
109            // from start to end of the path.
110            List<WeightedNode> accumulator = new LinkedList<WeightedNode>();
111            for (WeightedNodePath cur = this; cur!=null; cur = cur.path) {
112                accumulator.add(0, cur.end());
113            }
114            return accumulator.iterator();
115        }
116    
117        /**
118         * @return a string representation of this. 
119         **/
120        public String toString() {
121            StringBuilder sb = new StringBuilder();
122            sb.append("[WeightedNodePath: ");
123            boolean first=true;
124            for (WeightedNode wn : this) {
125                if (first) first = false;
126                else sb.append(", ");
127                sb.append(wn);
128            }
129            sb.append("]");
130            return sb.toString();
131        }
132    
133        /**
134         * @return true iff o is a WeightedNodePath and o.elements is the
135         * same sequence as this.elements
136         **/
137        @Override
138        public boolean equals(Object o) {
139            if (!(o instanceof WeightedNodePath))
140                return false;
141            return this.equals((WeightedNodePath) o);
142        }
143    
144        /**
145         * @return true iff wnp.elements is the same sequence as this.elements
146         **/
147        public boolean equals(WeightedNodePath wnp) {
148            return (wnp != null) &&
149                this.node.equals(wnp.node) && 
150                (this.path == null ? wnp.path==null : this.path.equals(wnp.path));
151        }
152    
153        /**
154         * @return a valid hashcode for this. 
155         **/
156        public int hashCode() {
157            return node.hashCode() + (this.path==null ? 0 : 13 * path.hashCode());
158        }
159    
160    
161        /**
162         * Compares the cost of this path to the given path.
163         * @return the value 0 if the cost of this path is equal to the
164         *         cost of the given path; a value less than 0 if this.cost is
165         *         less than p.cost; and a value greater than 0 if this.cost
166         *         is greater than p.cost.
167         * @see java.lang.Comparable#compareTo
168         */
169        public int compareTo(Path<?,?> p) {
170            return Double.compare(this.cost(), p.cost());
171        }
172        
173        /**
174         * Return the end of this path
175         */
176        public WeightedNode end() {
177            return node;
178        }
179    }