C has the speed and efficiency of assembly language combined with readability of assembly language
(Famous joke)

This page is deprecated

A newer version of this page is available on this page.

The context

PlantUML uses Graphviz/DOT to compute node positionning for every UML diagrams (except Sequence Diagram). The fact that DOT computes automatically the position of node is a key feature, and algorithms implemented in DOT usually give very good result.

However, there are some drawbacks of using DOT : the computation is done by an external program (dot.exe on Windows, dot on linux), and that means that :

Another way of using DOT would be to use it as a library, and to link it with PlantUML using JNI (Java Native Interface) but that would not be perfect thought.

If a Java version of Graphviz/DOT would exist, this would allow to greatly simplify the installation and the use of PlantUML.

Unfortunatly, this does not exist today...

So what about porting Graphviz/DOT to Java ? As Graphviz/DOT is written in C, and as Java syntax is close to C syntax it is possible to translate C source of Graphviz to Java source, although this is a large and complex task.

The good news is that we don't need to translate the whole Graphviz sources to Java : this is due to the fact that PlantUML uses only a limited portion of Graphviz. Firstly, only DOT algorithm is used (neato, ... and other are not used). Secondly, the parsing of the DOT language is not need : PlantUML could directly build the diagram in memory without generating DOT language. And thirdly all the drawing code of Graphviz is not needed anymore, since the drawing is done in Java by PlantUML.

Actually, we have already tried in 2012 and in 2014. Even those two tries were not successful, we learn a lot about the issues we have when translating C to Java.

Hello World!

It would be too long to explain here how this works (but nothing is hidden, the code is public and open here). To summerize, every C function of GraphViz is translated to a Java static method.

The translation is not finished yet, there are still some large work. However, we have translated enough to be able to run a Java version of GraphViz/Dot a some very simple diagram : two nodes and a single edge between then. The C program is:

#include<stdio.h>
#include "gvc.h"

void pointftoString(pointf coord) {
	printf("(%f ; %f )\n", coord.x, coord.y);
}

void printDebugNode(Agnode_t *n) {
	Agnodeinfo_t *data = (Agnodeinfo_t*) AGDATA(n);
	boxf bb = data->bb;
	printf("*********** PRINT NODE **********\n");
	printf("width=%f\n", data->width);
	printf("height=%f\n", data->height);
	printf("ht=%f\n", data->ht);
	printf("lw=%f\n", data->lw);
	printf("rw=%f\n", data->rw);
	printf("coords=");
	pointftoString(data->coord);
	printf("bb.LL=");
	pointftoString(bb.LL);
	printf("bb.UR=");
	pointftoString(bb.UR);
}

void printDebugBezier(bezier *bezier) {
	int i;
	printf("bezier.size=%d\n", bezier->size);
	printf("bezier.sflag=%d\n", bezier->sflag);
	printf("bezier.eflag=%d\n", bezier->eflag);
	printf("bezier.sp");
	pointftoString(bezier->sp);
	printf("bezier.ep");
	pointftoString(bezier->ep);
	for (i = 0; i < bezier->size; i++) {
		printf("pt=");
		pointftoString(bezier->list[i]);
	}
}


void printDebugEdge(Agedge_t *e) {
	Agedgeinfo_t *data = (Agedgeinfo_t*) AGDATA(e);
	splines *splines = data->spl;
	boxf bb = splines->bb;
	bezier *list = splines->list;
	printf("*********** PRINT EDGE **********\n");
	printf("splines.size=%d\n", splines->size);
	printf("bb.LL=");
	pointftoString(bb.LL);
	printf("bb.UR=");
	pointftoString(bb.UR);
	printDebugBezier(&list[0]);
}


main(int argc, char **argv)
{
Agraph_t *g;
Agnode_t *n, *m, *m2;
Agedge_t *e, *e2;
Agsym_t *a;
GVC_t *gvc;

/* set up a graphviz context */
gvc = gvContext();

/* parse command line args - minimally argv[0] sets layout engine */
// gvParseArgs(gvc, argc, argv);
/* Create a simple digraph */
g = agopen("g", Agdirected, NULL);
n = agnode(g, "n", 1);
m = agnode(g, "m", 1);
//m2 = agnode(g, "m2", 1);
e = agedge(g, n, m, 0, 1);
//e2 = agedge(g, n, m2, 0, 1);
/* Set an attribute - in this case one that affects the visible rendering */
//agsafeset(n, "color", "red", "");
/* Compute a layout using layout engine from command line args */
int ret = gvLayoutJobs(gvc, g);

printDebugNode(n);
printDebugNode(m);
printDebugEdge(e);

}
The corresponding Java program:
public class Demo01 {

  public static void main(String[] args) throws IOException {

    Agraph_s g = agopen(new CString("g"), Agdirected, null);
    Agnode_s n = agnode(g, new CString("n"), true);
    Agnode_s m = agnode(g, new CString("m"), true);
    // Agnode_s m2 = agnode(g, new CString("m2"), true);
    Agedge_s e = agedge(g, n, m, null, true);
    // Agedge_s e2 = agedge(g, n, m2, null, true);
    System.err.println("n=" + n);
    System.err.println("m=" + m);
    // System.err.println("m=" + m2);
    System.err.println("e=" + e);

    GVC_s gvc = gvContext();

    /* Compute a layout using layout engine from command line args */
    gvLayoutJobs(gvc, g);

    printDebugNode(n);
    printDebugNode(m);
    printDebugEdge(e);
  }


