package org.testng.internal; import org.testng.collections.ListMultiMap; import org.testng.collections.Lists; import org.testng.collections.Maps; import org.testng.collections.Sets; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.List; import java.util.Map; import java.util.Set; /** * Representation of the graph of methods. */ public class DynamicGraph { private static final boolean DEBUG = false; private List m_nodesReady = Lists.newArrayList(); private List m_nodesRunning = Lists.newArrayList(); private List m_nodesFinished = Lists.newArrayList(); private Comparator m_nodeComparator = null; private ListMultiMap m_dependedUpon = Maps.newListMultiMap(); private ListMultiMap m_dependingOn = Maps.newListMultiMap(); public static enum Status { READY, RUNNING, FINISHED } /** * Define a comparator for the nodes of this graph, which will be used * to order the free nodes when they are asked. */ public void setComparator(Comparator c) { m_nodeComparator = c; } /** * Add a node to the graph. */ public void addNode(T node) { m_nodesReady.add(node); } /** * Add an edge between two nodes. */ public void addEdge(T from, T 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 List getFreeNodes() { List result = Lists.newArrayList(); for (T m : m_nodesReady) { // A node is free if... List du = m_dependedUpon.get(m); // - no other nodes depend on it if (!m_dependedUpon.containsKey(m)) { result.add(m); } else if (getUnfinishedNodes(du).size() == 0) { result.add(m); } } // Sort the free nodes if requested (e.g. priorities) if (result != null && ! result.isEmpty()) { if (m_nodeComparator != null) { Collections.sort(result, m_nodeComparator); ppp("Nodes after sorting:" + result.get(0)); } } return result; } /** * @return a list of all the nodes that have a status other than FINISHED. */ private Collection getUnfinishedNodes(List nodes) { Set 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 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:\n"); for (Map.Entry> es : m_dependingOn.entrySet()) { result.append(" " + es.getKey() + "\n"); for (T t : es.getValue()) { result.append(" " + t + "\n"); } } 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"); List 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.keySet()) { List 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(); } public ListMultiMap getEdges() { return m_dependingOn; } }