001/* A graph with optionally-weighted nodes and edges.
002
003 Copyright (c) 1997-2014 The Regents of the University of California.
004 All rights reserved.
005 Permission is hereby granted, without written agreement and without
006 license or royalty fees, to use, copy, modify, and distribute this
007 software and its documentation for any purpose, provided that the above
008 copyright notice and the following two paragraphs appear in all copies
009 of this software.
010
011 IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY
012 FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
013 ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
014 THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF
015 SUCH DAMAGE.
016
017 THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
018 INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
019 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE
020 PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF
021 CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES,
022 ENHANCEMENTS, OR MODIFICATIONS.
023
024 PT_COPYRIGHT_VERSION_2
025 COPYRIGHTENDKEY
026
027
028 */
029package ptolemy.graph;
030
031import java.util.ArrayList;
032import java.util.Collection;
033import java.util.Collections;
034import java.util.HashMap;
035import java.util.HashSet;
036import java.util.Iterator;
037
038import ptolemy.graph.analysis.Analysis;
039import ptolemy.graph.analysis.SelfLoopAnalysis;
040import ptolemy.graph.analysis.strategy.CachedStrategy;
041
042///////////////////////////////////////////////////////////////////
043//// Graph
044
045/**
046 A graph with optionally-weighted nodes and edges.
047
048 <p>Each node or edge may have a weight associated with it
049 (see {@link Edge} and {@link Node}).
050 The nodes (edges) in a graph are always distinct, but their weights
051 need not be.
052
053 <p>Each node (edge) has a unique, integer label associated with it.
054 These labels can be used, for example, to index arrays and matrixes
055 whose rows/columns correspond to nodes (edges). See {@link #nodeLabel(Node)}
056 ({@link #edgeLabel(Edge)}) for details.
057
058 <p>Both directed and undirected graphs can be implemented using this
059 class. In directed graphs, the order of nodes specified to the
060 <code>addEdge</code> method is relevant, whereas in undirected graphs, the
061 order is unimportant. Support for both undirected and directed graphs
062 follows from the combined support for these in the underlying {@link
063 Node} and {@link Edge} classes. For more thorough support for directed
064 graphs, see {@link DirectedGraph}.
065
066 <p>The same node can exist in multiple graphs, but any given graph can
067 contain only one instance of the node. Node labels, however, are local to
068 individual graphs. Thus, the same node may have different labels in different
069 graphs.
070 Furthermore, the label assigned in a given graph to a node may change over
071 time (if the set of nodes in the graph changes). If a node is contained in
072 multiple graphs, it has the same weight in all of the graphs.
073 All of this holds for edges
074 as well. The same weight may be shared among multiple nodes and edges.
075
076 <p> Multiple edges in a graph can connect the same pair of nodes.
077 Thus, multigraphs are supported.
078
079 <p>Once assigned, node and edge weights should not be changed in ways that
080 affect comparison under the <code>equals</code> method.
081 Otherwise, unpredictable behavior may result.
082
083 <p>In discussions of complexity, <em>n</em> and <em>e</em> refers to the
084 number of graph nodes and edges, respectively.
085
086 <p>In derived classes, the following methods need special
087 attention regarding whether or not they should be overridden:
088 <br>{@link #validEdgeWeight(Object)} {@link #validNodeWeight(Object)}
089
090 @author Shuvra S. Bhattacharyya, Ming-Yung Ko, Fuat Keceli,
091 Shahrooz Shahparnia, Yuhong Xiong, Jie Liu.
092 @version $Id$
093 @since Ptolemy II 0.2
094 @Pt.ProposedRating Red (cxh)
095 @Pt.AcceptedRating Red (cxh)
096 @see ptolemy.graph.Edge
097 @see ptolemy.graph.Node
098 */
099public class Graph implements Cloneable {
100    /** Construct an empty graph.
101     */
102    public Graph() {
103        _nodes = new ElementList("node", this);
104        _edges = new ElementList("edge", this);
105        _initializeAnalyses();
106        _hiddenEdgeSet = new HashSet();
107        _incidentEdgeMap = new HashMap();
108    }
109
110    /** Construct an empty graph with enough storage allocated for the
111     *  specified number of nodes.  Memory management is more
112     *  efficient with this constructor if the number of nodes is
113     *  known.
114     *  @param nodeCount The number of nodes.
115     */
116    public Graph(int nodeCount) {
117        _nodes = new ElementList("node", this, nodeCount);
118        _edges = new ElementList("edge", this);
119        _initializeAnalyses();
120        _hiddenEdgeSet = new HashSet();
121        _incidentEdgeMap = new HashMap(nodeCount);
122    }
123
124    /** Construct an empty graph with enough storage allocated for the
125     *  specified number of edges, and number of nodes.  Memory
126     *  management is more efficient with this constructor if the
127     *  number of nodes and edges is known.
128     *  @param nodeCount The number of nodes.
129     *  @param edgeCount The number of edges.
130     */
131    public Graph(int nodeCount, int edgeCount) {
132        _nodes = new ElementList("node", this, nodeCount);
133        _edges = new ElementList("edge", this, edgeCount);
134        _initializeAnalyses();
135        _hiddenEdgeSet = new HashSet();
136        _incidentEdgeMap = new HashMap(nodeCount);
137    }
138
139    ///////////////////////////////////////////////////////////////////
140    ////                         public methods                    ////
141
142    /** Add an analysis to the list of analyses that this graph is associated
143     *  with. This method is called by {@link ptolemy.graph.analysis.Analysis}
144     *  when an analysis is created, and normally should not be called
145     *  elsewhere.
146     *
147     *  @param analysis The analysis.
148     *  @exception IllegalArgumentException If the graph associated with the
149     *  analysis is not equal to this graph, or if the graph already contains
150     *  the analysis in its list of analyses.
151     */
152    public void addAnalysis(Analysis analysis) {
153        if (analysis.graph() != this) {
154            throw new IllegalArgumentException("Invalid associated graph.\n"
155                    + "The analysis:\n" + analysis + "\n");
156        }
157
158        if (_analysisList.contains(analysis)) {
159            throw new IllegalArgumentException("Attempt to add "
160                    + "duplicate analysis.\nThe analysis:\n" + analysis);
161        }
162
163        _analysisList.add(analysis);
164    }
165
166    /** Add a weighted edge between two nodes.  If the edge is subsequently
167     *  operated on as a directed edge, its orientation will be taken
168     *  to be directed <i>from</i> the first (<code>node1</code>) node
169     *  <i>to</i> the second (<code>node2</code>) node. Multiple edges
170     *  between the same nodes are allowed, and are considered
171     *  different edges.  Self-loops are also allowed.
172     *
173     *  @param node1 The first node.
174     *  @param node2 The second node.
175     *  @param weight The weight.
176     *  @return The edge.
177     *  @exception GraphElementException If the first node or second
178     *  node is not already in the graph.
179     *  @exception NullPointerException If the weight is <code>null</code>.
180     */
181    public Edge addEdge(Node node1, Node node2, Object weight) {
182        return _addEdge(node1, node2, true, weight);
183    }
184
185    /** Add an unweighted edge between two nodes. Operation is the same as in
186     *  {@link #addEdge(Node, Node, Object)}, except that no
187     *  weight is assigned to the edge.
188     *
189     *  @param node1 The first node.
190     *  @param node2 The second node.
191     *  @return The edge.
192     *  @exception GraphElementException If the first node or second
193     *  node is not already in the graph.
194     */
195    public Edge addEdge(Node node1, Node node2) {
196        return _addEdge(node1, node2, false, null);
197    }
198
199    /** Given two node weights <i>w1</i> and <i>w2</i>, add weighted
200     *  edges of the form (<i>x1</i>, <i>x2</i>), where
201     *  <code>(x1.getWeight() == w1) &amp;&amp; (x2.getWeight() == w2)</code>.
202     *
203     *  @param weight1 The first node weight.
204     *  @param weight2 The second node weight.
205     *  @param newEdgeWeight The weight to assign to each new edge.
206     *  @return The set of edges that were added; each element
207     *  of this set is an instance of {@link Edge}.
208     *  @exception GraphElementException If no edge is
209     *  added (i.e., if no nodes x1, x2 satisfy the above condition).
210     */
211    public Collection addEdge(Object weight1, Object weight2,
212            Object newEdgeWeight) {
213        return _addEdges(weight1, weight2, true, newEdgeWeight);
214    }
215
216    /** Given two node weights <i>w1</i> and <i>w2</i>, add all unweighted
217     *  edges of the form (<i>x1</i>, <i>x2</i>), where
218     *  <code>(x1.getWeight() == w1) &amp;&amp; (x2.getWeight() == w2)</code>.
219     *
220     *  @param weight1 The first node weight.
221     *  @param weight2 The second node weight.
222     *  @return The set of edges that were added; each element
223     *  of this set is an instance of {@link Edge}.
224     *  @exception GraphElementException If no edge is
225     *  added (i.e., if no nodes x1, x2 satisfy the above condition).
226     */
227    public Collection addEdge(Object weight1, Object weight2) {
228        return _addEdges(weight1, weight2, false, null);
229    }
230
231    /** Add a pre-constructed edge (unweighted or weighted).
232     *
233     *  @param edge The edge.
234     *  @return The edge.
235     *  @exception GraphElementException If the source or sink node
236     *  of the edge is not already in the graph.
237     *  @exception GraphConstructionException If the edge is already in
238     *  the graph, or if the edge is hidden in the graph.
239     *  @see #hideEdge(Edge)
240     */
241    public Edge addEdge(Edge edge) {
242        if (!containsNode(edge.source())) {
243            throw new GraphElementException(edge, this,
244                    "The source node is not in the graph.");
245        } else if (!containsNode(edge.sink())) {
246            throw new GraphElementException(edge, this,
247                    "The sink node is not in the graph.");
248        } else if (containsEdge(edge)) {
249            throw new GraphConstructionException(
250                    "Attempt to add an edge that " + "is already in the graph."
251                            + GraphException.elementDump(edge, this));
252        } else if (hidden(edge)) {
253            throw new GraphConstructionException("Attempt to add an edge that "
254                    + "is already hidden in the graph."
255                    + GraphException.elementDump(edge, this));
256        } else {
257            _registerEdge(edge);
258            return edge;
259        }
260    }
261
262    /** Add a collection of edges to the graph. Each element in the
263     *  argument collection must be a unique {@link Edge}.
264     *  @param edgeCollection The collection of edges to add.
265     */
266    public void addEdges(Collection edgeCollection) {
267        Iterator edges = edgeCollection.iterator();
268
269        while (edges.hasNext()) {
270            addEdge((Edge) edges.next());
271        }
272    }
273
274    /** Add a given graph to this graph. This base class method simply
275     *  adds all nodes and edges in the given graph to this graph. If a derived
276     *  class contains extra fields associated with
277     *  edges, nodes and the graph itself, it can override this method to
278     *  handle those fields. This method does not add hidden edges of
279     *  the argument graph to this graph.
280     *  @param graph The graph to add.
281     *  @return True if this graph changed as a result of the call.
282     */
283    public boolean addGraph(Graph graph) {
284        addNodes(graph.nodes());
285        addEdges(graph.edges());
286        return graph.nodeCount() + graph.edgeCount() > 0;
287    }
288
289    /** Add an unweighted node to this graph.
290     *  @return The node.
291     */
292    public Node addNode() {
293        Node node = new Node();
294        _registerNode(node);
295        return node;
296    }
297
298    /** Add a pre-constructed node (unweighted or weighted).
299     *
300     *  @param node The node.
301     *  @return The node.
302     *  @exception GraphConstructionException If the node is already
303     *  in the graph.
304     *  @exception GraphWeightException If the weight is invalid.
305     */
306    public Node addNode(Node node) {
307        if (containsNode(node)) {
308            throw new GraphConstructionException("Attempt to add a node "
309                    + "that is already contained in the graph."
310                    + GraphException.elementDump(node, this));
311        } else {
312            _registerNode(node);
313            return node;
314        }
315    }
316
317    /** Add a new weighted node to this graph given the node weight.
318     *
319     *  @param weight The node weight.
320     *  @return The node.
321     *  @exception GraphWeightException If the specified weight is null.
322     */
323    public Node addNodeWeight(Object weight) {
324        if (weight == null) {
325            throw new NullPointerException("weight == null");
326        }
327        Node node = new Node(weight);
328        _registerNode(node);
329        return node;
330    }
331
332    /** Add a collection of nodes to the graph.
333     *  Each element of the collection is interpreted
334     *  as a weight of a new node to add in the graph.
335     *  @param weightCollection The collection of node weights; each element
336     *  is an instance of {@link Object}.
337     *  @return The set of nodes that that were added; each element
338     *  is an instance of {@link Node}.
339     */
340    public Collection addNodeWeights(Collection weightCollection) {
341        ArrayList nodes = new ArrayList();
342        Iterator weights = weightCollection.iterator();
343
344        while (weights.hasNext()) {
345            nodes.add(addNodeWeight(weights.next()));
346        }
347
348        return nodes;
349    }
350
351    /** Add a collection of nodes to the graph. Each element in the
352     *  argument collection must be a unique {@link Node}.
353     *  @param nodeCollection The collection of nodes to add.
354     */
355    public void addNodes(Collection nodeCollection) {
356        Iterator nodes = nodeCollection.iterator();
357
358        while (nodes.hasNext()) {
359            addNode((Node) nodes.next());
360        }
361    }
362
363    /** Return the present value of a counter that keeps track
364     *  of changes to the graph.
365     *  This counter is monitored by {@link Analysis}s to determine
366     *  if associated computations are obsolete. Upon overflow, the counter
367     *  resets to zero, broadcasts a change to all graph analyses, and
368     *  begins counting again.
369     *  @return The present value of the counter.
370     */
371    public long changeCount() {
372        return _changeCount;
373    }
374
375    /** Return a clone of this graph. The clone has the same set of
376     *  nodes and edges. Changes to the node or edge weights
377     *  affect the clone simultaneously. However,
378     *  modifications to the graph topology make the clone different from
379     *  this graph (e.g., they are no longer equal (see
380     *  {@link #equals(Object)})).
381     *
382     *  @return The clone graph.
383     */
384    @Override
385    public Object clone() {
386        return cloneAs(this);
387    }
388
389    /** Return a clone of this graph in the form of the argument graph type
390     *  (i.e., the run-time type of the returned graph is that of the
391     *  argument graph). The clone has the
392     *  same set of nodes and edges. Changes to the node or edge weights
393     *  affect the
394     *  clone simultaneously. If the run-time type of the argument graph is
395     *  equal to that of this graph, then the clone is equal to this
396     *  graph, as determined by {@link #equals(Object)}. However,
397     *  modifications to the graph topology
398     *  make the clone not equal to this graph.
399     *
400     *  @param graph The graph that gives the run-time type of the clone.
401     *  @return A clone of this graph.
402     */
403    public Graph cloneAs(Graph graph) {
404        Graph cloneGraph = graph._emptyGraph();
405
406        // copy nodes and edges
407        Iterator nodes = nodes().iterator();
408
409        while (nodes.hasNext()) {
410            cloneGraph.addNode((Node) nodes.next());
411        }
412
413        Iterator edges = edges().iterator();
414
415        while (edges.hasNext()) {
416            cloneGraph.addEdge((Edge) edges.next());
417        }
418
419        return cloneGraph;
420    }
421
422    /** Return the connected components of the graph. The connected
423     *  components are returned as a Collection, where each element
424     *  of the Collection is a Collection of Nodes.
425     *  @return The connected components.
426     */
427    public Collection connectedComponents() {
428        // We divide the set of nodes into disjoint subsets called 'components'.
429        // These components are repeatedly modified until they coincide with
430        // the connected components. The following HashMap is a map from
431        // nodes into the components that contain them. Each element in the map
432        // is a Node whose weight is an ArrayList of Nodes. We encapsulate
433        // each ArrayList as the weight of a Node (called the 'container' of
434        // the ArrayList) so that we can modify the ArrayList without
435        // interfering with the hashing semantics of the HashMap.
436        HashMap componentMap = new HashMap(nodeCount());
437        HashSet components = new HashSet(nodeCount());
438        Iterator nodes = nodes().iterator();
439
440        while (nodes.hasNext()) {
441            Node node = (Node) nodes.next();
442            ArrayList component = new ArrayList();
443            component.add(node);
444
445            Node container = new Node(component);
446            componentMap.put(node, container);
447            components.add(container);
448        }
449
450        Iterator edges = edges().iterator();
451
452        while (edges.hasNext()) {
453            Edge edge = (Edge) edges.next();
454            Node sourceContainer = (Node) componentMap.get(edge.source());
455            Node sinkContainer = (Node) componentMap.get(edge.sink());
456            ArrayList sourceSet = (ArrayList) sourceContainer.getWeight();
457            ArrayList sinkSet = (ArrayList) sinkContainer.getWeight();
458
459            if (sourceSet != sinkSet) {
460                // Construct the union of the two components in the source set.
461                components.remove(sinkContainer);
462
463                Iterator moveNodes = sinkSet.iterator();
464
465                while (moveNodes.hasNext()) {
466                    Node moveNode = (Node) moveNodes.next();
467                    componentMap.put(moveNode, sourceContainer);
468                    sourceSet.add(moveNode);
469                }
470            }
471        }
472
473        // Before returning the result, do away with the container that
474        // encapsulates each connected component.
475        ArrayList result = new ArrayList(components.size());
476        Iterator connectedComponents = components.iterator();
477
478        while (connectedComponents.hasNext()) {
479            result.add(((Node) connectedComponents.next()).getWeight());
480        }
481
482        return result;
483    }
484
485    /** Return true if the specified edge exists in the graph, and the
486     *  edge is not hidden in the graph.
487     *  @param edge The specified edge.
488     *  @return True if the specified edge exists in the graph and is not
489     *  hidden.
490     *  @see #hidden(Edge)
491     *  @see #hideEdge(Edge)
492     */
493    public boolean containsEdge(Edge edge) {
494        return _edges.contains(edge) && !hidden(edge);
495    }
496
497    /** Test if the specified object is an edge weight in this
498     *  graph. Equality is
499     *  determined by the <code>equals</code> method. If the specified
500     *  edge weight is null, return false.
501     *
502     *  @param weight The edge weight to be tested.
503     *  @return True if the specified object is an edge weight in this graph.
504     */
505    public boolean containsEdgeWeight(Object weight) {
506        // FIXME: on null, return true if there is an unweighted element.
507        return _edges.containsWeight(weight);
508    }
509
510    /** Return True if the specified node exists in the
511     *  graph.
512     *  @param node The specified node.
513     *  @return True if the specified node exists in the
514     *  graph.
515     */
516    public boolean containsNode(Node node) {
517        return _nodes.contains(node);
518    }
519
520    /** Test if the specified object is a node weight in this
521     *  graph. Equality is
522     *  determined by the <code>equals</code> method. If the specified
523     *  weight is null, return false.
524     *
525     *  @param weight The node weight to be tested.
526     *  @return True if the specified object is a node weight in this graph.
527     */
528    public boolean containsNodeWeight(Object weight) {
529        // FIXME: on null, return true if there is an unweighted element.
530        return _nodes.containsWeight(weight);
531    }
532
533    /** Return an edge in this graph that has a specified weight. If multiple
534     *  edges have the specified weight, then return one of them
535     *  arbitrarily. If the specified weight is null, return an unweighted
536     *  edge (again arbitrarily chosen if there are multiple unweighted
537     *  edges).
538     *  @param weight The specified edge weight.
539     *  @return An edge that has this weight.
540     *  @exception GraphWeightException If the specified weight
541     *  is not an edge weight in this graph or if the specified weight
542     *  is null but the graph does not contain any unweighted edges.
543     */
544    public Edge edge(Object weight) {
545        return (Edge) _edges.element(weight);
546    }
547
548    /** Return an edge in this graph given the edge label;
549     *  the returned edge may be hidden see {@link #hideEdge(Edge)}.
550     *  @param label The edge label.
551     *  @return The edge.
552     *  @exception GraphElementException If the label is not associated
553     *  with an edge in this graph.
554     *  @see #edgeLabel(Edge)
555     */
556    public Edge edge(int label) {
557        return (Edge) _edges.get(label);
558    }
559
560    /** Return the total number of edges in this graph.  Multiple
561     *  connections between two nodes are counted multiple times.
562     *  Hidden edges are not included in this count.
563     *  @return The total number of edges in this graph.
564     */
565    public int edgeCount() {
566        return _edges.size() - _hiddenEdgeSet.size();
567    }
568
569    /** Return the edge label of the specified edge.
570     *  The edge label is a unique integer from 0 through
571     *  <i>E</i>-1, where <i>E</i> is the number of edges
572     *  currently in the graph. Edge labels maintain their
573     *  consistency (remain constant) during periods when
574     *  no edges are removed from the graph. When edges are removed,
575     *  the labels assigned to the remaining edges may change.
576     *
577     *  @param edge A graph edge.
578     *  @return The edge label.
579     *  @exception GraphElementException If the specified edge is not
580     *  not an edge in this graph.
581     */
582    public int edgeLabel(Edge edge) {
583        return _edges.label(edge);
584    }
585
586    /** Return the edge label of the specified edge given the edge weight.
587     *  If multiple edges have the specified weight, then return one of their
588     *  labels arbitrarily.
589     *
590     *  @param weight The edge weight.
591     *  @return The edge label.
592     *  @exception GraphWeightException If the specified weight is not
593     *  an edge weight in this graph.
594     *  @see #edgeLabel(Edge)
595     */
596    public int edgeLabel(Object weight) {
597        return _edges.label(edge(weight));
598    }
599
600    /** Return the weight of a given edge in the graph given the edge label.
601     *
602     *  @param label The edge label.
603     *  @return The weight of the edge.
604     *  @exception IndexOutOfBoundsException If the label is
605     *  not valid.
606     *  @exception GraphWeightException If the edge corresponding
607     *  to the label is unweighted.
608     *  @see #edgeLabel(Edge)
609     */
610    public Object edgeWeight(int label) {
611        return ((Edge) _edges.get(label)).getWeight();
612    }
613
614    /** Return all the edges in this graph in the form of a collection.
615     *  Each element in the returned collection is an instance of {@link Edge}.
616     *  Hidden edges are not included in the returned collection.
617     *  The returned collection cannot be modified.
618     *  This is an <em>O(1)</em> operation if there are no hidden edges;
619     *  otherwise, it is an <em>O(e)</em> operation.
620     *  @return All the edges in this graph.
621     */
622    public Collection edges() {
623        if (hiddenEdgeCount() == 0) {
624            return Collections.unmodifiableList(_edges);
625        }
626
627        // There is at least one hidden edge.
628        int visibleEdgeCount = _edges.size() - hiddenEdgeCount();
629        ArrayList result = new ArrayList(visibleEdgeCount);
630
631        if (visibleEdgeCount == 0) {
632            return Collections.unmodifiableList(result);
633        }
634
635        // There is at least one edge to return.
636        Iterator edges = _edges.iterator();
637
638        while (edges.hasNext()) {
639            Edge edge = (Edge) edges.next();
640
641            if (!hidden(edge)) {
642                result.add(edge);
643            }
644        }
645
646        return Collections.unmodifiableList(result);
647    }
648
649    /** Return all the edges in this graph that have a specified weight.
650     *  The edges are returned in the form of a collection.
651     *  If the specified weight is null, return all the unweighted edges.
652     *  If no edges have the specified weight (or if the argument is null and
653     *  there are no unweighted edges), return an empty collection.
654     *  Each element in the returned collection is an instance of {@link Node}.
655     *  @param weight The specified weight.
656     *  @return The edges in this graph that have the specified weight.
657     */
658    public Collection edges(Object weight) {
659        // Hidden edges will not be included since their weights are
660        // disassociated in the element list.
661        return _edges.elements(weight);
662    }
663
664    /** Return all the edges in this graph whose weights are contained
665     *  in a specified collection.
666     *  The edges are returned in the form of a collection.
667     *  Each element in the returned collection is an instance of
668     *  {@link Edge}. A null element in the argument collection is interpreted
669     *  to mean that all unweighted edges are to be included in the result.
670     *  Duplicate weights or null elements in the specified collection result
671     *  in duplicate edges in the returned collection.
672     *  Non-null elements in the argument collection that are not edge weights
673     *  are ignored.
674     *  @param collection The specified collection of weights.
675     *  @return The edges in this graph whose weights are contained
676     *  in the specified collection.
677     *  @see #edges(Object)
678     */
679    public Collection edges(Collection collection) {
680        // Hidden edges will not be included since they are removed from
681        // the weight map.
682        ArrayList edges = new ArrayList();
683        Iterator weights = collection.iterator();
684
685        while (weights.hasNext()) {
686            edges.addAll(edges(weights.next()));
687        }
688
689        return Collections.unmodifiableCollection(edges);
690    }
691
692    /** Test if a graph is equal to this one. It is equal
693     *  if it is of the same class, and has the same sets of nodes
694     *  and edges.
695     *
696     *  <p> Derived graph classes may override this method if
697     *  there is additional information in the graphs (beyond nodes
698     *  and edges) that is relevant to equality.
699     *
700     *  @param graph The graph with which to compare this graph.
701     *  @return True if the graph is equal to this one.
702     *  @see #hashCode()
703     */
704    @Override
705    public boolean equals(Object graph) {
706        if (graph == null) {
707            return false;
708        }
709
710        if (graph.getClass() != getClass()) {
711            return false;
712        }
713
714        Graph argumentGraph = (Graph) graph;
715
716        if (argumentGraph.nodeCount() != nodeCount()
717                || argumentGraph.edgeCount() != edgeCount()) {
718            return false;
719        }
720
721        Iterator argumentNodes = argumentGraph.nodes().iterator();
722
723        while (argumentNodes.hasNext()) {
724            if (!containsNode((Node) argumentNodes.next())) {
725                return false;
726            }
727        }
728
729        Iterator argumentEdges = argumentGraph.edges().iterator();
730
731        while (argumentEdges.hasNext()) {
732            if (!containsEdge((Edge) argumentEdges.next())) {
733                return false;
734            }
735        }
736
737        return true;
738    }
739
740    /** Returns the hash code for this graph. The hash code is the
741     *  sum of the hash codes of the nodes and edges.
742     *
743     *  <p> Derived graph classes may override this method if
744     *  there is additional information in the graphs (beyond nodes
745     *  and edges) that is relevant to equality between graphs.
746     *
747     *  @return The hash code for this graph.
748     *  @see #equals(Object)
749     */
750    @Override
751    public int hashCode() {
752        int code = getClass().getName().hashCode();
753        Iterator nodes = nodes().iterator();
754
755        while (nodes.hasNext()) {
756            code += nodes.next().hashCode();
757        }
758
759        Iterator edges = edges().iterator();
760
761        while (edges.hasNext()) {
762            code += edges.next().hashCode();
763        }
764
765        return code;
766    }
767
768    /** Return true if a given edge is hidden in this graph.
769     *  @param edge The given edge.
770     *  @return True if the edge is hidden in this graph.
771     */
772    public boolean hidden(Edge edge) {
773        return _hiddenEdgeSet.contains(edge);
774    }
775
776    /** Return the number of hidden edges in the graph.
777     *  @return The number of hidden edges.
778     */
779    public int hiddenEdgeCount() {
780        return _hiddenEdgeSet.size();
781    }
782
783    /** Return all the hidden edges in this graph in the form of a collection.
784     *  Each element in the returned collection is an instance of {@link Edge}.
785     *  This is an <em>O(1)</em> operation.
786     *  @return All the hidden edges in this graph.
787     */
788    public Collection hiddenEdges() {
789        return Collections.unmodifiableCollection(_hiddenEdgeSet);
790    }
791
792    /** Hide an edge if the edge exists in the graph and is not already hidden.
793     *  This method removes an edge from the graph, including
794     *  removal from the incidence lists of the source and sink nodes, but
795     *  preserves the allocation of the edge label to the edge. This
796     *  makes the operation more efficient than standard edge removal
797     *  {@link #removeEdge(Edge)}, and
798     *  allows the same label to be used if the edge is restored later.
799     *  This is an <em>O(1)</em> operation.
800     *  @param edge The edge to hide.
801     *  @return True if the edge was in the graph and not already hidden.
802     *  @see #restoreEdge(Edge)
803     */
804    public boolean hideEdge(Edge edge) {
805        if (!containsEdge(edge)) {
806            return false;
807        }
808
809        if (_hiddenEdgeSet.add(edge)) {
810            _disconnectEdge(edge);
811            return true;
812        } else {
813            // The edge is already hidden.
814            return false;
815        }
816    }
817
818    /** Return the number of edges that are incident to a specified node.
819     *  @param node The node.
820     *  @return The number of incident edges.
821     *  @exception GraphElementException If the specified node is not in
822     *  the graph.
823     */
824    public int incidentEdgeCount(Node node) {
825        GraphElementException.checkNode(node, this);
826        return _incidentEdgeList(node).size();
827    }
828
829    /** Return the set of incident edges for a specified node. Each element in
830     *  the returned set is an {@link Edge}.
831     *
832     *  @param node The specified node.
833     *  @return The set of incident edges.
834     *  @exception GraphElementException If the specified node is not in
835     *  the graph.
836     */
837    public Collection incidentEdges(Node node) {
838        GraphElementException.checkNode(node, this);
839        return Collections.unmodifiableList(_incidentEdgeList(node));
840    }
841
842    /** Return the collection of edges that make a node node2 a neighbor of a
843     *  node node1. In other words, return the set of edges that are incident to
844     *  both node1 and node2. Each element of the returned collection is an
845     *  instance of {@link Edge}.
846     *  @param node1 The node node1.
847     *  @param node2 The node node2.
848     *  @return The collection of edges that make node2 a neighbor of node1.
849     *  @see DirectedGraph#predecessorEdges(Node, Node)
850     *  @see DirectedGraph#successorEdges(Node, Node)
851     *  @exception GraphElementException If node1 or node2 is not in this
852     *  graph.
853     */
854    public Collection neighborEdges(Node node1, Node node2) {
855        // Method incidentEdges will validate existence of node1 in the graph.
856        Collection edgeCollection = incidentEdges(node1);
857        GraphElementException.checkNode(node2, this);
858
859        Iterator edges = edgeCollection.iterator();
860        ArrayList commonEdges = new ArrayList();
861
862        while (edges.hasNext()) {
863            Edge edge = (Edge) edges.next();
864
865            if (edge.source() == node2) {
866                commonEdges.add(edge);
867            } else if (edge.sink() == node2) {
868                commonEdges.add(edge);
869            }
870        }
871
872        return commonEdges;
873    }
874
875    /** Return all of the neighbors of a given node in the form of a
876     *  a collection. Each element of the collection is a Node.
877     *  A neighbor of a node X is a node that is the sink
878     *  of an edge whose source is X, or the source of a node whose sink
879     *  is node X. In other words, a neighbor of X is a node that is adjacent
880     *  to X. All elements in the returned collection are unique nodes.
881     *  @param node The node whose neighbors are to be returned.
882     *  @return The neighbors of the node as a collection.
883     */
884    public Collection neighbors(Node node) {
885        // Method incidentEdges will validate existence of node in the graph.
886        Collection incidentEdgeCollection = incidentEdges(node);
887        Iterator incidentEdges = incidentEdgeCollection.iterator();
888        ArrayList result = new ArrayList(incidentEdgeCollection.size());
889
890        while (incidentEdges.hasNext()) {
891            Edge edge = (Edge) incidentEdges.next();
892            Node sink = edge.sink();
893            Node source = edge.source();
894
895            if (source == node) {
896                if (!result.contains(sink)) {
897                    result.add(sink);
898                }
899            } else if (sink == node) {
900                if (!result.contains(source)) {
901                    result.add(source);
902                }
903            }
904        }
905
906        return result;
907    }
908
909    /** Return a node in this graph that has a specified weight. If multiple
910     *  nodes have the specified weight, then return one of them
911     *  arbitrarily. If the specified weight is null, return an unweighted
912     *  node (again arbitrarily chosen if there are multiple unweighted
913     *  nodes).
914     *  @param weight The specified node weight.
915     *  @return A node that has this weight.
916     *  @exception GraphWeightException If the specified weight
917     *  is not a node weight in this graph or if the specified weight
918     *  is null but the graph does not contain any unweighted nodes.
919     */
920    public Node node(Object weight) {
921        return (Node) _nodes.element(weight);
922    }
923
924    /** Return a node in this graph given the node label.
925     *  @param label The node label.
926     *  @return The node.
927     *  @exception IndexOutOfBoundsException If the label is not
928     *  associated with a node in this graph.
929     *  @see #nodeLabel(Node)
930     */
931    public Node node(int label) {
932        return (Node) _nodes.get(label);
933    }
934
935    /** Return the total number of nodes in this graph.
936     *  @return The total number of nodes in this graph.
937     */
938    public int nodeCount() {
939        return _nodes.size();
940    }
941
942    /** Return the node label of the specified node.
943     *  The node label is a unique integer from 0 through
944     *  <i>N</i>-1, where <i>N</i> is the number of nodes
945     *  currently in the graph. Node labels maintain their
946     *  consistency (remain constant) during periods when
947     *  no nodes are removed from the graph. When nodes are removed,
948     *  the labels assigned to the remaining nodes may change.
949     *
950     *  @param node A graph node.
951     *  @return The node label.
952     *  @exception GraphElementException If the specified node is not
953     *  a node in this graph.
954     */
955    public int nodeLabel(Node node) {
956        return _nodes.label(node);
957    }
958
959    /** Return the node label of the specified node given the node weight.
960     *  If multiple nodes have the specified weight, then return one of their
961     *  labels arbitrarily.
962     *
963     *  @param weight The node weight.
964     *  @return The node label.
965     *  @exception GraphWeightException If the specified weight is not
966     *  a node weight in this graph.
967     *  @see #nodeLabel(Node)
968     */
969    public int nodeLabel(Object weight) {
970        return _nodes.label(node(weight));
971    }
972
973    /** Return the weight of a given node in the graph given the node label.
974     *
975     *  @param label The node label.
976     *  @return The weight of the node.
977     *  @exception IndexOutOfBoundsException If the label is
978     *  not valid.
979     *  @exception GraphWeightException If the node corresponding
980     *  to the label is unweighted.
981     *  @see #nodeLabel(Node)
982     */
983    public Object nodeWeight(int label) {
984        return ((Node) _nodes.get(label)).getWeight();
985    }
986
987    /** Return all the nodes in this graph in the form of a collection.
988     *  Each element in the returned collection is an instance of {@link Node}.
989     *  @return All the nodes in this graph.
990     */
991    public Collection nodes() {
992        return Collections.unmodifiableList(_nodes);
993    }
994
995    /** Return all the nodes in this graph that have a specified weight.
996     *  The nodes are returned in the form of a collection.
997     *  If the specified weight is null, return all the unweighted nodes.
998     *  If no nodes have the specified weight (or if the argument is null and
999     *  there are no unweighted nodes), return an empty collection.
1000     *  Each element in the returned collection is an instance of {@link Node}.
1001     *  @param weight The specified weight.
1002     *  @return The nodes in this graph that have the specified weight.
1003     */
1004    public Collection nodes(Object weight) {
1005        return _nodes.elements(weight);
1006    }
1007
1008    /** Return the collection of nodes in this graph whose weights are
1009     *  contained in a specified collection.
1010     *  Each element in the returned collection is an instance of
1011     *  {@link Node}.
1012     *  A null element in the argument collection is interpreted
1013     *  to mean that all unweighted nodes are to be included in the result.
1014     *  Duplicate weights or null elements in the specified collection result
1015     *  in duplicate nodes in the returned collection.
1016     *  Non-null elements in the argument collection that are not node weights
1017     *  are ignored.
1018     *  @param collection The specified collection of weights.
1019     *  @return The nodes in this graph whose weights are contained
1020     *  in a specified collection.
1021     *  @exception GraphWeightException If any specified weight
1022     *  is not a node weight in this graph.
1023     */
1024    public Collection nodes(Collection collection) {
1025        ArrayList nodes = new ArrayList();
1026        Iterator weights = collection.iterator();
1027
1028        while (weights.hasNext()) {
1029            nodes.addAll(nodes(weights.next()));
1030        }
1031
1032        return Collections.unmodifiableCollection(nodes);
1033    }
1034
1035    /** Remove an edge from this graph if it exists in the graph.
1036     *  The edge may be hidden.
1037     * An edge that is removed from a graph can be re-inserted
1038     * into the graph at a later time (using {@link #addEdge(Edge)}),
1039     * provided that the incident nodes are still in the graph.
1040     *
1041     * <p>This is an <em>O(e)</em> operation. A similar operation can be
1042     * performed in <em>O(1)</em> time using {@link #hideEdge(Edge)}.
1043     * @param edge The edge to be removed.
1044     * @return True if the edge was removed.
1045     * @see #hideEdge(Edge)
1046     */
1047    public boolean removeEdge(Edge edge) {
1048        if (!_edges.contains(edge)) {
1049            return false;
1050        }
1051
1052        _edges.remove(edge);
1053
1054        if (hidden(edge)) {
1055            _hiddenEdgeSet.remove(edge);
1056        } else {
1057            _disconnectEdge(edge);
1058        }
1059
1060        return true;
1061    }
1062
1063    /** Remove a node from this graph if it exists in the graph.
1064     * All edges incident to the node (excluding hidden edges) are also removed.
1065     * This is an
1066     * <em>O(n + ke)</em> operation, where <em>k</em> is the number of
1067     * incident edges.
1068     * @param node The node to be removed.
1069     * @return True if the node was removed.
1070     */
1071    public boolean removeNode(Node node) {
1072        if (!_nodes.contains(node)) {
1073            return false;
1074        }
1075
1076        // Avoid concurrent modification of the incident edges list.
1077        Object[] incidentEdgeArray = incidentEdges(node).toArray();
1078        _nodes.remove(node);
1079
1080        for (Object element : incidentEdgeArray) {
1081            removeEdge((Edge) element);
1082        }
1083
1084        _incidentEdgeMap.remove(node);
1085        _registerChange();
1086        return true;
1087    }
1088
1089    /** Restore an edge if the edge exists in the graph and is presently
1090     *  hidden. This is an <em>O(1)</em> operation.
1091     *  @param edge The edge to restore.
1092     *  @return True if the edge is in the graph and was hidden.
1093     *  @exception GraphElementException If the source node and
1094     *  sink node of the given edge are not both in the graph.
1095     *  @see #hideEdge(Edge)
1096     */
1097    public boolean restoreEdge(Edge edge) {
1098        if (_hiddenEdgeSet.remove(edge)) {
1099            // Make sure the source and sink are still in the graph.
1100            if (!containsNode(edge.source())) {
1101                throw new GraphElementException(edge, this,
1102                        "Source node is not in the graph.");
1103            }
1104
1105            if (!containsNode(edge.sink())) {
1106                throw new GraphElementException(edge, this,
1107                        "Sink node is not in the graph.");
1108            }
1109
1110            // Re-connect the edge.
1111            _connectEdge(edge);
1112            return true;
1113        } else {
1114            // The edge was not hidden.
1115            return false;
1116        }
1117    }
1118
1119    /** Return the number of self loop edges in this graph.
1120     *  @return The number of self loop edges.
1121     */
1122    public int selfLoopEdgeCount() {
1123        return selfLoopEdges().size();
1124    }
1125
1126    /** Return the number of self loop edges of a specified node.
1127     *  @param node The node.
1128     *  @return The number of self loop edges.
1129     *  @exception GraphElementException If the node is not in the graph.
1130     */
1131    public int selfLoopEdgeCount(Node node) {
1132        return selfLoopEdges(node).size();
1133    }
1134
1135    /** Return the collection of all self-loop edges in this graph.
1136     *  Each element in the returned collection is an {@link Edge}.
1137     *  This operation takes <i>O(E)</i> time, where E is the number
1138     *  of edges in the graph.
1139     *  @return The self-loop edges in this graph.
1140     */
1141    public Collection selfLoopEdges() {
1142        return _selfLoopAnalysis.edges();
1143    }
1144
1145    /** Return the collection of all self-loop edges that are incident to
1146     *  a specified node. Each element in the collection is an {@link Edge}.
1147     *
1148     *  @param node The node.
1149     *  @return The self-loop edges that are incident to the node.
1150     *  @exception GraphElementException If the node is not in the graph.
1151     */
1152    public Collection selfLoopEdges(Node node) {
1153        ArrayList result = new ArrayList();
1154
1155        // The call to incidentEdges validates existence of the node.
1156        Iterator edges = incidentEdges(node).iterator();
1157
1158        while (edges.hasNext()) {
1159            Edge edge = (Edge) edges.next();
1160
1161            if (edge.isSelfLoop()) {
1162                result.add(edge);
1163            }
1164        }
1165
1166        return result;
1167    }
1168
1169    /** Return the subgraph induced by a collection of nodes.
1170     *  In other words, return the subgraph formed by the given collection N of
1171     *  nodes together with the set of edges of the form (x, y), where
1172     *  x and y are both in N.
1173     *  Node and edge weights are preserved. In derived classes, this
1174     *  method returns the same type of graph as is returned by
1175     *  {@link ptolemy.graph.Graph#_emptyGraph()}.
1176     *  Derived classes that do not have zero-argument constructors may
1177     *  need to override this method to properly initialize the subgraph.
1178     *  @param collection The collection of nodes; each element
1179     *  is a {@link Node}.
1180     *  @return The induced subgraph.
1181     *  @exception GraphElementException If the collection contains a node
1182     *  that is not in this graph.
1183     */
1184    public Graph subgraph(Collection collection) {
1185        Graph subgraph = _emptyGraph();
1186        Iterator nodes = collection.iterator();
1187
1188        while (nodes.hasNext()) {
1189            subgraph.addNode((Node) nodes.next());
1190        }
1191
1192        nodes = collection.iterator();
1193
1194        while (nodes.hasNext()) {
1195            Node node = (Node) nodes.next();
1196
1197            if (!containsNode(node)) {
1198                throw new GraphElementException(node, this,
1199                        "Attempt to form an induced subgraph containing a "
1200                                + "node that is not in the 'parent' graph.");
1201            }
1202
1203            Iterator incidentEdges = incidentEdges(node).iterator();
1204
1205            while (incidentEdges.hasNext()) {
1206                Edge edge = (Edge) incidentEdges.next();
1207
1208                if (subgraph.containsNode(edge.source())
1209                        && subgraph.containsNode(edge.sink())
1210                        && !subgraph.containsEdge(edge)) {
1211                    subgraph.addEdge(edge);
1212                }
1213            }
1214        }
1215
1216        return subgraph;
1217    }
1218
1219    /** Return the subgraph formed by a subset of nodes and a subset of
1220     *  edges. Node and edge weights are preserved.
1221     *  In derived classes, this
1222     *  method returns the same type of graph as is returned by
1223     *  {@link ptolemy.graph.Graph#_emptyGraph()}.
1224     *  @param nodeCollection The subset of nodes; each element is an instance
1225     *  of {@link Node}.
1226     *  @param edgeCollection The subset of edges. Each element is an instance
1227     *  of {@link Edge}.
1228     *  @exception GraphElementException If the argument collections contain
1229     *  a node or edge that is not in this graph.
1230     *  @return The subgraph.
1231     *  @see #addEdges(Collection)
1232     *  @see #addNodes(Collection)
1233     */
1234    public Graph subgraph(Collection nodeCollection,
1235            Collection edgeCollection) {
1236        Graph subgraph = _emptyGraph();
1237
1238        Iterator nodes = nodeCollection.iterator();
1239
1240        while (nodes.hasNext()) {
1241            Node node = (Node) nodes.next();
1242
1243            if (!containsNode(node)) {
1244                throw new GraphElementException(node, this,
1245                        "Attempt to form a subgraph containing a node "
1246                                + "that is not in the 'parent' graph.");
1247            }
1248        }
1249
1250        Iterator edges = edgeCollection.iterator();
1251
1252        while (edges.hasNext()) {
1253            Edge edge = (Edge) edges.next();
1254
1255            if (!containsEdge(edge)) {
1256                throw new GraphElementException(edge, this,
1257                        "Attempt to form a subgraph containing an edge "
1258                                + "that is not in the 'parent' graph.");
1259            }
1260        }
1261
1262        subgraph.addNodes(nodeCollection);
1263        subgraph.addEdges(edgeCollection);
1264        return subgraph;
1265    }
1266
1267    /** Return a string representation of this graph. The string
1268     *  representation lists the nodes, including their labels
1269     *  and their weights, followed by the edges, including their
1270     *  labels, source nodes, sink nodes, and weights.
1271     *  @return A string representation of this graph.
1272     */
1273    @Override
1274    public String toString() {
1275        StringBuffer result = new StringBuffer(
1276                "{" + this.getClass().getName() + "\n");
1277        result.append("Node Set:\n" + _nodes.toString("\n", true) + "\n");
1278        result.append("Edge Set:\n" + _edges.toString("\n", true) + "\n}\n");
1279        return result.toString();
1280    }
1281
1282    /** Return true if the given object is a valid edge weight for this graph.
1283     *  An object is a valid edge weight if it is meaningful to assign it as
1284     *  an edge weight in this type of graph.
1285     *  If the given object is null this method returns true if it is valid
1286     *  to have an unweighted edge in this type of graph.
1287     *  This base class method returns true unconditionally.
1288     *  In derived classes, the method should be
1289     *  overridden to take into account any restrictions on edge weights.
1290     *  @param object The given object.
1291     *  @return True if if the given object is a valid edge weight for this
1292     *  graph.
1293     */
1294    public boolean validEdgeWeight(Object object) {
1295        return true;
1296    }
1297
1298    /** Return true if the given object is a valid node weight for this graph.
1299     *  An object is a valid node weight if it is meaningful to assign it as
1300     *  a node weight in this type of graph.
1301     *  If the given object is null this method returns true if it is valid
1302     *  to have an unweighted node in this type of graph.
1303     *  This base class method returns true unconditionally.
1304     *  In derived classes, the method should be
1305     *  overridden to take into account any restrictions on node weights.
1306     *  @param object The given object.
1307     *  @return True if if the given object is a valid node weight for this
1308     *  graph.
1309     */
1310    public boolean validNodeWeight(Object object) {
1311        return true;
1312    }
1313
1314    /** Validate the weight of an edge. Operation parallels that of
1315     *  #validateWeight(Node).
1316     *  @param edge The edge whose weight is to be validated.
1317     *  @return True if the edge weight has changed, as determined by
1318     *  the equals method.
1319     *  @exception GraphElementException If the specified edge is not in
1320     *  the graph.
1321     *  @exception GraphWeightException If the weight of the given edge
1322     *  is not valid, as determined by {@link #validEdgeWeight(Object)}.
1323     *  @see #validateWeight(Edge, Object)
1324     *  @see #validateWeight(Node)
1325     */
1326    public boolean validateWeight(Edge edge) {
1327        if (edge == null) {
1328            throw new NullPointerException("Attempt to validate the weight "
1329                    + "of a null graph edge.");
1330        }
1331
1332        if (!containsEdge(edge)) {
1333            throw new GraphElementException(edge, this,
1334                    "The specified edge is not in the graph.");
1335        }
1336
1337        Object weightArgument = edge.hasWeight() ? edge.getWeight() : null;
1338
1339        if (!validEdgeWeight(weightArgument)) {
1340            throw new GraphWeightException(weightArgument, edge, this,
1341                    "Invalid weight associated with an edge in the graph.\n");
1342        }
1343
1344        boolean changed = _edges.changeWeight(edge);
1345
1346        if (changed) {
1347            _registerChange();
1348        }
1349
1350        return changed;
1351    }
1352
1353    /** Validate the weight of an edge given the edge and its previous weight.
1354     *  Operation parallels that of #validateWeight(Node, Object).
1355     *
1356     *  @param edge The edge whose weight is to be validated.
1357     *  @param oldWeight The previous weight of the edge (null if the edge
1358     *  was previously unweighted).
1359     *  @return True if the edge weight has changed, as determined by
1360     *  the equals method.
1361     *  @see #validateWeight(Edge)
1362     *  @see #validateWeight(Node, Object)
1363     */
1364    public boolean validateWeight(Edge edge, Object oldWeight) {
1365        if (!containsEdge(edge)) {
1366            throw new GraphElementException(edge, this,
1367                    "The specified edge is not in the graph.");
1368        }
1369
1370        Object newWeight = edge.hasWeight() ? edge.getWeight() : null;
1371
1372        if (!validEdgeWeight(newWeight)) {
1373            throw new GraphWeightException(newWeight, edge, this,
1374                    "Invalid weight associated with an edge in the graph.");
1375        }
1376
1377        boolean changed = _edges.validateWeight(edge, oldWeight);
1378
1379        if (changed) {
1380            _registerChange();
1381        }
1382
1383        return changed;
1384    }
1385
1386    /** Validate the weight of a node. This method checks the validity of
1387     *  the node weight (using {@link #validNodeWeight(Object)}, and
1388     *  updates, if necessary, the internal mapping of weights into
1389     *  their associated nodes.
1390     *  This updating operation is necessary for correct operation of
1391     *  {@link #containsNodeWeight(Object)},
1392     *  {@link #node(Object)},
1393     *  {@link #nodes(Collection)}, and
1394     *  {@link #nodes(Object)},
1395     *  if the node weight has changed in a way
1396     *  that affects comparison under the equals method.
1397     *  This method returns true if the node weight has changed (as determined
1398     *  by the equals() method) since the last time the graph was notified
1399     *  of the node's weight. Furthermore, if the node weight has changed in
1400     *  this way,  a graph change is registered.
1401     *  This is an <em>O(n)</em> operation.
1402     *  @param node The node whose weight is to be validated.
1403     *  @return True if the node weight has changed, as determined by
1404     *  the equals method.
1405     *  @exception GraphElementException If the specified node is not in
1406     *  the graph.
1407     *  @exception GraphWeightException If the weight of the given node
1408     *  is not valid, as determined by {@link #validNodeWeight(Object)}.
1409     *  @see #validateWeight(Node, Object)
1410     */
1411    public boolean validateWeight(Node node) {
1412        if (node == null) {
1413            throw new NullPointerException("Attempt to validate the weight "
1414                    + "of a null graph node.");
1415        }
1416
1417        if (!containsNode(node)) {
1418            throw new GraphElementException(node, this,
1419                    "The specified node is not in the graph.");
1420        }
1421
1422        Object weightArgument = node.hasWeight() ? node.getWeight() : null;
1423
1424        if (!validNodeWeight(weightArgument)) {
1425            throw new GraphWeightException(weightArgument, node, this,
1426                    "Invalid weight associated with a node in the graph.");
1427        }
1428
1429        boolean changed = _nodes.changeWeight(node);
1430
1431        if (changed) {
1432            _registerChange();
1433        }
1434
1435        return changed;
1436    }
1437
1438    /** Validate the weight of a node given the node and its previous weight.
1439     *  The previous weight argument should be set to
1440     *  the weight of the node when the node was last added
1441     *  to the graph or last had its node validated, whichever was more recent.
1442     *  Operation is equivalent to {@link #validateWeight(Node)}
1443     *  except that the additional argument is used to improve efficiency.
1444     *  The previous node weight should be set to null to indicate that
1445     *  the node was previously unweighted.
1446     *
1447     *  <p>Consider an example in which a given Node <em>node</em> is
1448     *  contained in two graphs <em>graph1</em> and <em> graph2 </em>,
1449     *  and suppose that we wish to change the weight of the
1450     *  node. Below is a sample code fragment that achieves such a
1451     *  weight change with proper notification to the containing
1452     *  graphs.
1453     *
1454     *  <pre>
1455     *  Object oldWeight = node.getWeight();
1456     *  node.setWeight(newWeight);
1457     *  graph1.validateWeight(node, oldWeight);
1458     *  graph2.validateWeight(node, oldWeight);
1459     *  </pre>
1460     *
1461     *  <p>In this example, #validateWeight(Node) could be used
1462     *  (e.g., if the previous weight <em>oldWeight</em> was not available)
1463     *  in place of #validateWeight(Node, Object), but the efficiency would be
1464     *  lower.
1465     *
1466     *  @param node The node whose weight is to be validated.
1467     *  @param oldWeight The previous weight of the node.
1468     *  @return True if the node weight has changed, as determined by
1469     *  the equals method.
1470     *  @see #validateWeight(Node)
1471     */
1472    public boolean validateWeight(Node node, Object oldWeight) {
1473        if (!containsNode(node)) {
1474            throw new GraphElementException(node, this,
1475                    "The specified node is not in the graph.");
1476        }
1477
1478        Object newWeight = node.hasWeight() ? node.getWeight() : null;
1479
1480        if (!validNodeWeight(newWeight)) {
1481            throw new GraphWeightException(newWeight, node, this,
1482                    "Invalid weight associated with a node in the graph.");
1483        }
1484
1485        boolean changed = _nodes.validateWeight(node, oldWeight);
1486
1487        if (changed) {
1488            _registerChange();
1489        }
1490
1491        return changed;
1492    }
1493
1494    /** Given a collection of graph elements (nodes and edges), return an array
1495     * of weights associated with these elements.
1496     * If a weight is common across multiple elements in
1497     * the collection, it will appear multiple times in the array.
1498     * If the element collection is null or empty, an empty (zero-element)
1499     * array is returned.
1500     * @param elementCollection The collection of graph elements;
1501     * each element is a {@link Node} or an {@link Edge}.
1502     * @return The weights of the graph elements, in the order that that
1503     * elements are returned by collection's iterator; each element in the
1504     * returned array is an {@link Object}.
1505     * @exception NullPointerException If the specified collection contains
1506     * a null value.
1507     * @exception GraphElementException If the specified collection
1508     * contains a non-null value that is neither a node nor an edge.
1509     */
1510    public static Object[] weightArray(Collection elementCollection) {
1511        if (elementCollection == null) {
1512            return new Object[0];
1513        } else {
1514            Element element = null;
1515            Object[] result = new Object[elementCollection.size()];
1516            Iterator elements = elementCollection.iterator();
1517
1518            try {
1519                for (int i = 0; i < elementCollection.size(); i++) {
1520                    element = (Element) elements.next();
1521
1522                    if (element == null) {
1523                        throw new NullPointerException(
1524                                "Null graph element " + "specified.\n");
1525                    } else {
1526                        result[i] = element.getWeight();
1527                    }
1528                }
1529            } catch (ClassCastException exception) {
1530                throw new GraphElementException(
1531                        "Illegal graph element "
1532                                + "(neither a Node nor an Edge) specified.\n"
1533                                + "The element's type is: "
1534                                + (element == null ? "null"
1535                                        : element.getClass().getName())
1536                                + ".\n");
1537            }
1538
1539            return result;
1540        }
1541    }
1542
1543    ///////////////////////////////////////////////////////////////////
1544    ////                         protected methods                 ////
1545
1546    /** Create and add an edge with a specified source node, sink node,
1547     *  and optional weight.
1548     *  The third parameter specifies whether the edge is to be
1549     *  weighted, and the fourth parameter is the weight that is
1550     *  to be applied if the edge is weighted.
1551     *  Returns the edge that is added.
1552     *  @param node1 The source node of the edge.
1553     *  @param node2 The sink node of the edge.
1554     *  @param weighted True if the edge is to be weighted.
1555     *  @param weight The weight that is to be applied if the edge is to
1556     *  be weighted.
1557     *  @return The edge.
1558     *  @exception GraphElementException If either of the specified
1559     *  nodes is not in the graph.
1560     *  @exception NullPointerException If the edge is to be weighted, but
1561     *  the specified weight is null.
1562     */
1563    protected Edge _addEdge(Node node1, Node node2, boolean weighted,
1564            Object weight) {
1565        if (!containsNode(node1)) {
1566            throw new GraphElementException(node1, this,
1567                    "The specified first node is not in the graph.");
1568        } else if (!containsNode(node2)) {
1569            throw new GraphElementException(node2, this,
1570                    "The specified second node is not in the graph.");
1571        } else if (weighted && weight == null) {
1572            throw new NullPointerException("Attempt to assign a null "
1573                    + "weight to an edge. The first node:\n" + node1
1574                    + "\nThe second node:\n" + node2 + "\nThe graph: \n"
1575                    + this);
1576        } else {
1577            Edge edge = null;
1578
1579            if (weighted) {
1580                edge = new Edge(node1, node2, weight);
1581            } else {
1582                edge = new Edge(node1, node2);
1583            }
1584
1585            _registerEdge(edge);
1586            return edge;
1587        }
1588    }
1589
1590    /** Connect an edge to a node by appropriately modifying
1591     * the adjacency information associated with the node.
1592     * The node is assumed to be in the graph.
1593     * @param edge The edge.
1594     * @param node The node.
1595     * @exception GraphConstructionException If the edge has already
1596     * been connected to the node.
1597     */
1598    protected void _connect(Edge edge, Node node) {
1599        if (_incidentEdgeList(node).contains(edge)) {
1600            throw new GraphConstructionException(
1601                    "Attempt to connect the same edge multiple times."
1602                            + GraphException.elementDump(edge, this));
1603        } else {
1604            _incidentEdgeList(node).add(edge);
1605        }
1606    }
1607
1608    /** Connect a given edge in this graph. The edge and its source and sink
1609     *  nodes are assumed to be in
1610     *  the graph. This method performs operations that are common to
1611     *  the addition of a new edge and the restoration of a hidden edge.
1612     *  Specifically, this method connects, using {@link #_connect(Edge, Node)},
1613     *  the given edge to its source and sink nodes;
1614     *  updates the mapping of weights into corresponding graph
1615     *  edges; and registers a change in the graph. This method should be
1616     *  overridden to perform additional operations that are necessary
1617     *  to connect edges in derived graph classes.
1618     *  @param edge The edge to connect.
1619     *  @see #hideEdge(Edge)
1620     *  @see #removeEdge(Edge)
1621     *  @see #_disconnectEdge(Edge)
1622     *  @see #_registerChange()
1623     */
1624    protected void _connectEdge(Edge edge) {
1625        _connect(edge, edge.source());
1626
1627        if (!edge.isSelfLoop()) {
1628            _connect(edge, edge.sink());
1629        }
1630
1631        _edges.registerWeight(edge);
1632        _registerChange();
1633    }
1634
1635    /** Disconnect an edge from a node that it is incident to.
1636     *  Specifically, this method removes the edge from the set of
1637     *  edges that are considered incident to the node in this graph.
1638     *  This method does nothing if the given edge is not incident to the
1639     *  given node.
1640     *  This method should be overridden to incorporate additional operations
1641     *  that are required to disconnect an edge from a node (see, for
1642     *  example, DirectedGraph.#_disconnect(Edge, Node)).
1643     *  @param edge The edge.
1644     *  @param node The node.
1645     */
1646    protected void _disconnect(Edge edge, Node node) {
1647        _incidentEdgeList(node).remove(edge);
1648    }
1649
1650    /** Disconnect a given edge in this graph. The edge is assumed to be in
1651     *  the graph and
1652     *  not already hidden. This method performs operations that are common to
1653     *  the removal of and hiding of an edge. Specifically, this method
1654     *  disconnects, using {@link #_disconnect(Edge, Node)}, the given edge
1655     *  from its source and sink nodes;
1656     *  updates the mapping of weights into corresponding graph
1657     *  edges; and registers a change in the graph. This method should be
1658     *  overridden to perform additional operations that are necessary
1659     *  to disconnect edges in derived graph classes.
1660     *  @param edge The edge to disconnect.
1661     *  @see #hideEdge(Edge)
1662     *  @see #removeEdge(Edge)
1663     *  @see #_connectEdge(Edge)
1664     *  @see #_registerChange()
1665     */
1666    protected void _disconnectEdge(Edge edge) {
1667        _disconnect(edge, edge.source());
1668        _disconnect(edge, edge.sink());
1669        _edges.cancelWeight(edge);
1670        _registerChange();
1671    }
1672
1673    /** Return an empty graph that has the same run-time type as this graph.
1674     *  This class should be overridden in derived classes that do not
1675     *  have zero-argument constructors.
1676     *  @return An empty graph.
1677     */
1678    protected Graph _emptyGraph() {
1679        Graph graph = null;
1680
1681        try {
1682            // Kepler (jdk1.4?) requires this cast
1683            graph = getClass().newInstance();
1684        } catch (Exception exception) {
1685            throw new GraphConstructionException("Could not create an "
1686                    + "empty graph from this one.\n" + exception + "\n"
1687                    + GraphException.graphDump(this));
1688        }
1689
1690        return graph;
1691    }
1692
1693    /** Initialize the list of analyses that are associated with this graph,
1694     *  and initialize the change counter of the graph.
1695     *  @see ptolemy.graph.analysis.Analysis
1696     */
1697    protected void _initializeAnalyses() {
1698        _analysisList = new ArrayList();
1699        _selfLoopAnalysis = new SelfLoopAnalysis(this);
1700        _changeCount = 0;
1701    }
1702
1703    /** Register a change to the graph by updating the change counter.
1704     *  This method must be called after any change to the graph
1705     *  that may affect (invalidate) any of the computations associated with
1706     *  analyses that this graph is associated with.
1707     *  @see Analysis
1708     */
1709    protected void _registerChange() {
1710        if (_changeCount == Long.MAX_VALUE) {
1711            // Invalidate all of the associated analyses.
1712            Iterator analyses = _analysisList.iterator();
1713
1714            while (analyses.hasNext()) {
1715                Analysis analysis = (Analysis) analyses.next();
1716
1717                if (analysis.analyzer() instanceof CachedStrategy) {
1718                    ((CachedStrategy) analysis.analyzer()).reset();
1719                }
1720            }
1721
1722            _changeCount = 0;
1723        } else {
1724            _changeCount++;
1725        }
1726    }
1727
1728    /** Register a new edge in the graph. The edge is assumed to
1729     *  be non-null, unique, and consistent with the node set.
1730     *  This method performs updates of internal
1731     *  data structures that are required for every edge that is added
1732     *  to the graph.
1733     *  Derived classes can override this method to perform additional updates
1734     *  of internal data structures.
1735     *  @param edge The new edge.
1736     *  @exception GraphWeightException If the weight of the given edge is
1737     *  not valid, as determined by {@link #validEdgeWeight(Object)}.
1738     *  @see #_registerNode(Node)
1739     */
1740    protected void _registerEdge(Edge edge) {
1741        Object weight = edge.hasWeight() ? edge.getWeight() : null;
1742
1743        if (!validEdgeWeight(weight)) {
1744            throw new GraphWeightException(weight, edge, this,
1745                    "Invalid edge weight.");
1746        }
1747
1748        _edges.add(edge);
1749        _connectEdge(edge);
1750    }
1751
1752    /** Register a new node in the graph. The node is assumed to
1753     *  be non-null and unique. This method performs updates of internal
1754     *  data structures that are required for every node that is added
1755     *  to the graph.
1756     *  Derived classes can override this method to perform additional updates
1757     *  of internal data structures.
1758     *  @param node The new node.
1759     *  @exception GraphWeightException If the weight of the given node is
1760     *  not valid, as determined by {@link #validNodeWeight(Object)}.
1761     *  @see #_registerEdge(Edge)
1762     */
1763    protected void _registerNode(Node node) {
1764        Object weight = node.hasWeight() ? node.getWeight() : null;
1765
1766        if (!validNodeWeight(weight)) {
1767            throw new GraphWeightException(weight, node, this,
1768                    "Invalid node weight.");
1769        }
1770
1771        _nodes.add(node);
1772        _incidentEdgeMap.put(node, new ArrayList());
1773        _nodes.registerWeight(node);
1774        _registerChange();
1775    }
1776
1777    ///////////////////////////////////////////////////////////////////
1778    ////                         private methods                   ////
1779    /** Given two node weights w1 and w2, add all edges of the form
1780     * (x1, x2), where
1781     *     (x1.getWeight() == w1) &amp;&amp; (x2.getWeight() == w2).
1782     * The third parameter specifies whether the edges are to be
1783     * weighted, and the fourth parameter is the weight that is
1784     * to be applied if the edges are weighted.
1785     * The method returns one of the edges that is added.
1786     * The method returns an iterator over the edges that were added;
1787     * each element of this iterator is an instance of Edge.
1788     * The method throws an GraphConstructionException if no edge is
1789     * added (i.e., if no nodes x1, x2 satisfy the above condition.
1790     * The method throws a NullPointerException if w1 or w2 is null.
1791     * @param weight1 The source node weight.
1792     * @param weight2 The sink node weight.
1793     * @param weighted True if weighted.
1794     * @param weight The weight
1795     */
1796    private Collection _addEdges(Object weight1, Object weight2,
1797            boolean weighted, Object weight) {
1798        if (weight1 == null) {
1799            throw new NullPointerException("Null source node weight");
1800        } else if (weight2 == null) {
1801            throw new NullPointerException("Null sink node weight");
1802        }
1803
1804        Iterator nodes1 = nodes(weight1).iterator();
1805        Edge newEdge = null;
1806        ArrayList newEdges = new ArrayList();
1807
1808        while (nodes1.hasNext()) {
1809            Node node1 = (Node) nodes1.next();
1810            Iterator nodes2 = nodes(weight2).iterator();
1811
1812            while (nodes2.hasNext()) {
1813                newEdge = _addEdge(node1, (Node) nodes2.next(), weighted,
1814                        weight);
1815                newEdges.add(newEdge);
1816            }
1817        }
1818
1819        if (newEdges.isEmpty()) {
1820            throw new GraphConstructionException("No edge can be added based "
1821                    + "on the specified source and sink node weights.\n"
1822                    + "Weight1:\n" + weight1 + "\nWeight2:\n" + weight2 + "\n"
1823                    + GraphException.graphDump(this));
1824        } else {
1825            return newEdges;
1826        }
1827    }
1828
1829    // Return the list of incident edges for a specified node.
1830    // Return null if the specified node is not in the graph.
1831    private ArrayList _incidentEdgeList(Node node) {
1832        return (ArrayList) _incidentEdgeMap.get(node);
1833    }
1834
1835    ///////////////////////////////////////////////////////////////////
1836    ////                         private variables                 ////
1837    // A list of analyses that are associated with this graph. Each
1838    // element of the list is an instance of ptolemy.graph.analysis.Analysis.
1839    private ArrayList _analysisList;
1840
1841    // A counter that keeps track of changes to the graph.
1842    private long _changeCount;
1843
1844    // The list of edges in this graph.
1845    // Each element of this list is an Edge.
1846    private ElementList _edges;
1847
1848    // The set of hidden edges. Each element is an Edge.
1849    // Hidden edges remain contained in the _edges list, but are removed from
1850    // the incidence and weight maps.
1851    private HashSet _hiddenEdgeSet;
1852
1853    // A mapping from nodes into their lists of incident edges.
1854    // This redundant information is maintained for improved
1855    // run-time efficiency when handing undirected graphs, or when operating
1856    // on directed graphs in ways for which edge orientation is not relevant.
1857    // Each key in this map is an instance of Node. Each value
1858    // is an instance of ArrayList whose elements are instances of Edge.
1859    private HashMap _incidentEdgeMap;
1860
1861    // The list of nodes in this graph.
1862    // Each element of this list is a Node.
1863    private ElementList _nodes;
1864
1865    // The analysis for computation of self loop edges.
1866    private SelfLoopAnalysis _selfLoopAnalysis;
1867}