Home | History | Annotate | Download | only in internal
      1 package org.testng.internal;
      2 
      3 import org.testng.collections.ListMultiMap;
      4 import org.testng.collections.Lists;
      5 import org.testng.collections.Maps;
      6 import org.testng.collections.Sets;
      7 
      8 import java.util.Collection;
      9 import java.util.Collections;
     10 import java.util.Comparator;
     11 import java.util.List;
     12 import java.util.Map;
     13 import java.util.Set;
     14 
     15 /**
     16  * Representation of the graph of methods.
     17  */
     18 public class DynamicGraph<T> {
     19   private static final boolean DEBUG = false;
     20 
     21   private List<T> m_nodesReady = Lists.newArrayList();
     22   private List<T> m_nodesRunning = Lists.newArrayList();
     23   private List<T> m_nodesFinished = Lists.newArrayList();
     24 
     25   private Comparator<? super T> m_nodeComparator = null;
     26 
     27   private ListMultiMap<T, T> m_dependedUpon = Maps.newListMultiMap();
     28   private ListMultiMap<T, T> m_dependingOn = Maps.newListMultiMap();
     29 
     30   public static enum Status {
     31     READY, RUNNING, FINISHED
     32   }
     33 
     34   /**
     35    * Define a comparator for the nodes of this graph, which will be used
     36    * to order the free nodes when they are asked.
     37    */
     38   public void setComparator(Comparator<? super T> c) {
     39     m_nodeComparator = c;
     40   }
     41 
     42   /**
     43    * Add a node to the graph.
     44    */
     45   public void addNode(T node) {
     46     m_nodesReady.add(node);
     47   }
     48 
     49   /**
     50    * Add an edge between two nodes.
     51    */
     52   public void addEdge(T from, T to) {
     53     m_dependingOn.put(to, from);
     54     m_dependedUpon.put(from, to);
     55   }
     56 
     57   /**
     58    * @return a set of all the nodes that don't depend on any other nodes.
     59    */
     60   public List<T> getFreeNodes() {
     61     List<T> result = Lists.newArrayList();
     62     for (T m : m_nodesReady) {
     63       // A node is free if...
     64 
     65       List<T> du = m_dependedUpon.get(m);
     66       // - no other nodes depend on it
     67       if (!m_dependedUpon.containsKey(m)) {
     68         result.add(m);
     69       } else if (getUnfinishedNodes(du).size() == 0) {
     70         result.add(m);
     71       }
     72     }
     73 
     74     // Sort the free nodes if requested (e.g. priorities)
     75     if (result != null && ! result.isEmpty()) {
     76       if (m_nodeComparator != null) {
     77         Collections.sort(result, m_nodeComparator);
     78         ppp("Nodes after sorting:" + result.get(0));
     79       }
     80     }
     81 
     82     return result;
     83   }
     84 
     85   /**
     86    * @return a list of all the nodes that have a status other than FINISHED.
     87    */
     88   private Collection<? extends T> getUnfinishedNodes(List<T> nodes) {
     89     Set<T> result = Sets.newHashSet();
     90     for (T node : nodes) {
     91       if (m_nodesReady.contains(node) || m_nodesRunning.contains(node)) {
     92         result.add(node);
     93       }
     94     }
     95     return result;
     96   }
     97 
     98   /**
     99    * Set the status for a set of nodes.
    100    */
    101   public void setStatus(Collection<T> nodes, Status status) {
    102     for (T n : nodes) {
    103       setStatus(n, status);
    104     }
    105   }
    106 
    107   /**
    108    * Set the status for a node.
    109    */
    110   public void setStatus(T node, Status status) {
    111     removeNode(node);
    112     switch(status) {
    113       case READY: m_nodesReady.add(node); break;
    114       case RUNNING: m_nodesRunning.add(node); break;
    115       case FINISHED: m_nodesFinished.add(node); break;
    116       default: throw new IllegalArgumentException();
    117     }
    118   }
    119 
    120   private void removeNode(T node) {
    121     if (!m_nodesReady.remove(node)) {
    122       if (!m_nodesRunning.remove(node)) {
    123         m_nodesFinished.remove(node);
    124       }
    125     }
    126   }
    127 
    128   /**
    129    * @return the number of nodes in this graph.
    130    */
    131   public int getNodeCount() {
    132     int result = m_nodesReady.size() + m_nodesRunning.size() + m_nodesFinished.size();
    133     return result;
    134   }
    135 
    136   public int getNodeCountWithStatus(Status status) {
    137     switch(status) {
    138       case READY: return m_nodesReady.size();
    139       case RUNNING: return m_nodesRunning.size();
    140       case FINISHED: return m_nodesFinished.size();
    141       default: throw new IllegalArgumentException();
    142     }
    143   }
    144 
    145   private static void ppp(String string) {
    146     if (DEBUG) {
    147       System.out.println("   [GroupThreadPoolExecutor] " + Thread.currentThread().getId() + " "
    148           + string);
    149     }
    150   }
    151 
    152   @Override
    153   public String toString() {
    154     StringBuilder result = new StringBuilder("[DynamicGraph ");
    155     result.append("\n  Ready:" + m_nodesReady);
    156     result.append("\n  Running:" + m_nodesRunning);
    157     result.append("\n  Finished:" + m_nodesFinished);
    158     result.append("\n  Edges:\n");
    159     for (Map.Entry<T, List<T>> es : m_dependingOn.entrySet()) {
    160       result.append("     " + es.getKey() + "\n");
    161       for (T t : es.getValue()) {
    162         result.append("        " + t + "\n");
    163       }
    164     }
    165     result.append("]");
    166     return result.toString();
    167   }
    168 
    169   private String getName(T t) {
    170     String s = t.toString();
    171     int n1 = s.lastIndexOf('.') + 1;
    172     int n2 = s.indexOf('(');
    173     return s.substring(n1, n2);
    174   }
    175 
    176   /**
    177    * @return a .dot file (GraphViz) version of this graph.
    178    */
    179   public String toDot() {
    180     String FREE = "[style=filled color=yellow]";
    181     String RUNNING = "[style=filled color=green]";
    182     String FINISHED = "[style=filled color=grey]";
    183     StringBuilder result = new StringBuilder("digraph g {\n");
    184     List<T> freeNodes = getFreeNodes();
    185     String color;
    186     for (T n : m_nodesReady) {
    187       color = freeNodes.contains(n) ? FREE : "";
    188       result.append("  " + getName(n) + color + "\n");
    189     }
    190     for (T n : m_nodesRunning) {
    191       color = freeNodes.contains(n) ? FREE : RUNNING;
    192       result.append("  " + getName(n) + color + "\n");
    193     }
    194     for (T n : m_nodesFinished) {
    195       result.append("  " + getName(n) + FINISHED+ "\n");
    196     }
    197     result.append("\n");
    198 
    199     for (T k : m_dependingOn.keySet()) {
    200       List<T> nodes = m_dependingOn.get(k);
    201       for (T n : nodes) {
    202         String dotted = m_nodesFinished.contains(k) ? "style=dotted" : "";
    203         result.append("  " + getName(k) + " -> " + getName(n) + " [dir=back " + dotted + "]\n");
    204       }
    205     }
    206     result.append("}\n");
    207 
    208     return result.toString();
    209   }
    210 
    211   public ListMultiMap<T, T> getEdges() {
    212     return m_dependingOn;
    213   }
    214 
    215 }
    216