/******************************************************************************
* Copyright (c) 2009 - 2015 IBM Corporation.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
*****************************************************************************/
/**
*
*/
package com.ibm.wala.memsat.util;
import java.util.AbstractCollection;
import java.util.AbstractSet;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.Set;
import com.ibm.wala.util.Predicate;
import com.ibm.wala.util.graph.Graph;
/**
* Provides a set of utility methods for working with {@linkplain Graph graphs}.
*
* @author etorlak
*/
public final class Graphs {
private Graphs() {}
/**
* Returns the roots of the given graph.
* @return roots of the given graph
*/
public static final <T> Set<T> roots(Graph<T> graph) {
final Set<T> ret = new LinkedHashSet<T>();
for(T n : graph) {
ret.add(n);
}
for(T n : graph) {
for(Iterator<? extends T> itr = graph.getSuccNodes(n); itr.hasNext(); ) {
ret.remove(itr.next());
}
}
return ret;
}
/**
* Returns a root of the given graph.
* @requires some n: graph.nodes | no (graph.edges).n
* @return a root of the given graph
*/
public static final <T> T root(Graph<T> graph) {
final Set<T> roots = roots(graph);
assert roots.size()>=1;
return roots.iterator().next();
}
/**
* Returns a new Graph with the given nodes and no edges.
* @return a new Graph with the given nodes and no edges.
*/
public static final <T> Graph<T> graph(T...nodes) {
final Graph<T> ret = new LinkedHashGraph<T>();
for(T node : nodes)
ret.addNode(node);
return ret;
}
/**
* Returns a new Graph with the given nodes and no edges.
* @return a new Graph with the given nodes and no edges.
*/
public static final <T> Graph<T> graph(Collection<? extends T> nodes) {
final Graph<T> ret = new LinkedHashGraph<T>();
for(T node : nodes)
ret.addNode(node);
return ret;
}
/**
* Returns a set view of the nodes in the given graph.
* @return a set view of the nodes in the given graph.
*/
public static final <T> Set<T> nodes(final Graph<T> g) {
return new AbstractSet<T>() {
@Override
public Iterator<T> iterator() { return g.iterator(); }
@Override
public int size() { return g.getNumberOfNodes(); }
@SuppressWarnings("unchecked")
@Override
public boolean contains(Object o) {
return g.containsNode((T) o);
}
};
}
/**
* Returns a collection view of the successors of the node in the graph g.
* @return a collection view of the successors of the node in the graph g.
*/
public static final <T> Collection<T> successors(final T n, final Graph<T> g) {
return new AbstractCollection<T>() {
@Override
public Iterator<T> iterator() {
return (Iterator<T>)g.getSuccNodes(n);
}
@Override
public int size() { return g.getSuccNodeCount(n); }
};
}
/**
* Returns a collection view of the predecessors of the node in the graph g.
* @return a collection view of the predecessors of the node in the graph g.
*/
public static final <T> Collection<T> predecessors(final T n, final Graph<T> g) {
return new AbstractCollection<T>() {
@Override
public Iterator<T> iterator() {
return (Iterator<T>)g.getPredNodes(n);
}
@Override
public int size() { return g.getPredNodeCount(n); }
};
}
/**
* Returns a graph that is the transitive closure of g.
* @return a graph that is the transitive closure of g.
*/
public static final <T> Graph<T> transitiveClosure(Graph<T> g) {
final Graph<T> ret = new LinkedHashGraph<T>();
for(T n : g) {
ret.addNode(n);
for(Iterator<? extends T> succs = g.getSuccNodes(n); succs.hasNext(); ) {
ret.addEdge(n, succs.next());
}
}
for(T nk : g) {
for(T ni : g) {
for(T nj : g) {
if (ret.hasEdge(ni, nk) && ret.hasEdge(nk, nj))
ret.addEdge(ni, nj);
}
}
}
return ret;
}
/**
* Returns a graph that is the reflexive transitive closure of g.
* @return a graph that is the reflexive transitive closure of g.
*/
public static final <T> Graph<T> reflexiveTransitiveClosure(Graph<T> g) {
final Graph<T> ret = transitiveClosure(g);
for(T n : g) {
ret.addEdge(n, n);
}
return ret;
}
/**
* Returns a restriction of the given graph onto the nodes accepted by the given filter.
* @return a restriction of the given graph onto the nodes accepted by the given filter.
*/
public static final <T> Graph<T> restrict(Graph<T> g, Predicate<T> filter) {
final Graph<T> ret = new LinkedHashGraph<T>();
for(T n : g) {
if (filter.test(n)) {
ret.addNode(n);
for(Iterator<? extends T> succs = g.getSuccNodes(n); succs.hasNext(); ) {
final T succ = succs.next();
if (filter.test(succ)) {
ret.addEdge(n, succ);
}
}
}
}
return ret;
}
/**
* Returns a new graph whose edge set consists of the edges in g, plus self-edges on each node in g.
* @return a new graph whose edge set consists of the edges in g, plus self-edges on each node in g.
*/
public static final <T> Graph<T> reflexive(final Graph<T> g) {
final Graph<T> ret = union(Collections.singleton(g));
for(T n : g) {
ret.addEdge(n, n);
}
return ret;
}
/**
* Returns a new graph whose edge set consists of the edges in g, minus any self-edges.
* @return a new graph whose edge set consists of the edges in g, minus any self-edges.
*/
public static final <T> Graph<T> irreflexive(final Graph<T> g) {
final Graph<T> ret = union(Collections.singleton(g));
for(T n : g) {
ret.removeEdge(n, n);
}
return ret;
}
/**
* Returns a new graph that is the union of the nodes and edges in the given
* collection of graphs.
* @return a new graph that is the union of the nodes and edges in the given
* collection of graphs.
*/
public static final <T> Graph<T> union(Collection<Graph<T>> graphs) {
final Graph<T> ret = new LinkedHashGraph<T>();
for(Graph<T> g : graphs) {
for(T n : g) {
ret.addNode(n);
for(Iterator<? extends T> succs = g.getSuccNodes(n); succs.hasNext(); ) {
ret.addEdge(n, succs.next());
}
}
}
return ret;
}
/**
* Returns true if the given graphs are logically equal.
* @return true if the given graphs are logically equal
*/
public static <T> boolean equal(Graph<T> g1, Graph<T> g2) {
if (g1==g2) return true;
else if (g1==null)
return g2==null;
else if (nodes(g1).equals(nodes(g2))) {
for(T n : g1) {
if (!(new HashSet<T>(successors(n, g1))).equals(new HashSet<T>(successors(n, g2))))
return false;
}
return true;
} else
return false;
}
}