Flowcharts-R-Us

Joining the dots

So it turns out that a lot of people are incapable of reading, or if they can read, to actually absorb the information in that text in any meaningful way.

So in order to explain a bit of code that is mostly words and a fair bit of punctuation, you might find yourself creating a flowchart that describe how control passes from one statement to another, with little arrows proceeding from one rectangle to another, with the arrows between those rectangles signifying the relentless forward march of time.

j2gv-1

like a recipe, or something with some decision points in it, like

j2gv-2

like a choose-your-own adventure book, if those still exist in this day and age.

And even enlightened, sophisticated people like you or I who know that the Dunnig-Kruger effect isn’t real could do with a diagram to explain the exact sequence of steps involved in your multi-modal authentication scheme for your e-commerce site, if they still call them e-commerce sites in this day and age.

Go on

So if you find yourself in this position, what you’ll probably be doing is creating that diagram in some software called graphviz, so that it can work out the layout for you automatically, and because graphviz diagrams are defined in written lines of text, which as a software developer you find easier to deal with than shapes on a diagram.

And the way in which you’ll be doing that will probably be via reading through the code, converting each statement to a node and each if/else decision to another type of node, and joining them all up with edges, with occasional backwards edges for loops and exceptions and continue/breaks and method calls and returns.

And then you’ll look at that and say something like “Jesus H Christ in a handbasket I’m never going to do that again”, and write some software to do that for you.

Jesus H Christ in a handbasket

So here’s some software I’ve created called java-to-graphviz that takes in java source code as input and converts that into a control flow diagram ( CFD ).

Usually when you parse software you get an abstract syntax tree ( AST ), which is great for compilers, but not so great for users.

For example, when a compiler reads (parses) the following bit of code:

{
    before();
    for (i = 0; i < 10; i++) {
        println(i);
    }
    after();
}

It creates an in-memory structure that looks a bit like this:

So what java-to-graphviz does is create a parallel representation of that, wiring up edges between each node, and the node that executes after each node ( a 'happened-before' relationship ), without thinking too much about memory barriers or threading, or out-of-order optimisations, or class hierarchies, or dynamic dispatch, or any of that sort of malarky.

And nobody cares about the AST, so you can get rid of the red arrows, and shake it out a bit:

j2gv-6

And most people, if they can be persuaded to care about any of this at all, don't want to see most of those nodes and to just get the big picture, which is in fact, a smaller picture, vis-à-vis:

j2gv-7

Which are the kinds of diagrams you're going to be getting out of this thing, if you can persuade it not to go into infinite loops following those back edges.

But hang on, I want those nodes and edges to have slightly more readable names

You can do that by adding some comments to your code with some nicer labels. Those comments will start with // gv: ( for 'graphviz' ) and the text that follows that will represent that node in the diagram.

// gv: like this

But hang on, I want some of those nodes to be formatted differently

Well in that case you can add some styles to those comments with some graphviz node formatting directives.

Those styles will be contained in curly brackets ( { and } ) and look a bit like CSS rules. e.g.

// gv: like this { color: red; }

But hang on, I want all nodes of a particular type to be formatted differently without having to specify the style on each node

Well in that case you can add some classes to your comments by changing the gv: prefix to gv.className: instead, and define your classes in a stylesheet in a comment block somewhere else in the document with a gv-style: prefix,

// gv-style: { .forExample { color: red; } }
// gv.forExample: like this

That style block is actually CSS, by the way, so you can create more complicated rules that apply to nodes with multiple styles, or nested styles, or nodes with certain attributes.

But hang on, I want all my styles to be externalised in a separate file

Well in that case you can use an @import "mystyles.css"; comment which will read them from there instead

// gv-style: { @import "mystyle.css"; }

But hang on, I want some styles to only apply to specific nodes

Well in that case you can use a gv#nodeId: prefix instead, and use a CSS id selector for the rule

// gv-style: { #thisOne { color: green; } }
// gv#thisOne: like this

But hang on, I want some styles to apply to all AST nodes of a particular type

Well in that case you can use a gv.nodeType: prefix, because every AST node will have that AST’s type added as a class automatically.

// gv-style: { .if { shape: diamond; } } 
if (something) {
   ...
}

But hang on, I’ve got multiple nodes on a line, and/or want to label nodes using a comment on the next/previous line