  private static void printDebugEdge(Agedge_s e) {
    System.err.println("*********** PRINT EDGE ********** " + getUID(e));
    final Agedgeinfo_t data = (Agedgeinfo_t) Macro.AGDATA(e)
	  .castTo(Agedgeinfo_t.class);
    final splines splines = (splines) data.getPtr("spl");
    __struct__<boxf> bb = splines.getStruct("bb");
    final bezier list = (bezier) splines.getPtr("list");
    System.err.println("splines.UID=" + ((StarStruct) splines).getUID36());
    System.err.println("splines.size=" + splines.getInt("size"));
    System.err.println("bb.LL=" + pointftoString(bb.getStruct("LL")));
    System.err.println("bb.UR=" + pointftoString(bb.getStruct("UR")));
    printDebugBezier((bezier) splines.getPtr("list").getPtr());

  }

  public static String getUID(Object o) {
    if (o instanceof StarArray) {
      return ((StarArray) o).getUID36();
    }
    return ((StarStruct) o).getUID36();
  }

  private static void printDebugBezier(bezier bezier) {
    System.err.println("bezier.size=" + bezier.getInt("size"));
    System.err.println("bezier.sflag=" + bezier.getInt("sflag"));
    System.err.println("splines.eflag=" + bezier.getInt("eflag"));
    System.err.println("bezier.sp=" + pointftoString(bezier.getStruct("sp")));
    System.err.println("bezier.ep=" + pointftoString(bezier.getStruct("ep")));
    System.err.println("bezier.list=" + getUID(bezier.getPtr("list")));
    for (int i = 0; i < bezier.getInt("size"); i++) {
      final __ptr__ pt = bezier.getPtr("list").plus(i).getPtr();
      System.err.println("pt=" + pointftoString(pt));
    }

  }

  private static void printDebugNode(Agnode_s n) {
    System.err.println("*********** PRINT NODE ********** ");
    final Agnodeinfo_t data = (Agnodeinfo_t) Macro.AGDATA(n)
	  .castTo(Agnodeinfo_t.class);
    System.err.println("width=" + data.getDouble("width"));
    System.err.println("height=" + data.getDouble("height"));
    System.err.println("ht=" + data.getDouble("ht"));
    System.err.println("lw=" + data.getDouble("lw"));
    System.err.println("rw=" + data.getDouble("rw"));
    System.err.println("coord=" + pointftoString(data.getStruct("coord")));

    __struct__<boxf> bb = data.getStruct("bb");
    System.err.println("bb.LL=" + pointftoString(bb.getStruct("LL")));
    System.err.println("bb.UR=" + pointftoString(bb.getStruct("UR")));
    // TODO Auto-generated method stub
  }

  private static String pointftoString(__struct__<pointf> point) {
    final StringBuilder sb = new StringBuilder();
    sb.append("(");
    sb.append(point.getDouble("x"));
    sb.append(" ; ");
    sb.append(point.getDouble("y"));
    sb.append(")");
    return sb.toString();

  }

  private static String pointftoString(__ptr__ point) {
    final StringBuilder sb = new StringBuilder();
    sb.append("(");
    sb.append(point.getDouble("x"));
    sb.append(" ; ");
    sb.append(point.getDouble("y"));
    sb.append(")");
    return sb.toString();
  }
}
Running Demo01.java class prints the following result:
*********** PRINT NODE ********** 
SKIPPING gv_fixLocale
width=0.75
height=0.5
ht=36.0
lw=27.0
rw=27.0
coord=(27.0 ; 90.0)
bb.LL=(0.0 ; 0.0)
bb.UR=(0.0 ; 0.0)
*********** PRINT NODE ********** 
width=0.75
height=0.5
ht=36.0
lw=27.0
rw=27.0
coord=(27.0 ; 18.0)
bb.LL=(0.0 ; 0.0)
bb.UR=(0.0 ; 0.0)
*********** PRINT EDGE ********** am
splines.UID=ehr
splines.size=1
bb.LL=(0.0 ; 0.0)
bb.UR=(0.0 ; 0.0)
bezier.size=4
bezier.sflag=0
splines.eflag=1
bezier.sp=(0.0 ; 0.0)
bezier.ep=(27.0 ; 36.10433368267388)
bezier.list=ei4
pt=(27.0 ; 71.69662499427795)
pt=(27.0 ; 63.982712746350444)
pt=(27.0 ; 54.712457302958484)
pt=(27.0 ; 46.11242283518388)

And this is the same result as the C program!

What's next ?

A lot of work!

Clusters, edges decoration... and many other things are not working because the corresponding code is not translated yet.

However, we have proven that it is feasible to translate GraphViz/DOT in Java.

The next step is to run a slightly more complex diagram, with 3 nodes and 2 edges.

Then we will have to write some glue code to call this very basic version of DOT in Java. This will allow the generation of very simple Class Diagrams without GraphViz/DOT installed.


Privacy Policy      Advertise