1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package ubic.basecode.dataStructure.graph;
20
21 import java.lang.reflect.Constructor;
22 import java.util.ArrayList;
23 import java.util.Collection;
24 import java.util.Collections;
25 import java.util.HashMap;
26 import java.util.HashSet;
27 import java.util.Iterator;
28 import java.util.LinkedHashMap;
29 import java.util.LinkedList;
30 import java.util.List;
31 import java.util.Map;
32 import java.util.Queue;
33 import java.util.Set;
34
35 import javax.swing.JTree;
36 import javax.swing.tree.DefaultMutableTreeNode;
37 import javax.swing.tree.DefaultTreeModel;
38
39 import org.slf4j.Logger;
40 import org.slf4j.LoggerFactory;
41
42
43
44
45
46
47
48
49
50 public class DirectedGraph<K, V> extends AbstractGraph<DirectedGraphNode<K, V>, K, V> {
51
52 private static Logger log = LoggerFactory.getLogger( DirectedGraph.class );
53 protected DefaultTreeModel dtm;
54 protected Map<K, DirectedGraphNode<K, V>> items;
55
56 private JTree treeView;
57
58 public DirectedGraph() {
59 items = new LinkedHashMap<K, DirectedGraphNode<K, V>>();
60 }
61
62
63
64
65
66
67
68
69 public void addChildTo( K key, K newChildKey ) throws IllegalStateException {
70
71 assert key != null;
72 assert newChildKey != null;
73
74 if ( !items.containsKey( newChildKey ) ) {
75 throw new IllegalArgumentException( "Attempt to add link to node that is not in the graph" );
76 }
77
78 if ( items.containsKey( key ) ) {
79 items.get( key ).addChild( newChildKey );
80 items.get( newChildKey ).addParent( key );
81 } else {
82 throw new IllegalArgumentException( "Attempt to add child to a node that is not in the graph" );
83 }
84
85 }
86
87
88
89
90
91
92
93
94 public void addChildTo( K key, K newChildKey, V newChild ) {
95
96 assert key != null;
97 assert newChildKey != null;
98 assert newChild != null;
99
100 if ( !items.containsKey( key ) ) {
101 throw new IllegalArgumentException( "Attempt to add a child to a non-existent node: " + key );
102 }
103
104 if ( !items.containsKey( newChildKey ) ) {
105 this.addNode( newChildKey, newChild );
106 }
107
108 this.addChildTo( key, newChildKey );
109 this.addParentTo( newChildKey, key );
110 }
111
112
113
114
115
116
117 @Override
118 public void addNode( DirectedGraphNode<K, V> node ) {
119 assert node != null;
120 node.setGraph( this );
121 items.put( node.getKey(), node );
122 }
123
124
125
126
127
128
129
130 @Override
131 public void addNode( K key, V item ) {
132 assert key != null;
133 assert item != null;
134
135 if ( !items.containsKey( key ) ) {
136 items.put( key, new DirectedGraphNode<K, V>( key, item, this ) );
137 }
138 }
139
140
141
142
143
144
145 public void addParentTo( K key, K newParentKey ) throws IllegalStateException {
146 assert key != null;
147 assert newParentKey != null;
148
149 if ( !items.containsKey( newParentKey ) ) {
150 throw new IllegalArgumentException(
151 "Attempt to add as parent a node that is not in the graph: " + newParentKey );
152 }
153
154 if ( items.containsKey( key ) ) {
155 items.get( key ).addParent( newParentKey );
156 items.get( newParentKey ).addChild( key );
157 }
158
159 }
160
161
162
163
164
165
166 public void addParentTo( K key, K newParentKey, V newParent ) {
167
168 assert newParentKey != null;
169 assert newParent != null;
170
171 if ( !items.containsKey( newParent ) ) {
172 this.addNode( newParentKey, newParent );
173 }
174
175 assert key != null;
176
177 this.addChildTo( key, newParentKey );
178 this.addParentTo( newParentKey, key );
179 }
180
181
182
183
184
185
186 @Override
187 public boolean containsKey( K key ) {
188 if ( key == null ) return false;
189 return getItems().containsKey( key );
190 }
191
192
193
194
195 public void deleteLeaf( K leaf ) {
196 assert leaf != null;
197 DirectedGraphNode<K, V> leafNode = this.get( leaf );
198 if ( leafNode == null ) {
199 throw new IllegalArgumentException( "No such node: " + leaf );
200 }
201
202 if ( !leafNode.isLeaf() ) {
203 return;
204 }
205
206 Set<DirectedGraphNode<K, V>> parents = leafNode.getAllParentNodes();
207
208 items.remove( leaf );
209
210 for ( DirectedGraphNode<K, V> node : parents ) {
211 node.prune();
212 }
213
214 }
215
216
217
218
219
220
221 @Override
222 public Map<K, DirectedGraphNode<K, V>> getItems() {
223 return this.items;
224 }
225
226
227
228
229 public DirectedGraphNode<K, V> getRoot() {
230 this.topoSort();
231 List<DirectedGraphNode<K, V>> nodes = new ArrayList<DirectedGraphNode<K, V>>( items.values() );
232 Collections.sort( nodes );
233 return nodes.get( 0 );
234 }
235
236 public DefaultTreeModel getTreeModel() {
237 if ( treeView == null ) this.treeView();
238 return dtm;
239 }
240
241
242
243
244
245
246 public Collection<V> getValues() {
247 Collection<V> result = new HashSet<V>();
248 for ( K element : this.items.keySet() ) {
249 DirectedGraphNode<K, V> v = items.get( element );
250 result.add( v.getItem() );
251 }
252 return result;
253 }
254
255
256
257
258 public void prune() {
259 for ( K element : this.items.keySet() ) {
260 assert element != null;
261 DirectedGraphNode<K, V> v = items.get( element );
262 v.prune();
263 }
264 }
265
266
267
268
269 public void topoSort() {
270 Queue<DirectedGraphNode<K, V>> q = new LinkedList<DirectedGraphNode<K, V>>();
271 int counter = 0;
272
273 Map<DirectedGraphNode<K, V>, Integer> degrees = new HashMap<DirectedGraphNode<K, V>, Integer>();
274
275
276 for ( K element : this.items.keySet() ) {
277 DirectedGraphNode<K, V> v = items.get( element );
278 degrees.put( v, new Integer( v.inDegree() ) );
279 if ( degrees.get( v ).intValue() == 0 ) {
280 q.offer( v );
281 }
282 }
283
284 while ( !q.isEmpty() ) {
285 DirectedGraphNode<K, V> v = q.remove();
286 v.setTopoSortOrder( ++counter );
287 for ( DirectedGraphNode<K, V> w : v.getChildNodes() ) {
288
289 assert w != null;
290 assert degrees.containsKey( w );
291
292 Integer inDegree = degrees.get( w );
293 inDegree--;
294 degrees.put( w, inDegree );
295
296
297 if ( inDegree == 0 ) {
298 q.offer( w );
299 }
300 }
301 }
302
303 if ( counter != items.size() ) {
304 throw new IllegalStateException(
305 "Graph contains a cycle? " + counter + " items found, " + items.size() + " expected" );
306 }
307
308 }
309
310
311
312
313
314
315 @Override
316 public String toString() {
317 prune();
318 this.topoSort();
319 List<DirectedGraphNode<K, V>> nodes = new ArrayList<DirectedGraphNode<K, V>>( items.values() );
320 Collections.sort( nodes );
321 DirectedGraphNode<K, V> root = nodes.get( 0 );
322 StringBuffer buf = new StringBuffer();
323 int tablevel = 0;
324 makeString( root, buf, tablevel );
325 return buf.toString();
326 }
327
328
329
330
331
332
333 public JTree treeView() {
334 if ( treeView == null ) {
335 throw new IllegalStateException(
336 "You must call treeview(Class<? extends DefaultMutableTreeNode> nodeClass ) first" );
337 }
338 return treeView;
339 }
340
341
342
343
344
345
346
347
348 @SuppressWarnings("unchecked")
349 public JTree treeView( Class<? extends DefaultMutableTreeNode> nodeClass ) {
350 log.debug( "Constructing tree view of graph" );
351 DirectedGraphNode<K, V> root = getRoot();
352 Constructor<DefaultMutableTreeNode> constructor;
353 DefaultMutableTreeNode top = null;
354 treeView = null;
355 try {
356 constructor = ( Constructor<DefaultMutableTreeNode> ) nodeClass
357 .getConstructor( new Class[] { root.getClass() } );
358 top = constructor.newInstance( new Object[] { root } );
359 log.debug( "Starting tree with: " + top.getClass().getName() );
360 root.mark();
361 addJTreeNode( top, root, constructor );
362 dtm = new DefaultTreeModel( top );
363 treeView = new JTree( dtm );
364
365 } catch ( Exception e ) {
366 throw new RuntimeException( e );
367 }
368 return treeView;
369 }
370
371
372
373
374
375
376 private void addJTreeNode( DefaultMutableTreeNode startJTreeNode, DirectedGraphNode<K, V> startNode,
377 Constructor<DefaultMutableTreeNode> constructor ) {
378 if ( startJTreeNode == null ) return;
379 Set<DirectedGraphNode<K, V>> children = startNode.getChildNodes();
380
381 for ( Iterator<DirectedGraphNode<K, V>> it = children.iterator(); it.hasNext(); ) {
382 DirectedGraphNode<K, V> nextNode = it.next();
383 if ( !nextNode.isVisited() ) {
384 DefaultMutableTreeNode newJTreeNode = null;
385 try {
386 newJTreeNode = constructor.newInstance( new Object[] { nextNode } );
387 } catch ( Exception e ) {
388 log.error( e.getMessage(), e );
389 }
390 startJTreeNode.add( newJTreeNode );
391 nextNode.mark();
392 this.addJTreeNode( newJTreeNode, nextNode, constructor );
393 }
394 }
395 }
396
397
398
399
400 private String makeString( DirectedGraphNode<K, V> startNode, StringBuffer buf, int tabLevel ) {
401
402 if ( buf == null ) {
403 buf = new StringBuffer();
404 }
405
406 Set<DirectedGraphNode<K, V>> children = startNode.getChildNodes();
407
408 if ( !startNode.isVisited() ) {
409 buf.append( startNode + "\n" );
410 startNode.mark();
411 }
412 tabLevel++;
413 for ( Iterator<DirectedGraphNode<K, V>> it = children.iterator(); it.hasNext(); ) {
414 DirectedGraphNode<K, V> f = it.next();
415 if ( !f.isVisited() ) {
416 for ( int i = 0; i < tabLevel; i++ ) {
417 buf.append( "\t" );
418 }
419 buf.append( f + "\n" );
420 f.mark();
421 this.makeString( f, buf, tabLevel );
422 }
423 }
424
425 return buf.toString();
426 }
427
428 }