Well in that case you can add a directional modifier ( ^, >, v and < for up, right, down and left ) to your comment so java-to-graphviz knows which specific node the comment applies to, like this:

// gv: this comment has it's own node

println("s1"); // gv: this comment is for s1

// gv:v: this comment is for s2
println("s2"); 
println("s3");
// gv:^: this comment is for s3

println("s4");  println("s5");  // gv: this comment is for s4
println("s6");  println("s7");  // gv:<: this comment is for s7
/* gv:>:this comment is for s8 */ println("s8");  println("snot8");

println("s9");   /* gv:<--:this comment is for s9 */  println("s10");
println("s11");  /* gv:-->:this comment is for s12 */ println("s12");

But hang on, I don’t want all these bloody nodes and edges

Well in that case you can filter out the nodes that you don’t want, and all the edges to those nodes will get collapsed for you.

// gv-keepNode: -expressionStatement -block -switchCase

But hang on, does it hook in via JVMTI and animate when the program runs ? Does it show AOP pointcuts ? Does it show the disassembled bytecodes in the nodes ? Does it create complexity metrics ? Can it colour in nodes using jacoco coverage .exec output ?

No, no, no, no and no.

What about lambdas ?

What about lambdas ?

Does it do those

Well it does now.

But hang on, I want a couple of other things that are too tedious to describe in a blog post

Well in that case there’s a more complete description of the other bells and whistles over on the github site.

Well I’m convinced

Thanks, hypothetical other human being who is going to download and use this thing.

How do I run it ?

If you’re running from the command-line, there’s a few options you can supply:

C:\>java -jar java-to-graphviz-cli.jar
Usage:
  java -jar java-to-graphviz-cli.jar [options] filename filename ...

where [options] are:
 -h -?                       display this help text
 --verbose  -v               increase verbosity ( info level )
 --verbose2 -vv              increase verbosity a bit more ( debug level )
 --format   -f dot|dom1|dom2 output format: DOT diagram (default), pre-styled DOM, post-styled DOM
 --output   -o filename      send output to filename
                             For multiple output files, use {f} for base filename, {i} for diagram index
                             e.g. "{f}-{i}.dot" for dot output, "{f}.html" for dom output
                             default is stdout
 --source   -s version       set Java source language version ( 1.0, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7,
                               8, 9, 10, 11, 12, 13, 14, 15, 16 )

 --base-css -b url           replace base css with url ( relative to classpath, then file://./ )
                               default = JavaToGraphviz.css
 --user-css -u url           add url to list of user css imports
 --css      -c {rules}       add css rules

 --option   -p key=value     set initial option; can be modified in source by 'gv-option' and 'gv-keepNode' directives

Options keys and defaults:
 edgerNamesCsv=control-flow  csv list of edger names
                             possible edger names: control-flow, ast
 enableKeepNodeFilter=false  if true, will perform node filtering
 defaultKeepNode=true        if true, filter will keep nodes by default
 keepNode=                   space-separated nodeTypes for fine-grained keepNode control
                             prefix nodeType with '-' to exclude, '+' or no prefix to include
                               e.g. "-expressionStatement -block -switchCase"
                               to exclude those nodeTypes when defaultKeepNode=true
                             NB: any node with a 'gv' comment will always be kept

e.g.

C:\>java -jar java-to-graphviz-cli.jar --base-css https://raw.githubusercontent.com/randomnoun/java-to-graphviz/master/src/main/resources/JavaToGraphviz.css src\test\java\com\example\input\ForStatement.java
digraph G {
  node [
    shape = rect;
    fontname = "Handlee";
  ]
  edge [
    fontname = "Handlee";
  ]
  bgcolor = transparent;
  fontname = "Handlee";
  compound = true;
  s_19 [
    class = "methodDeclaration";
    label = "MethodDeclaration";
    fillcolor = white;
    style = filled;
  ];
  s_19_3 [
    class = "block";
    label = "Block";
    fillcolor = white;
    style = filled;
  ];
...

How do I run it in maven?

There’s a sample pom.xml over on the java-to-graphviz-maven-plugin site

Source

java-to-graphviz
git@github.com:randomnoun/java-to-graphviz.git

and here’s the maven plugin:

java-to-graphviz-maven-plugin
git@github.com:randomnoun/java-to-graphviz-maven-plugin.git

JARs

If you’re after the CLI jar, you can grab that from maven central.

One Comment

Add a Comment

Your email address will not be published. Required fields are marked *