Moved jdvue utility from ONOS-tools repo into onos repo.

Change-Id: I0bc1cef80541075c800c5309cb642a244a79fa0b
diff --git a/utils/jdvue/src/main/java/org/onlab/jdvue/DependencyViewer.java b/utils/jdvue/src/main/java/org/onlab/jdvue/DependencyViewer.java
new file mode 100644
index 0000000..a210d71
--- /dev/null
+++ b/utils/jdvue/src/main/java/org/onlab/jdvue/DependencyViewer.java
@@ -0,0 +1,188 @@
+package org.onlab.jdvue;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.ObjectWriter;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+
+import java.io.BufferedReader;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.Set;
+
+/**
+ * Generator of a self-contained HTML file which serves as a GUI for
+ * visualizing Java package dependencies carried in the supplied catalog.
+ *
+ * The HTML file is an adaptation of D3.js Hierarchical Edge Bundling as
+ * shown at http://bl.ocks.org/mbostock/7607999.
+ *
+ * @author Thomas Vachuska
+ */
+public class DependencyViewer {
+
+    private static final String JPD_EXT = ".db";
+    private static final String HTML_EXT = ".html";
+
+    private static final String INDEX = "index.html";
+    private static final String D3JS = "d3.v3.min.js";
+
+    private static final String TITLE_PLACEHOLDER = "TITLE_PLACEHOLDER";
+    private static final String D3JS_PLACEHOLDER = "D3JS_PLACEHOLDER";
+    private static final String DATA_PLACEHOLDER = "DATA_PLACEHOLDER";
+
+    private final Catalog catalog;
+
+    /**
+     * Creates a Java package dependency viewer.
+     *
+     * @param catalog dependency catalog
+     */
+    public DependencyViewer(Catalog catalog) {
+        this.catalog = catalog;
+    }
+
+    /**
+     * Main program entry point.
+     *
+     * @param args command line arguments
+     */
+    public static void main(String[] args) {
+        Catalog cat = new Catalog();
+        DependencyViewer viewer = new DependencyViewer(cat);
+        try {
+            String path = args[0];
+            cat.load(path + JPD_EXT);
+            cat.analyze();
+
+            System.err.println(cat);
+            viewer.dumpLongestCycle(cat);
+            viewer.writeHTMLFile(path);
+        } catch (IOException e) {
+            System.err.println("Unable to process catalog: " + e.getMessage());
+        }
+    }
+
+    /**
+     * Prints out the longest cycle; just for kicks.
+     * @param cat catalog
+     */
+    private void dumpLongestCycle(Catalog cat) {
+        DependencyCycle longest = null;
+        for (DependencyCycle cycle : cat.getCycles()) {
+            if (longest == null || longest.getCycleSegments().size() < cycle.getCycleSegments().size()) {
+                longest = cycle;
+            }
+        }
+
+        if (longest != null) {
+            for (Dependency dependency : longest.getCycleSegments()) {
+                System.out.println(dependency);
+            }
+        }
+    }
+
+    /**
+     * Writes the HTML catalog file for the given viewer.
+     *
+     * @param path base file path
+     * @throws IOException if issues encountered writing the HTML file
+     */
+    public void writeHTMLFile(String path) throws IOException {
+        String index = slurp(getClass().getResourceAsStream(INDEX));
+        String d3js = slurp(getClass().getResourceAsStream(D3JS));
+
+        FileWriter fw = new FileWriter(path + HTML_EXT);
+        ObjectWriter writer = new ObjectMapper().writer(); // .writerWithDefaultPrettyPrinter();
+        fw.write(index.replace(TITLE_PLACEHOLDER, path)
+                         .replace(D3JS_PLACEHOLDER, d3js)
+                         .replace(DATA_PLACEHOLDER, writer.writeValueAsString(toJson())));
+        fw.close();
+    }
+
+    /**
+     * Slurps the specified input stream into a string.
+     *
+     * @param stream input stream to be read
+     * @return string containing the contents of the input stream
+     * @throws IOException if issues encountered reading from the stream
+     */
+     static String slurp(InputStream stream) throws IOException {
+        StringBuilder sb = new StringBuilder();
+        BufferedReader br = new BufferedReader(new InputStreamReader(stream));
+        String line;
+        while ((line = br.readLine()) != null) {
+            sb.append(line).append(System.lineSeparator());
+        }
+        br.close();
+        return sb.toString();
+    }
+
+    // Produces a JSON structure designed to drive the hierarchical visual
+    // representation of Java package dependencies and any dependency cycles
+     private JsonNode toJson() {
+        ObjectMapper mapper = new ObjectMapper();
+        ObjectNode root = mapper.createObjectNode();
+        root.put("packages", jsonPackages(mapper));
+        root.put("cycleSegments", jsonCycleSegments(mapper, catalog.getCycleSegments()));
+        root.put("summary", jsonSummary(mapper));
+        return root;
+    }
+
+    // Produces a JSON summary of dependencies
+    private JsonNode jsonSummary(ObjectMapper mapper) {
+        ObjectNode summary = mapper.createObjectNode();
+        summary.put("packages", catalog.getPackages().size());
+        summary.put("sources", catalog.getSources().size());
+        summary.put("cycles", catalog.getCycles().size());
+        summary.put("cycleSegments", catalog.getCycleSegments().size());
+        return summary;
+    }
+
+    // Produces a JSON structure with package dependency data
+    private JsonNode jsonPackages(ObjectMapper mapper) {
+        ArrayNode packages = mapper.createArrayNode();
+        for (JavaPackage javaPackage : catalog.getPackages()) {
+            packages.add(json(mapper, javaPackage));
+        }
+        return packages;
+    }
+
+    // Produces a JSON structure with all cyclic segments
+    private JsonNode jsonCycleSegments(ObjectMapper mapper,
+                                       Set<Dependency> segments) {
+        ObjectNode cyclicSegments = mapper.createObjectNode();
+        for (Dependency dependency : segments) {
+            String s = dependency.getSource().name();
+            String t = dependency.getTarget().name();
+            cyclicSegments.put(t + "-" + s,
+                               mapper.createObjectNode().put("s", s).put("t", t));
+        }
+        return cyclicSegments;
+    }
+
+    // Produces a JSON object structure describing the specified Java package.
+    private JsonNode json(ObjectMapper mapper, JavaPackage javaPackage) {
+        ObjectNode node = mapper.createObjectNode();
+
+        ArrayNode imports = mapper.createArrayNode();
+        for (JavaPackage dependency : javaPackage.getDependencies()) {
+            imports.add(dependency.name());
+        }
+
+        Set<DependencyCycle> packageCycles = catalog.getPackageCycles(javaPackage);
+        Set<Dependency> packageCycleSegments = catalog.getPackageCycleSegments(javaPackage);
+
+        node.put("name", javaPackage.name());
+        node.put("size", javaPackage.getSources().size());
+        node.put("imports", imports);
+        node.put("cycleSegments", jsonCycleSegments(mapper, packageCycleSegments));
+        node.put("cycleCount", packageCycles.size());
+        node.put("cycleSegmentCount", packageCycleSegments.size());
+        return node;
+    }
+
+}