blob: f0b2374306c64dd8d01bb58fdf24da6c65d4422a [file] [log] [blame]
/*
* Copyright (c) 2008, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*
*/
package com.sun.hotspot.igv.hierarchicallayout;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
/**
*
* @author Thomas Wuerthinger
*/
public class Graph<N, E> {
private HashMap<Object, Node<N, E>> nodes;
private HashMap<Object, Edge<N, E>> edges;
private List<Node<N, E>> nodeList;
public Graph() {
nodes = new HashMap<Object, Node<N, E>>();
edges = new HashMap<Object, Edge<N, E>>();
nodeList = new ArrayList<Node<N, E>>();
}
public Node<N, E> createNode(N data, Object key) {
Node<N, E> n = new Node<N, E>(this, data);
assert key == null || !nodes.containsKey(key);
if (key != null) {
nodes.put(key, n);
}
nodeList.add(n);
return n;
}
public Edge<N, E> createEdge(Node<N, E> source, Node<N, E> dest, E data, Object key) {
Edge<N, E> e = new Edge<N, E>(this, source, dest, data);
source.addOutEdge(e);
dest.addInEdge(e);
if (key != null) {
edges.put(key, e);
}
return e;
}
public Node<N, E> getNode(Object key) {
return nodes.get(key);
}
public Edge<N, E> getEdge(Object key) {
return edges.get(key);
}
public Collection<Edge<N, E>> getEdges() {
return Collections.unmodifiableCollection(edges.values());
}
public Collection<Node<N, E>> getNodes() {
return Collections.unmodifiableList(nodeList);
}
public void removeEdge(Edge<N, E> e, Object key) {
assert key == null || edges.containsKey(key);
if (key != null) {
edges.remove(key);
}
e.getSource().removeOutEdge(e);
e.getDest().removeInEdge(e);
}
public class DFSTraversalVisitor {
public void visitNode(Node<N, E> n) {
}
public boolean visitEdge(Edge<N, E> e, boolean backEdge) {
return true;
}
}
public class BFSTraversalVisitor {
public void visitNode(Node<N, E> n, int depth) {
}
}
public List<Node<N, E>> getNodesWithInDegree(int x) {
return getNodesWithInDegree(x, true);
}
public List<Node<N, E>> getNodesWithInDegree(int x, boolean countSelfLoops) {
List<Node<N, E>> result = new ArrayList<Node<N, E>>();
for (Node<N, E> n : getNodes()) {
if (n.getInDegree(countSelfLoops) == x) {
result.add(n);
}
}
return result;
}
private void markReachable(Node<N, E> startingNode) {
ArrayList<Node<N, E>> arr = new ArrayList<Node<N, E>>();
arr.add(startingNode);
for (Node<N, E> n : getNodes()) {
n.setReachable(false);
}
traverseDFS(arr, new DFSTraversalVisitor() {
@Override
public void visitNode(Node<N, E> n) {
n.setReachable(true);
}
});
}
public void traverseBFS(Node<N, E> startingNode, BFSTraversalVisitor tv, boolean longestPath) {
if (longestPath) {
markReachable(startingNode);
}
for (Node<N, E> n : getNodes()) {
n.setVisited(false);
n.setActive(false);
}
Queue<Node<N, E>> queue = new LinkedList<Node<N, E>>();
queue.add(startingNode);
startingNode.setVisited(true);
int layer = 0;
Node<N, E> lastOfLayer = startingNode;
Node<N, E> lastAdded = null;
while (!queue.isEmpty()) {
Node<N, E> current = queue.poll();
tv.visitNode(current, layer);
current.setActive(false);
for (Edge<N, E> e : current.getOutEdges()) {
if (!e.getDest().isVisited()) {
boolean allow = true;
if (longestPath) {
for (Node<N, E> pred : e.getDest().getPredecessors()) {
if ((!pred.isVisited() || pred.isActive()) && pred.isReachable()) {
allow = false;
break;
}
}
}
if (allow) {
queue.offer(e.getDest());
lastAdded = e.getDest();
e.getDest().setVisited(true);
e.getDest().setActive(true);
}
}
}
if (current == lastOfLayer && !queue.isEmpty()) {
lastOfLayer = lastAdded;
layer++;
}
}
}
public void traverseDFS(DFSTraversalVisitor tv) {
traverseDFS(getNodes(), tv);
}
public void traverseDFS(Collection<Node<N, E>> startingNodes, DFSTraversalVisitor tv) {
for (Node<N, E> n : getNodes()) {
n.setVisited(false);
n.setActive(false);
}
boolean result = false;
for (Node<N, E> n : startingNodes) {
traverse(tv, n);
}
}
private void traverse(DFSTraversalVisitor tv, Node<N, E> n) {
if (!n.isVisited()) {
n.setVisited(true);
n.setActive(true);
tv.visitNode(n);
for (Edge<N, E> e : n.getOutEdges()) {
Node<N, E> next = e.getDest();
if (next.isActive()) {
tv.visitEdge(e, true);
} else {
if (tv.visitEdge(e, false)) {
traverse(tv, next);
}
}
}
n.setActive(false);
}
}
public boolean hasCycles() {
for (Node<N, E> n : getNodes()) {
n.setVisited(false);
n.setActive(false);
}
boolean result = false;
for (Node<N, E> n : getNodes()) {
result |= checkCycles(n);
if (result) {
break;
}
}
return result;
}
private boolean checkCycles(Node<N, E> n) {
if (n.isActive()) {
return true;
}
if (!n.isVisited()) {
n.setVisited(true);
n.setActive(true);
for (Node<N, E> succ : n.getSuccessors()) {
if (checkCycles(succ)) {
return true;
}
}
n.setActive(false);
}
return false;
}
@Override
public String toString() {
StringBuilder s = new StringBuilder();
s.append("Nodes: ");
for (Node<N, E> n : getNodes()) {
s.append(n.toString());
s.append("\n");
}
s.append("Edges: ");
for (Edge<N, E> e : getEdges()) {
s.append(e.toString());
s.append("\n");
}
return s.toString();
}
}