1 package baseCode.dataStructure.graph;
2
3 import java.util.ArrayList;
4 import java.util.Collections;
5 import java.util.HashMap;
6 import java.util.Iterator;
7 import java.util.LinkedHashMap;
8 import java.util.List;
9 import java.util.Map;
10 import java.util.Set;
11
12 import javax.swing.JTree;
13 import javax.swing.tree.DefaultMutableTreeNode;
14
15 import baseCode.dataStructure.Queue;
16
17 /***
18 * A graph that contains DirectedGraphNodes. It can be cyclic. Small unconnected parts of the graph will be ignored for
19 * many operation. Tree traversals start from the root node, which is defined as the node with the most children.
20 * <p>
21 * Copyright (c) Columbia University
22 *
23 * @todo do something about cyclicity; make this a dag or a subclass...
24 * @author Paul Pavlidis
25 * @version $Id: DirectedGraph.java,v 1.8 2004/07/27 03:18:58 pavlidis Exp $
26 */
27 public class DirectedGraph extends AbstractGraph {
28
29 public DirectedGraph() {
30 super();
31 }
32
33 /***
34 * @param nodes Set of DirectedGraphNodes
35 */
36 public DirectedGraph( Set nodes ) {
37 items = new LinkedHashMap();
38 for ( Iterator it = nodes.iterator(); it.hasNext(); ) {
39 DirectedGraphNode a = ( DirectedGraphNode ) it.next();
40 this.addNode( a );
41 }
42 }
43
44 /***
45 * @param key Object
46 * @param item Object
47 */
48 public void addNode( Object key, Object item ) {
49 if ( !items.containsKey( key ) ) {
50 items.put( key, new DirectedGraphNode( key, item, this ) );
51 }
52 }
53
54 /***
55 * Add a child to a particualar node identified by key.
56 *
57 * @param key Object
58 * @param newChildKey Object
59 * @param newChild Object
60 */
61 public void addChildTo( Object key, Object newChildKey, Object newChild ) {
62 if ( !items.containsKey( newChild ) ) {
63 this.addNode( newChildKey, newChild );
64 }
65
66 this.addChildTo( key, newChildKey );
67 this.addParentTo( newChildKey, key );
68 }
69
70 /***
71 * Add a child to a particular node identified by key; if the node is not in the graph, an exception is thrown.
72 *
73 * @param key Object
74 * @param newChildKey Object
75 * @throws IllegalStateException if the graph doesn't contain the child node.
76 */
77 public void addChildTo( Object key, Object newChildKey )
78 throws IllegalStateException {
79 if ( !items.containsKey( newChildKey ) ) {
80 throw new IllegalStateException(
81 "Attempt to add link to node that is not in the graph" );
82 }
83
84 if ( items.containsKey( key ) ) {
85 ( ( DirectedGraphNode ) items.get( key ) ).addChild( newChildKey );
86 ( ( DirectedGraphNode ) items.get( newChildKey ) ).addParent( key );
87 }
88
89 }
90
91 /***
92 * @param key Object
93 * @param newParentKey Object
94 * @param newParent Object
95 */
96 public void addParentTo( Object key, Object newParentKey, Object newParent ) {
97 if ( !items.containsKey( newParent ) ) {
98 this.addNode( newParentKey, newParent );
99 }
100
101 this.addChildTo( key, newParentKey );
102 this.addParentTo( newParentKey, key );
103 }
104
105 /***
106 * @param key Object
107 * @param newParentKey Object
108 * @throws IllegalStateException
109 */
110 public void addParentTo( Object key, Object newParentKey )
111 throws IllegalStateException {
112 if ( !items.containsKey( newParentKey ) ) {
113 throw new IllegalStateException(
114 "Attempt to add link to node that is not in the graph" );
115 }
116
117 if ( items.containsKey( key ) ) {
118 ( ( DirectedGraphNode ) items.get( key ) ).addParent( newParentKey );
119 ( ( DirectedGraphNode ) items.get( newParentKey ) ).addChild( key );
120 }
121
122 }
123
124 /***
125 * Shows the tree as a tabbed list. Items that have no parents are shown at the 'highest' level.
126 *
127 * @return String
128 */
129 public String toString() {
130 prune();
131 this.topoSort();
132 List nodes = new ArrayList( items.values() );
133 Collections.sort( nodes );
134 DirectedGraphNode root = ( DirectedGraphNode ) nodes.get( 0 );
135 StringBuffer buf = new StringBuffer();
136 int tablevel = 0;
137 makeString( root, buf, tablevel );
138 return ( buf.toString() );
139 }
140
141
142
143
144 private String makeString( DirectedGraphNode startNode, StringBuffer buf,
145 int tabLevel ) {
146
147 if ( buf == null ) {
148 buf = new StringBuffer();
149 }
150
151 Set children = startNode.getChildNodes();
152
153 if ( !startNode.isVisited() ) {
154 buf.append( startNode + "\n" );
155 startNode.mark();
156 }
157 tabLevel++;
158 for ( Iterator it = children.iterator(); it.hasNext(); ) {
159 DirectedGraphNode f = ( DirectedGraphNode ) it.next();
160 if ( !f.isVisited() ) {
161 for ( int i = 0; i < tabLevel; i++ ) {
162 buf.append( "\t" );
163 }
164 buf.append( f + "\n" );
165 f.mark();
166 this.makeString( f, buf, tabLevel );
167 }
168 }
169
170 return buf.toString();
171 }
172
173 /***
174 * Remove vertices to nodes that aren't in the graph.
175 */
176 public void prune() {
177 for ( Iterator it = this.items.keySet().iterator(); it.hasNext(); ) {
178 DirectedGraphNode v = ( DirectedGraphNode ) items.get( it.next() );
179 v.prune();
180 }
181 }
182
183 /***
184 * Fills in the topoSortOrder for each node.
185 */
186 public void topoSort() {
187 Queue q = new Queue();
188 int counter = 0;
189 DirectedGraphNode v;
190 Map degrees = new HashMap();
191
192
193 for ( Iterator it = this.items.keySet().iterator(); it.hasNext(); ) {
194 v = ( DirectedGraphNode ) items.get( it.next() );
195 degrees.put( v, new Integer( v.inDegree() ) );
196 if ( ( ( Integer ) degrees.get( v ) ).intValue() == 0 ) {
197 q.enqueue( v );
198 }
199 }
200
201 while ( !q.isEmpty() ) {
202 v = ( DirectedGraphNode ) q.dequeue();
203 v.setTopoSortOrder( ++counter );
204 for ( Iterator vit = v.getChildNodes().iterator(); vit.hasNext(); ) {
205 DirectedGraphNode w = ( DirectedGraphNode ) vit.next();
206
207 int inDegree = ( ( Integer ) degrees.get( w ) ).intValue();
208 inDegree--;
209 degrees.put( w, new Integer( inDegree ) );
210
211
212 if ( inDegree == 0 ) {
213 q.enqueue( w );
214 }
215 }
216 }
217
218 if ( counter != items.size() ) {
219 throw new IllegalStateException( "Graph contains a cycle; " + counter
220 + " items found, " + items.size() + " expected" );
221 }
222
223 }
224
225 /***
226 * Generate a JTree corresponding to this graph.
227 *
228 * @todo note that nodes are only allowed to have one parent in DefaultMutableTreeNodes
229 * @return javax.swing.JTree
230 */
231 public JTree treeView() {
232 this.topoSort();
233 List nodes = new ArrayList( items.values() );
234 Collections.sort( nodes );
235 DirectedGraphNode root = ( DirectedGraphNode ) nodes.get( 0 );
236 DefaultMutableTreeNode top = new DefaultMutableTreeNode( root );
237 root.mark();
238 addJTreeNode( top, root );
239 JTree tree = new JTree( top );
240 return tree;
241 }
242
243 private void addJTreeNode( DefaultMutableTreeNode startJTreeNode,
244 DirectedGraphNode startNode ) {
245 Set children = startNode.getChildNodes();
246
247 for ( Iterator it = children.iterator(); it.hasNext(); ) {
248 DirectedGraphNode nextNode = ( DirectedGraphNode ) it.next();
249 if ( !nextNode.isVisited() ) {
250 DefaultMutableTreeNode newJTreeNode = new DefaultMutableTreeNode(
251 nextNode );
252 startJTreeNode.add( newJTreeNode );
253 nextNode.mark();
254 this.addJTreeNode( newJTreeNode, nextNode );
255 }
256 }
257
258 }
259
260 }