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