blob: db1c8cdf2223b4c906a74a5b91907380c3346ce5 [file] [log] [blame]
package org.testng.internal;
import org.testng.internal.annotations.Sets;
import java.util.Collection;
import java.util.List;
import java.util.Set;
/**
* Representation of the graph of methods.
*/
public class DynamicGraph<T> {
private static final boolean DEBUG = false;
private Set<T> m_nodesReady = Sets.newHashSet();
private Set<T> m_nodesRunning = Sets.newHashSet();
private Set<T> m_nodesFinished = Sets.newHashSet();
private MapList<T, T> m_dependedUpon = new MapList<T, T>();
private MapList<T, T> m_dependingOn = new MapList<T, T>();
public static enum Status {
READY, RUNNING, FINISHED
}
/**
* Add a node to the graph.
*/
public void addNode(T node) {
m_nodesReady.add(node);
}
/**
* Add an edge between two nodes, which don't have to already be in the graph
* (they will be added by this method).
*/
public void addEdge(T from, T to) {
addNode(from);
addNode(to);
m_dependingOn.put(to, from);
m_dependedUpon.put(from, to);
}
/**
* @return a set of all the nodes that don't depend on any other nodes.
*/
public Set<T> getFreeNodes() {
Set<T> result = Sets.newHashSet();
for (T m : m_nodesReady) {
// A node is free if...
// - no other nodes depend on it
if (!m_dependedUpon.containsKey(m)) result.add(m);
// - or all the nodes that it depends on have already run
else if (getUnfinishedNodes(m_dependedUpon.get(m)).size() == 0) result.add(m);
}
return result;
}
/**
* @return a list of all the nodes that have a status other than FINISHED.
*/
private Collection<? extends T> getUnfinishedNodes(List<T> nodes) {
Set<T> result = Sets.newHashSet();
for (T node : nodes) {
if (m_nodesReady.contains(node) || m_nodesRunning.contains(node)) result.add(node);
}
return result;
}
/**
* Set the status for a set of nodes.
*/
public void setStatus(Collection<T> nodes, Status status) {
for (T n : nodes) {
setStatus(n, status);
}
}
/**
* Set the status for a node.
*/
public void setStatus(T node, Status status) {
removeNode(node);
switch(status) {
case READY: m_nodesReady.add(node); break;
case RUNNING: m_nodesRunning.add(node); break;
case FINISHED: m_nodesFinished.add(node); break;
default: throw new IllegalArgumentException();
}
}
private void removeNode(T node) {
if (!m_nodesReady.remove(node)) {
if (!m_nodesRunning.remove(node)) {
m_nodesFinished.remove(node);
}
}
}
/**
* @return the number of nodes in this graph.
*/
public int getNodeCount() {
int result = m_nodesReady.size() + m_nodesRunning.size() + m_nodesFinished.size();
return result;
}
public int getNodeCountWithStatus(Status status) {
switch(status) {
case READY: return m_nodesReady.size();
case RUNNING: return m_nodesRunning.size();
case FINISHED: return m_nodesFinished.size();
default: throw new IllegalArgumentException();
}
}
private static void ppp(String string) {
if (DEBUG) {
System.out.println(" [GroupThreadPoolExecutor] " + Thread.currentThread().getId() + " "
+ string);
}
}
@Override
public String toString() {
StringBuilder result = new StringBuilder("[DynamicGraph ");
result.append("\n Ready:" + m_nodesReady);
result.append("\n Running:" + m_nodesRunning);
result.append("\n Finished:" + m_nodesFinished);
result.append("\n Edges:" + m_dependingOn);
result.append("]");
return result.toString();
}
private String getName(T t) {
String s = t.toString();
int n1 = s.lastIndexOf('.') + 1;
int n2 = s.indexOf('(');
return s.substring(n1, n2);
}
/**
* @return a .dot file (GraphViz) version of this graph.
*/
public String toDot() {
String FREE = "[style=filled color=yellow]";
String RUNNING = "[style=filled color=green]";
String FINISHED = "[style=filled color=grey]";
StringBuilder result = new StringBuilder("digraph g {\n");
Set<T> freeNodes = getFreeNodes();
String color;
for (T n : m_nodesReady) {
color = freeNodes.contains(n) ? FREE : "";
result.append(" " + getName(n) + color + "\n");
}
for (T n : m_nodesRunning) {
color = freeNodes.contains(n) ? FREE : RUNNING;
result.append(" " + getName(n) + color + "\n");
}
for (T n : m_nodesFinished) {
result.append(" " + getName(n) + FINISHED+ "\n");
}
result.append("\n");
for (T k : m_dependingOn.getKeys()) {
List<T> nodes = m_dependingOn.get(k);
for (T n : nodes) {
String dotted = m_nodesFinished.contains(k) ? "style=dotted" : "";
result.append(" " + getName(k) + " -> " + getName(n) + " [dir=back " + dotted + "]\n");
}
}
result.append("}\n");
return result.toString();
}
}