1 /*
2  * Copyright (c) 2008, 2015, Oracle and/or its affiliates. All rights reserved.
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * This code is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 only, as
7  * published by the Free Software Foundation.
8  *
9  * This code is distributed in the hope that it will be useful, but WITHOUT
10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12  * version 2 for more details (a copy is included in the LICENSE file that
13  * accompanied this code).
14  *
15  * You should have received a copy of the GNU General Public License version
16  * 2 along with this work; if not, write to the Free Software Foundation,
17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18  *
19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20  * or visit www.oracle.com if you need additional information or have any
21  * questions.
22  *
23  */
24 package com.sun.hotspot.igv.hierarchicallayout;
25 
26 import java.util.*;
27 
28 /**
29  *
30  * @author Thomas Wuerthinger
31  */
32 public class Graph<N, E> {
33 
34     private HashMap<Object, Node<N, E>> nodes;
35     private HashMap<Object, Edge<N, E>> edges;
36     private List<Node<N, E>> nodeList;
37 
Graph()38     public Graph() {
39         nodes = new HashMap<>();
40         edges = new HashMap<>();
41         nodeList = new ArrayList<>();
42     }
43 
createNode(N data, Object key)44     public Node<N, E> createNode(N data, Object key) {
45         Node<N, E> n = new Node<>(this, data);
46         assert key == null || !nodes.containsKey(key);
47         if (key != null) {
48             nodes.put(key, n);
49         }
50         nodeList.add(n);
51         return n;
52     }
53 
createEdge(Node<N, E> source, Node<N, E> dest, E data, Object key)54     public Edge<N, E> createEdge(Node<N, E> source, Node<N, E> dest, E data, Object key) {
55         Edge<N, E> e = new Edge<>(this, source, dest, data);
56         source.addOutEdge(e);
57         dest.addInEdge(e);
58         if (key != null) {
59             edges.put(key, e);
60         }
61         return e;
62     }
63 
getNode(Object key)64     public Node<N, E> getNode(Object key) {
65         return nodes.get(key);
66     }
67 
getEdge(Object key)68     public Edge<N, E> getEdge(Object key) {
69         return edges.get(key);
70     }
71 
getEdges()72     public Collection<Edge<N, E>> getEdges() {
73         return Collections.unmodifiableCollection(edges.values());
74     }
75 
getNodes()76     public Collection<Node<N, E>> getNodes() {
77         return Collections.unmodifiableList(nodeList);
78     }
79 
removeEdge(Edge<N, E> e, Object key)80     public void removeEdge(Edge<N, E> e, Object key) {
81         assert key == null || edges.containsKey(key);
82         if (key != null) {
83             edges.remove(key);
84         }
85         e.getSource().removeOutEdge(e);
86         e.getDest().removeInEdge(e);
87     }
88 
89     public class DFSTraversalVisitor {
90 
visitNode(Node<N, E> n)91         public void visitNode(Node<N, E> n) {
92         }
93 
visitEdge(Edge<N, E> e, boolean backEdge)94         public boolean visitEdge(Edge<N, E> e, boolean backEdge) {
95             return true;
96         }
97     }
98 
99     public class BFSTraversalVisitor {
100 
visitNode(Node<N, E> n, int depth)101         public void visitNode(Node<N, E> n, int depth) {
102         }
103     }
104 
getNodesWithInDegree(int x)105     public List<Node<N, E>> getNodesWithInDegree(int x) {
106         return getNodesWithInDegree(x, true);
107     }
108 
getNodesWithInDegree(int x, boolean countSelfLoops)109     public List<Node<N, E>> getNodesWithInDegree(int x, boolean countSelfLoops) {
110 
111         List<Node<N, E>> result = new ArrayList<>();
112         for (Node<N, E> n : getNodes()) {
113             if (n.getInDegree(countSelfLoops) == x) {
114                 result.add(n);
115             }
116         }
117 
118         return result;
119 
120     }
121 
markReachable(Node<N, E> startingNode)122     private void markReachable(Node<N, E> startingNode) {
123         ArrayList<Node<N, E>> arr = new ArrayList<>();
124         arr.add(startingNode);
125         for (Node<N, E> n : getNodes()) {
126             n.setReachable(false);
127         }
128         traverseDFS(arr, new DFSTraversalVisitor() {
129 
130             @Override
131             public void visitNode(Node<N, E> n) {
132                 n.setReachable(true);
133             }
134         });
135     }
136 
traverseBFS(Node<N, E> startingNode, BFSTraversalVisitor tv, boolean longestPath)137     public void traverseBFS(Node<N, E> startingNode, BFSTraversalVisitor tv, boolean longestPath) {
138 
139         if (longestPath) {
140             markReachable(startingNode);
141         }
142 
143         for (Node<N, E> n : getNodes()) {
144             n.setVisited(false);
145             n.setActive(false);
146         }
147 
148         Queue<Node<N, E>> queue = new LinkedList<>();
149         queue.add(startingNode);
150         startingNode.setVisited(true);
151         int layer = 0;
152         Node<N, E> lastOfLayer = startingNode;
153         Node<N, E> lastAdded = null;
154 
155         while (!queue.isEmpty()) {
156 
157             Node<N, E> current = queue.poll();
158             tv.visitNode(current, layer);
159             current.setActive(false);
160 
161 
162             for (Edge<N, E> e : current.getOutEdges()) {
163                 if (!e.getDest().isVisited()) {
164 
165                     boolean allow = true;
166                     if (longestPath) {
167                         for (Node<N, E> pred : e.getDest().getPredecessors()) {
168                             if ((!pred.isVisited() || pred.isActive()) && pred.isReachable()) {
169                                 allow = false;
170                                 break;
171                             }
172                         }
173                     }
174 
175                     if (allow) {
176                         queue.offer(e.getDest());
177                         lastAdded = e.getDest();
178                         e.getDest().setVisited(true);
179                         e.getDest().setActive(true);
180                     }
181                 }
182             }
183 
184             if (current == lastOfLayer && !queue.isEmpty()) {
185                 lastOfLayer = lastAdded;
186                 layer++;
187             }
188         }
189     }
190 
traverseDFS(DFSTraversalVisitor tv)191     public void traverseDFS(DFSTraversalVisitor tv) {
192         traverseDFS(getNodes(), tv);
193     }
194 
traverseDFS(Collection<Node<N, E>> startingNodes, DFSTraversalVisitor tv)195     public void traverseDFS(Collection<Node<N, E>> startingNodes, DFSTraversalVisitor tv) {
196 
197         for (Node<N, E> n : getNodes()) {
198             n.setVisited(false);
199             n.setActive(false);
200         }
201 
202         boolean result = false;
203         for (Node<N, E> n : startingNodes) {
204             traverse(tv, n);
205         }
206     }
207 
traverse(DFSTraversalVisitor tv, Node<N, E> n)208     private void traverse(DFSTraversalVisitor tv, Node<N, E> n) {
209 
210         if (!n.isVisited()) {
211             n.setVisited(true);
212             n.setActive(true);
213             tv.visitNode(n);
214 
215             for (Edge<N, E> e : n.getOutEdges()) {
216 
217                 Node<N, E> next = e.getDest();
218                 if (next.isActive()) {
219                     tv.visitEdge(e, true);
220                 } else {
221                     if (tv.visitEdge(e, false)) {
222                         traverse(tv, next);
223                     }
224                 }
225             }
226 
227             n.setActive(false);
228         }
229 
230     }
231 
hasCycles()232     public boolean hasCycles() {
233 
234         for (Node<N, E> n : getNodes()) {
235             n.setVisited(false);
236             n.setActive(false);
237         }
238 
239         boolean result = false;
240         for (Node<N, E> n : getNodes()) {
241             result |= checkCycles(n);
242             if (result) {
243                 break;
244             }
245         }
246         return result;
247     }
248 
checkCycles(Node<N, E> n)249     private boolean checkCycles(Node<N, E> n) {
250 
251         if (n.isActive()) {
252             return true;
253         }
254 
255         if (!n.isVisited()) {
256 
257             n.setVisited(true);
258             n.setActive(true);
259 
260             for (Node<N, E> succ : n.getSuccessors()) {
261                 if (checkCycles(succ)) {
262                     return true;
263                 }
264             }
265 
266             n.setActive(false);
267 
268         }
269 
270         return false;
271     }
272 
273     @Override
toString()274     public String toString() {
275 
276         StringBuilder s = new StringBuilder();
277         s.append("Nodes: ");
278         for (Node<N, E> n : getNodes()) {
279             s.append(n.toString());
280             s.append("\n");
281         }
282 
283         s.append("Edges: ");
284 
285         for (Edge<N, E> e : getEdges()) {
286             s.append(e.toString());
287             s.append("\n");
288         }
289 
290         return s.toString();
291     }
292 }
293