6 public class GraphNode {
8 public static boolean useEdgeLabels;
10 /* NodeStatus enumeration pattern ***********/
12 public static final NodeStatus UNVISITED = new NodeStatus("UNVISITED");
13 public static final NodeStatus PROCESSING = new NodeStatus("PROCESSING");
14 public static final NodeStatus FINISHED = new NodeStatus("FINISHED");
16 public static class NodeStatus {
17 private static String name;
18 private NodeStatus(String name) { this.name = name; }
19 public String toString() { return name; }
22 /* Edge *****************/
24 public static class Edge {
27 private GraphNode target;
28 private GraphNode source;
29 private String dotnodeparams = new String();
31 public Edge(String label, GraphNode target) {
36 public String getLabel() {
40 public void setSource(GraphNode s) {
44 public GraphNode getSource() {
48 public GraphNode getTarget() {
52 public void setDotNodeParameters(String param) {
54 throw new NullPointerException();
56 if (param.length() > 0) {
57 dotnodeparams = "," + param;
59 dotnodeparams = new String();
65 int discoverytime = -1;
66 int finishingtime = -1; /* used for searches */
69 Vector edges = new Vector();
70 Vector inedges = new Vector();
73 NodeStatus status = UNVISITED;
74 String dotnodeparams = new String();
77 public GraphNode(String label) {
78 this.nodelabel = label;
79 this.textlabel = label;
82 public GraphNode(String label, Object owner) {
83 this.nodelabel = label;
84 this.textlabel = label;
88 public GraphNode(String label, String textlabel, Object owner) {
89 this.nodelabel = label;
90 this.textlabel = textlabel;
94 public Object getOwner() {
98 public static void computeclosure(Collection nodes, Collection removed) {
99 Stack tovisit=new Stack();
100 tovisit.addAll(nodes);
101 while(!tovisit.isEmpty()) {
102 GraphNode gn=(GraphNode)tovisit.pop();
103 for(Iterator it=gn.edges();it.hasNext();) {
104 Edge edge=(Edge)it.next();
105 GraphNode target=edge.getTarget();
106 if (!nodes.contains(target)) {
107 if ((removed==null)||
108 (!removed.contains(target))) {
110 tovisit.push(target);
117 public void setDotNodeParameters(String param) {
119 throw new NullPointerException();
121 if (param.length() > 0) {
122 dotnodeparams = "," + param;
124 dotnodeparams = new String();
128 public void setStatus(NodeStatus status) {
129 if (status == null) {
130 throw new NullPointerException();
132 this.status = status;
135 public String getLabel() {
139 public String getTextLabel() {
143 public NodeStatus getStatus() {
147 public Iterator edges() {
148 return edges.iterator();
151 public Iterator inedges() {
152 return inedges.iterator();
155 public void addEdge(Edge newedge) {
156 newedge.setSource(this);
157 edges.addElement(newedge);
158 GraphNode tonode=newedge.getTarget();
159 tonode.inedges.addElement(newedge);
173 public int getSCC() {
181 void discover(int time) {
182 discoverytime = time;
186 void finish(int time) {
187 assert status == PROCESSING;
188 finishingtime = time;
192 /** Returns finishing time for dfs */
194 public int getFinishingTime() {
195 return finishingtime;
199 public static class DOTVisitor {
201 java.io.PrintWriter output;
205 private DOTVisitor(java.io.OutputStream output) {
208 this.output = new java.io.PrintWriter(output, true);
211 private String getNewID(String name) {
212 tokennumber = tokennumber + 1;
213 return new String (name+tokennumber);
218 public static void visit(java.io.OutputStream output, Collection nodes) {
219 DOTVisitor visitor = new DOTVisitor(output);
220 visitor.nodes = nodes;
225 private void make() {
226 output.println("digraph dotvisitor {");
227 output.println("\trotate=90;");
228 /* output.println("\tpage=\"8.5,11\";");
229 output.println("\tnslimit=1000.0;");
230 output.println("\tnslimit1=1000.0;");
231 output.println("\tmclimit=1000.0;");
232 output.println("\tremincross=true;");*/
233 output.println("\tnode [fontsize=10,height=\"0.1\", width=\"0.1\"];");
234 output.println("\tedge [fontsize=6];");
236 output.println("}\n");
239 private void traverse() {
240 Set cycleset=GraphNode.findcycles(nodes);
242 Iterator i = nodes.iterator();
243 while (i.hasNext()) {
244 GraphNode gn = (GraphNode) i.next();
245 Iterator edges = gn.edges();
246 String label = gn.getTextLabel(); // + " [" + gn.discoverytime + "," + gn.finishingtime + "];";
248 if (cycleset.contains(gn))
249 option=",style=bold";
250 output.println("\t" + gn.getLabel() + " [label=\"" + label + "\"" + gn.dotnodeparams + option+"];");
252 while (edges.hasNext()) {
253 Edge edge = (Edge) edges.next();
254 GraphNode node = edge.getTarget();
255 if (nodes.contains(node)) {
256 String edgelabel = useEdgeLabels ? "label=\"" + edge.getLabel() + "\"" : "label=\"\"";
257 output.println("\t" + gn.getLabel() + " -> " + node.getLabel() + " [" + edgelabel + edge.dotnodeparams + "];");
264 /* XXXXXXXX Should use SCC algorithm here - will change */
265 public static Set findcycles(Collection nodes) {
266 Stack st=new Stack();
267 HashSet acyclic=new HashSet();
268 HashSet cycles=new HashSet();
269 for(Iterator it=nodes.iterator();it.hasNext();) {
270 GraphNode node=(GraphNode)it.next();
271 if (acyclic.contains(node))
273 if (cycles.contains(node))
275 findcycles(cycles, acyclic, st,node,nodes);
280 private static boolean findcycles(Set cycles, Set acyclic, Stack visited, GraphNode gn, Collection nodes) {
281 if (visited.contains(gn)) {/* Found cycle */
282 cycles.addAll(visited.subList(visited.indexOf(gn),visited.size())); /* Add this cycle */
285 boolean acyclicflag=true;
287 for(Iterator it=gn.edges();it.hasNext();) {
288 Edge e=(Edge) it.next();
289 GraphNode node = e.getTarget();
290 if (!nodes.contains(node))
291 continue; /* Don't visit stuff outside set */
292 if (acyclic.contains(node))
294 if (findcycles(cycles,acyclic,visited,node,nodes)) {
301 acyclic.add(gn); /* no cycles through gn */
304 return true; /* found cycle */
307 public static class SCC {
311 public SCC(boolean hascycles, HashMap map,int numscc) {
312 this.hascycles=hascycles;
317 public boolean hasCycles() {
321 public int numSCC() {
325 public Set getSCC(int i) {
326 Integer scc=new Integer(i);
327 return (Set)map.get(scc);
330 boolean hascycle(int i) {
331 Integer scc=new Integer(i);
332 Set s=(Set)map.get(scc);
335 Object [] array=s.toArray();
336 GraphNode gn=(GraphNode)array[0];
337 for(Iterator it=gn.edges();it.hasNext();) {
338 Edge e=(Edge)it.next();
339 if (e.getTarget()==gn)
340 return true; /* Self Cycle */
347 * DFS encapsulates the depth first search algorithm
349 public static class DFS {
354 Vector finishingorder=null;
357 private DFS(Collection nodes) {
361 public static SCC computeSCC(Collection nodes) {
363 throw new NullPointerException();
365 DFS dfs=new DFS(nodes);
366 dfs.sccmap=new HashMap();
367 dfs.finishingorder=new Vector();
368 boolean hascycles=dfs.go();
369 for (Iterator it = nodes.iterator();it.hasNext();) {
370 GraphNode gn = (GraphNode) it.next();
373 for(int i=dfs.finishingorder.size()-1;i>=0;i--) {
374 GraphNode gn=(GraphNode)dfs.finishingorder.get(i);
375 if (gn.getStatus() == UNVISITED) {
377 dfs.sccindex++; /* Increment scc index */
380 return new SCC(hascycles,dfs.sccmap,dfs.sccindex);
383 void dfsprev(GraphNode gn) {
384 if (gn.getStatus()==FINISHED||!nodes.contains(gn))
386 gn.setStatus(FINISHED);
388 Integer i=new Integer(sccindex);
389 if (!sccmap.containsKey(i))
390 sccmap.put(i,new HashSet());
391 ((Set)sccmap.get(i)).add(gn);
392 for(Iterator edgeit=gn.inedges();edgeit.hasNext();) {
393 Edge e=(Edge)edgeit.next();
394 GraphNode gn2=e.getSource();
399 public static boolean depthFirstSearch(Collection nodes) {
401 throw new NullPointerException();
404 DFS dfs = new DFS(nodes);
408 private boolean go() {
411 boolean acyclic=true;
412 i = nodes.iterator();
413 while (i.hasNext()) {
414 GraphNode gn = (GraphNode) i.next();
418 i = nodes.iterator();
419 while (i.hasNext()) {
420 GraphNode gn = (GraphNode) i.next();
421 assert gn.getStatus() != PROCESSING;
422 if (gn.getStatus() == UNVISITED) {
430 private boolean dfs(GraphNode gn) {
431 boolean acyclic=true;
433 Iterator edges = gn.edges();
435 while (edges.hasNext()) {
436 Edge edge = (Edge) edges.next();
437 GraphNode node = edge.getTarget();
438 if (!nodes.contains(node)) /* Skip nodes which aren't in the set */
440 if (node.getStatus() == UNVISITED) {
443 } else if (node.getStatus()==PROCESSING) {
447 if (finishingorder!=null)
448 finishingorder.add(gn);