RegionViewer

Introduction

RegionViewer allows you to export a region (graph of objects on a JVM) to several different types of graph files. The most useful type of output graph file is GraphViz format. These files have a .dot extension. Using GraphViz tools, you can easily convert the dot file to many different formats, such as SVG.

RegionViewer uses some heuristics to decide what details to show and which to suppress. It is smart enough to detect and avoid loops. An example output looks like the following.

Example export of two Java Strings

Example export of two Java Strings

This above example shows the export from two Java Strings. The Strings were defined like this:

String brother = "brother";
String the = brother.substring(3, 6);

The RegionViewer was given two object references, brother and the. The two rectangles at the top of the graph that are labeled "brother" and "the" (do not have a border) are the object references passed into the RegionViewer code.

The RegionViewer first followed all the objects reachable from brother and colored those nodes red-orange, and then followed the objects reachable from the and colored those nodes yellow-green. Any object previously visited was not exported a second time.

This example shows Java's immutable String objects exhibiting structural sharing. The code used to generate this graph is in the RegionViewer project in the file acceptanceTest/net/slreynolds/ds/SimplestExample.java. You can read and run this Java file to see how to use the RegionViewer. Here is the essential part of that code:

String brother = "brother";
String the = brother.substring(3, 6);

HashMap<String,Object> options = new HashMap<String,Object>();
options.put(ExporterOptions.OUTPUT_PATH, "simplest_strings.dot");

ObjectSaver gvizSaver = new ObjectSaver(new GraphVizExporter());
gvizSaver.save(new Object[]{brother,the},
                   new String[]{"brother","the"}, 
                   options);

To convert the resulting output dot file to SVG, use the GraphViz command dot -O -Tsvg simplest_strings.dot. Some other interesting examples are in SecondTest.java from the same directory.

Changing Export Colors

The default colors used by the exporter may not appeal to you. You can easily change them, see the example acceptanceTest/net/slreynolds/ds/CustomColorsExample.java. The important lines of code are the following:

GraphVizExporter exporter = new GraphVizExporter();
List<Color> colors = new ArrayList<Color>();
colors.add(Color.GRAY);
colors.add(Color.LIGHT_GRAY);
exporter.setColors(colors);
ObjectSaver gvizSaver = new ObjectSaver(exporter);

You must ensure that the number of colors given to the exporter matches or exceeds the number of object references given to the saver.

Example showing custom export colors

Example showing custom export colors

Using a Different Exporter

RegionViewer can export to other graph file types. You just specify the exporter you want to use when you construct the saver. The code from the SimplerGraphVizExporterExample.java looks like this

ObjectSaver gvizSaver = new ObjectSaver(new SimpleGraphVizExporter());

The output from this example looks like the following.

ArrayList example using SimpleGraphVisExporter

ArrayList example using SimpleGraphVisExporter

The same example using the standard GraphVizExporter looks like the following.

ArrayList example using GraphVisExporter

ArrayList example using GraphVisExporter

The following exporters are curently provided

The two GraphViz exporters are the easiest and most flexible to use. The TulipExporter is for the Tulip data visualization application. This works very well, but can only save to bitmap images. The GraphMLExporter saves to graphml. This works but does not preserve node colors and so is less useful.

Building RegionViewer

RegionViewer has a gradle build file, and can be built with the command (in the root project directory) gradle build. The jar file is created at build/libs/RegionViewer.jar.

More Examples

Some other examples are the following.

Export of a Java ArrayList

Export of a Java ArrayList

This above example shows the export from a Java ArrayList that contains two objects.

Export of a Java LinkedList

Export of a Java LinkedList

This above example shows the export from a Java ArrayList that contains two objects.

Export of a Scala HashMap

Export of a Scala HashMap

This above example shows the export from a Scala HashMap and a map derived from the original by adding a key/value. These last two Scala examples were generated by code in the ScalaRegionExamples project.

Simpler export of a Scala HashMap

Simpler export of a Scala HashMap

This above example shows the same scenario as above but using the simpler GraphViz exporter (SimpleGraphVizExporter). This exporter suppresses all primitive fields and therefore only shows objects and their relationships.

Export of a Clojure HashMap

Export of a Clojure HashMap

This above example shows the export from a Clojure HashMap and a map derived from the original by adding a key/value. This Clojure example was generated by code in the ClojureRegionExamples project.

Suppression of Details

The RegionViewer uses some heuristics to decide which fields should be exported and which should be suppressed. Depending on your interests and your use case, the heuristics may not be suitable. These heuristics takes two forms

  1. The NodeBuilder exports fields in the given object instance Class and recurses up the the instances parent classes. This process continues until the parent class is in a different package than the given object instance. Fields from parents in different packages are not exported.

  2. Some fields and some objects are just not interesting. For example static fields are not exported. Also objects like Threads are not exported to conserve space.

It is anticipated that future versions of RegionView will make these heuristics pluggable. At this point, if you need to change them, you will need to change some code.

  1. The heuristic that truncates exporting when the parent is in a different package is in the method NodeBuilder.buildNode. Look for the following line
    while (clazz != null && classNameToPackage(clazz.getName()).equals(packageNameOfInstance)) {

  2. The second heuristic is implemented in the method NodeBuilde.shouldFollowField. If this method returns false, then the field will be skipped. You can easily add or remove conditions from this method.