f84873e3ee0b0a761223a060f91a5ccae08bd138
[utils] / system / general / src / main / java / org / wamblee / system / graph / Graph.java
1 /*
2  * Copyright 2008 the original author or authors.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package org.wamblee.system.graph;
17
18 import java.util.ArrayList;
19 import java.util.Iterator;
20 import java.util.List;
21
22
23 /**
24  * Represents a graph consisting of nodes and edges.
25  *
26  * @author Erik Brakkee
27  */
28 public class Graph {
29     /**
30      * DOCUMENT ME!
31      */
32     private List<Node> nodes;
33
34     /**
35      * DOCUMENT ME!
36      */
37     private List<Edge> edges;
38
39 /**
40      * Constructs the graph.
41      */
42     public Graph() {
43         nodes     = new ArrayList<Node>();
44         edges     = new ArrayList<Edge>();
45     }
46
47     /**
48      * Adds a node.
49      *
50      * @param aNode Node to add.
51      *
52      * @throws IllegalArgumentException In case the node already exists. Node
53      *         equality is checked using <code>equals</code>.
54      */
55     public void addNode(Node aNode) {
56         if (nodes.contains(aNode)) {
57             throw new IllegalArgumentException("Node '" + aNode.getName()
58                 + "' already exists");
59         }
60
61         nodes.add(aNode);
62     }
63
64     /**
65      * Finds a node with the given name.
66      *
67      * @param aName Node name.
68      *
69      * @return Node or null if not found.
70      */
71     public Node findNode(String aName) {
72         for (Node node : nodes) {
73             if (node.getName().equals(aName)) {
74                 return node;
75             }
76         }
77
78         return null;
79     }
80
81     /**
82      * Removes a node.
83      *
84      * @param aNode Node to remove.
85      *
86      * @return True iff the node was removed.
87      *
88      * @throws IllegalArgumentException In case there are edges of which the
89      *         node is a part.
90      */
91     public boolean removeNode(Node aNode) {
92         if (!findOutgoing(aNode).isEmpty() || !findIncoming(aNode).isEmpty()) {
93             throw new IllegalArgumentException("Cannot remove node '"
94                 + aNode.getName()
95                 + "' because it is connected to one or more edges");
96         }
97
98         return nodes.remove(aNode);
99     }
100
101     /**
102      * Adds a list of nodes.
103      *
104      * @param aNodes Nodes to add.
105      *
106      * @see #addNode(Node)
107      */
108     public void addNodes(List<Node> aNodes) {
109         for (Node node : aNodes) {
110             addNode(node);
111         }
112     }
113
114     /**
115      * Adds an edge.
116      *
117      * @param aEdge Edge to add.
118      *
119      * @throws IllegalArgumentException In case one of the nodes of the edges
120      *         is not part of the graph or if the same edge (as determined by
121      *         {@link #equals(Object)} is already a part of the graph.
122      */
123     public void addEdge(Edge aEdge) {
124         if (edges.contains(aEdge)) {
125             throw new IllegalArgumentException("Edge '" + aEdge
126                 + "' already exists");
127         }
128
129         if (!nodes.contains(aEdge.getFrom())) {
130             throw new IllegalArgumentException("From node '" + aEdge.getFrom()
131                 + "' from edge '" + aEdge + "' is not part of the graph");
132         }
133
134         if (!nodes.contains(aEdge.getTo())) {
135             throw new IllegalArgumentException("To node '" + aEdge.getTo()
136                 + "' from edge '" + aEdge + "' is not part of the graph");
137         }
138
139         edges.add(aEdge);
140     }
141
142     /**
143      * Removes an edge.
144      *
145      * @param aEdge Edge to remove.
146      *
147      * @return True if the edge was removed.
148      */
149     public boolean removeEdge(Edge aEdge) {
150         return edges.remove(aEdge);
151     }
152
153     /**
154      * Adds a number of edges.
155      *
156      * @param aEdges Edges to add.
157      */
158     public void addEdges(List<Edge> aEdges) {
159         for (Edge edge : aEdges) {
160             addEdge(edge);
161         }
162     }
163
164     /**
165      * Gets the nodes.
166      *
167      * @return Copy of the list of nodes of the graph.
168      */
169     public List<Node> getNodes() {
170         return new ArrayList<Node>(nodes);
171     }
172
173     /**
174      * Gets the edges.
175      *
176      * @return Copy of the list of edges of the graph.
177      */
178     public List<Edge> getEdges() {
179         return new ArrayList<Edge>(edges);
180     }
181
182     /**
183      * Extends the graph with edges using an edge factory. All
184      * combinations of  nodes are passed to the factory which creates
185      * additional edges.
186      *
187      * @param aFactory Edge factory.
188      */
189     public void extend(EdgeFactory aFactory) {
190         for (Node from : nodes) {
191             for (Node to : nodes) {
192                 edges.addAll(aFactory.create(from, to));
193             }
194         }
195     }
196
197     /**
198      * Applies a filter to the graph for removing elements.
199      *
200      * @param aFilter Filter to apply.
201      */
202     public void applyFilter(EdgeFilter aFilter) {
203         for (Iterator<Edge> edge = edges.iterator(); edge.hasNext();) {
204             if (aFilter.isViolated(edge.next())) {
205                 edge.remove();
206             }
207         }
208     }
209
210     /**
211      * Finds all outgoing edges of a node. More specifically, finds
212      * all edges <code>e</code> for which <code>e.getFrom().getName() =
213      * aNode.getName()</code>.
214      *
215      * @param aNode Node for which to find outgoing edges.
216      *
217      * @return List of outgoing edges.
218      */
219     public List<Edge> findOutgoing(Node aNode) {
220         List<Edge> result = new ArrayList<Edge>();
221
222         for (Edge edge : edges) {
223             if (edge.getFrom().getName().equals(aNode.getName())) {
224                 result.add(edge);
225             }
226         }
227
228         return result;
229     }
230
231     /**
232      * Finds all incoming edges of a node.  More specifically, finds
233      * all edges <code>e</code> for which <code>e.getTo().getName() =
234      * aNode.getName()</code>.
235      *
236      * @param aNode Node for which to find incoming edges.
237      *
238      * @return List of incoming edges.
239      */
240     public List<Edge> findIncoming(Node aNode) {
241         List<Edge> result = new ArrayList<Edge>();
242
243         for (Edge edge : edges) {
244             if (edge.getTo().getName().equals(aNode.getName())) {
245                 result.add(edge);
246             }
247         }
248
249         return result;
250     }
251
252     /**
253      * Implements a visitor design pattern. This loops over all nodes
254      * and all edges and invokes the appropriate visit methods on the visitor.
255      *
256      * @param aVisitor Visitor.
257      */
258     public void accept(Visitor aVisitor) {
259         List<Node> nodes = getNodes(); // copy to make sure the visitor can
260                                        // modify the
261                                        // list of nodes as part of the loop.
262
263         List<Edge> edges = getEdges(); // copy ..... (see above).
264
265         for (Node node : nodes) {
266             aVisitor.visitNode(node);
267         }
268
269         for (Edge edge : edges) {
270             aVisitor.visitEdge(edge);
271         }
272     }
273 }