aboutsummaryrefslogtreecommitdiffstats
path: root/framework/src/onos/utils/jdvue/src/main/java/org/onlab/jdvue/DependencyViewer.java
diff options
context:
space:
mode:
Diffstat (limited to 'framework/src/onos/utils/jdvue/src/main/java/org/onlab/jdvue/DependencyViewer.java')
-rw-r--r--framework/src/onos/utils/jdvue/src/main/java/org/onlab/jdvue/DependencyViewer.java188
1 files changed, 188 insertions, 0 deletions
diff --git a/framework/src/onos/utils/jdvue/src/main/java/org/onlab/jdvue/DependencyViewer.java b/framework/src/onos/utils/jdvue/src/main/java/org/onlab/jdvue/DependencyViewer.java
new file mode 100644
index 00000000..a210d717
--- /dev/null
+++ b/framework/src/onos/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;
+ }
+
+}