001/*
002 * Copyright 2006 - 2013
003 *     Stefan Balev     <stefan.balev@graphstream-project.org>
004 *     Julien Baudry    <julien.baudry@graphstream-project.org>
005 *     Antoine Dutot    <antoine.dutot@graphstream-project.org>
006 *     Yoann Pigné      <yoann.pigne@graphstream-project.org>
007 *     Guilhelm Savin   <guilhelm.savin@graphstream-project.org>
008 * 
009 * This file is part of GraphStream <http://graphstream-project.org>.
010 * 
011 * GraphStream is a library whose purpose is to handle static or dynamic
012 * graph, create them from scratch, file or any source and display them.
013 * 
014 * This program is free software distributed under the terms of two licenses, the
015 * CeCILL-C license that fits European law, and the GNU Lesser General Public
016 * License. You can  use, modify and/ or redistribute the software under the terms
017 * of the CeCILL-C license as circulated by CEA, CNRS and INRIA at the following
018 * URL <http://www.cecill.info> or under the terms of the GNU LGPL as published by
019 * the Free Software Foundation, either version 3 of the License, or (at your
020 * option) any later version.
021 * 
022 * This program is distributed in the hope that it will be useful, but WITHOUT ANY
023 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
024 * PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
025 * 
026 * You should have received a copy of the GNU Lesser General Public License
027 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
028 * 
029 * The fact that you are presently reading this means that you have had
030 * knowledge of the CeCILL-C and LGPL licenses and that you accept their terms.
031 */
032package org.graphstream.graph;
033
034import java.util.Collection;
035import java.util.Iterator;
036import java.util.List;
037import java.util.Stack;
038
039/**
040 * Path description.
041 * 
042 * <p>
043 * A path is a class that stores ordered lists of nodes and links that are
044 * adjacent. Such a path may be manipulated with nodes and/or edges added or
045 * removed. This class is designed as a dynamic structure that is, to add edges
046 * during the construction of the path. Only edges need to be added, the nodes
047 * list is maintained automatically.
048 * </p>
049 * 
050 * <p>
051 * The two lists (one for nodes, one for edges) may be acceded at any moment in
052 * constant time.
053 * </p>
054 * 
055 * <p>
056 * The constraint of this class is that it needs to know the first node of the
057 * path (the root). This root can be set with the {@link #setRoot(Node)} method
058 * or by using the {@link #add(Node, Edge)} method.
059 * </p>
060 * 
061 * <p>
062 * The normal use with this class is to first use the {@link #setRoot(Node)}
063 * method to initialize the path; then to use the {@link #add(Edge)} method to
064 * grow it and the {@link #popEdge()} or {@link #popNode()}.
065 * 
066 */
067public class Path implements Structure {
068        // ------------- ATTRIBUTES ------------
069
070        /**
071         * The root of the path;
072         */
073        private Node root = null;
074
075        /**
076         * The list of edges that represents the path.
077         */
078        Stack<Edge> edgePath;
079
080        /**
081         * The list of nodes representing the path.
082         */
083        Stack<Node> nodePath;
084
085        // ------------- CONSTRUCTORS ------------
086
087        /**
088         * New empty path.
089         */
090        public Path() {
091                edgePath = new Stack<Edge>();
092                nodePath = new Stack<Node>();
093        }
094
095        // -------------- ACCESSORS --------------
096
097        /**
098         * Get the root (the first node) of the path.
099         * 
100         * @return the root of the path.
101         */
102        public Node getRoot() {
103                return this.root;
104        }
105
106        /**
107         * Set the root (first node) of the path.
108         * 
109         * @param root
110         *            The root of the path.
111         */
112        public void setRoot(Node root) {
113                if (this.root == null) {
114                        this.root = root;
115                        nodePath.push(root);
116                } else {
117                        System.err
118                                        .printf("Error in org.miv.graphstream.graph.Path: root is not null. First use the clear method.%n");
119                }
120
121        }
122
123        /**
124         * Says whether the path contains this node or not.
125         * 
126         * @param node
127         *            The node tested for existence in the path.
128         * @return <code>true</code> if the path contains the node.
129         */
130        public boolean contains(Node node) {
131                return nodePath.contains(node);
132        }
133
134        /**
135         * Says whether the path contains this edge or not.
136         * 
137         * @param edge
138         *            The edge tested for existence in the path.
139         * @return <code>true</code> if the path contains the edge.
140         */
141        public boolean contains(Edge edge) {
142                return edgePath.contains(edge);
143        }
144
145        /**
146         * Returns true if the path is empty.
147         * 
148         * @return <code>true</code> if the path is empty.
149         */
150        public boolean empty() {
151                return nodePath.empty();
152        }
153
154        /**
155         * Returns the size of the path
156         */
157        public int size() {
158                return nodePath.size();
159        }
160
161        /**
162         * It returns the sum of the <code>characteristic</code> given value in the
163         * Edges of the path.
164         * 
165         * @param characteristic
166         *            The characteristic.
167         * @return Sum of the characteristics.
168         */
169        public Double getPathWeight(String characteristic) {
170                double d = 0;
171                for (Edge l : edgePath) {
172                        d += (Double) l.getAttribute(characteristic, Number.class);
173                }
174                return d;
175        }
176
177        /**
178         * Returns the list of edges representing the path.
179         * 
180         * @return The list of edges representing the path.
181         */
182        public List<Edge> getEdgePath() {
183                return edgePath;
184        }
185
186        /**
187         * Construct an return a list of nodes that represents the path.
188         * 
189         * @return A list of nodes representing the path.
190         */
191        public List<Node> getNodePath() {
192                return nodePath;
193        }
194
195        // -------------- MODIFIERS -------------
196
197        /**
198         * Method that adds a node (and an edge) to the path. Parameters are the
199         * start node : the one who already belong to the path or the first one if
200         * the path is empty. The other parameter is the the new edge to add.
201         * 
202         * @param from
203         *            The start node.
204         * @param edge
205         *            The edge used.
206         */
207        public void add(Node from, Edge edge) {
208                if (root == null) {
209                        if (from == null) {
210                                System.err
211                                                .print("Error using org.miv.graphstream.graph.Path: Use setRoot( ) first. %n");
212                                System.exit(0);
213                        } else {
214                                setRoot(from);
215                        }
216                }
217
218                if (from == null) {
219                        from = nodePath.peek();
220                }
221
222                if (nodePath.size() == 1
223                                || ((nodePath.peek() == from) && (from == edgePath.peek()
224                                                .getSourceNode() || from == edgePath.peek()
225                                                .getTargetNode()))) {
226
227                        nodePath.push(edge.getOpposite(from));
228                        edgePath.push(edge);
229                } else {
230                        System.err
231                                        .printf("Path: Cannot add the specified edge, it cannot be part of the path! %n");
232                }
233        }
234
235        /**
236         * Method that adds an edge an a node to the path. The new edge to add is
237         * given.
238         * 
239         * @param edge
240         *            The edge to add to the path.
241         */
242        public void add(Edge edge) {
243                if (nodePath.isEmpty()) {
244                        add(null, edge);
245                } else {
246                        add(nodePath.peek(), edge);
247                }
248        }
249
250        /**
251         * A synonym for {@link #add(Edge)}.
252         */
253        public void push(Node from, Edge edge) {
254                add(from, edge);
255        }
256
257        /**
258         * A synonym for {@link #add(Edge)}.
259         */
260        public void push(Edge edge) {
261                add(edge);
262        }
263
264        /**
265         * This methods pops the 2 stacks (<code>edgePath</code> and
266         * <code>nodePath</code>) and returns the removed edge.
267         * 
268         * @return The edge that have just been removed.
269         */
270        public Edge popEdge() {
271                nodePath.pop();
272                return edgePath.pop();
273        }
274
275        /**
276         * This methods pops the 2 stacks (<code>edgePath</code> and
277         * <code>nodePath</code>) and returns the removed node.
278         * 
279         * @return The node that have just been removed.
280         */
281        public Node popNode() {
282                edgePath.pop();
283                return nodePath.pop();
284        }
285
286        /**
287         * Looks at the node at the top of the stack without removing it from the
288         * stack.
289         * 
290         * @return The node at the top of the stack.
291         */
292        public Node peekNode() {
293                return nodePath.peek();
294        }
295
296        /**
297         * Looks at the edge at the top of the stack without removing it from the
298         * stack.
299         * 
300         * @return The edge at the top of the stack.
301         */
302
303        public Edge peekEdge() {
304                return edgePath.peek();
305        }
306
307        /**
308         * Clears the path;
309         */
310        public void clear() {
311                nodePath.clear();
312                edgePath.clear();
313                // Runtime.getRuntime().gc();
314                root = null;
315        }
316
317        /**
318         * Get a copy of this path
319         * 
320         * @return A copy of this path.
321         */
322        @SuppressWarnings("unchecked")
323        public Path getACopy() {
324                Path newPath = new Path();
325                newPath.root = this.root;
326                newPath.edgePath = (Stack<Edge>) edgePath.clone();
327                newPath.nodePath = (Stack<Node>) nodePath.clone();
328
329                return newPath;
330        }
331
332        /**
333         * Remove all parts of the path that start at a given node and pass a new at
334         * this node.
335         */
336        public void removeLoops() {
337                int n = nodePath.size();
338                /*
339                 * System.err.printf( "removeLoop()%n" ); System.err.printf(
340                 * "  path size = %d==%d%n  [ ", n, edgePath.size() );
341                 * 
342                 * for( int i=0; i<n; i++ ) { System.err.printf( "%d=%s ", i,
343                 * nodePath.get(i).getId() ); } System.err.printf( "]%n" );
344                 */
345                // For each node-edge pair
346                for (int i = 0; i < n; i++) {
347                        // Lookup each other following node. We start
348                        // at the end to find the largest loop possible.
349                        for (int j = n - 1; j > i; j--) {
350                                // If another node match, this is a loop.
351                                if (nodePath.get(i) == nodePath.get(j)) {
352                                        // We found a loop between i and j.
353                                        // Remove ]i,j].
354                                        // System.err.printf( "removed ]%d,%d]%n", i, j );
355                                        for (int k = i + 1; k <= j; k++) {
356                                                nodePath.remove(i + 1);
357                                                edgePath.remove(i);
358                                        }
359                                        n -= (j - i);
360                                        j = i; // To stop the search.
361                                }
362                        }
363                }
364                /*
365                 * System.err.printf( "  NEW path size = %d==%d%n  NEW [ ", n,
366                 * edgePath.size() );
367                 * 
368                 * for( int i=0; i<n; i++ ) { System.err.printf( "%d=%s ", i,
369                 * nodePath.get(i).getId() ); } System.err.printf( "]%n" );
370                 */}
371
372        /**
373         * Compare the content of the current path and the specified path to decide
374         * weather they are equal or not.
375         * 
376         * @param p
377         *            A path to compare to the curent one.
378         * @return True if both paths are equal.
379         */
380        public boolean equals(Path p) {
381                if (nodePath.size() != p.nodePath.size()) {
382                        return false;
383                } else {
384                        for (int i = 0; i < nodePath.size(); i++) {
385                                if (nodePath.get(i) != p.nodePath.get(i)) {
386                                        return false;
387                                }
388                        }
389                }
390                return true;
391        }
392
393        // ------------ UTILITY METHODS ------------
394
395        /**
396         * Returns a String description of the path.
397         * 
398         * @return A String representation of the path.
399         */
400        @Override
401        public String toString() {
402                return nodePath.toString();
403        }
404
405        /**
406         * Returns the size of the path. Identical to {@link #size()}.
407         * 
408         * @return The size of the path.
409         */
410        public int getNodeCount() {
411                return nodePath.size();
412        }
413
414        /*
415         * (non-Javadoc)
416         * 
417         * @see org.graphstream.graph.Structure#getEdgeCount()
418         */
419        public int getEdgeCount() {
420                return edgePath.size();
421        }
422
423        /*
424         * (non-Javadoc)
425         * 
426         * @see org.graphstream.graph.Structure#getNodeIterator()
427         */
428        @SuppressWarnings("unchecked")
429        public <T extends Node> Iterator<T> getNodeIterator() {
430                return (Iterator<T>) nodePath.iterator();
431        }
432
433        /*
434         * (non-Javadoc)
435         * 
436         * @see org.graphstream.graph.Structure#getEdgeIterator()
437         */
438        @SuppressWarnings("unchecked")
439        public <T extends Edge> Iterator<T> getEdgeIterator() {
440                return (Iterator<T>) edgePath.iterator();
441        }
442
443        /*
444         * (non-Javadoc)
445         * 
446         * @see org.graphstream.graph.Structure#getEachNode()
447         */
448        @SuppressWarnings("unchecked")
449        public <T extends Node> Iterable<? extends T> getEachNode() {
450                return (Iterable<? extends T>) nodePath;
451        }
452
453        /*
454         * (non-Javadoc)
455         * 
456         * @see org.graphstream.graph.Structure#getEachEdge()
457         */
458        @SuppressWarnings("unchecked")
459        public <T extends Edge> Iterable<? extends T> getEachEdge() {
460                return (Iterable<? extends T>) edgePath;
461        }
462
463        /*
464         * (non-Javadoc)
465         * 
466         * @see org.graphstream.graph.Structure#getNodeSet()
467         */
468        @SuppressWarnings("unchecked")
469        public <T extends Node> Collection<T> getNodeSet() {
470                return (Collection<T>) nodePath;
471        }
472
473        /*
474         * (non-Javadoc)
475         * 
476         * @see org.graphstream.graph.Structure#getEdgeSet()
477         */
478        @SuppressWarnings("unchecked")
479        public <T extends Edge> Collection<T> getEdgeSet() {
480                return (Collection<T>) edgePath;
481        }
482}