Free: A Java Doclet Producing Inheritance GIFs Using Graphviz's Dot tool
In the last three hours I wrote this doclet -- a javadoc plugin. This
doclet generates a GIF image for each package. The GIF image shows the
inheritance tree for all types in package. There is one mandatory
parameter to doclet: the root directory where to put generated dot
files and gif files. I suspect that this is etiher the source root or
compiled classes root or a documentaiton root. The source assumes a
location for Graphviz's dot tool. For this doclet to work in a
different environment the location of Graphviz's dot tool may need to
be changed.
Here is the doclet class:
import com.sun.javadoc.*;
import java.io.*;
import java.util.*;
public class DotInheritance {
private static final String dotexe =
"c:\\graphviz\\Graphviz\\bin\\dot " +
"-Tgif inheritance.dot -o inheritance.gif";
public static boolean start(RootDoc root) {
String outputtree = readOptions(root.options());
PackageDoc packs[] = root.specifiedPackages();
for (int i=0; i<packs.length; i++) {
String packname = ""+packs[i];
String packdir = packname.replace(".", File.separator );
ClassDoc classes[] = packs[i].allClasses(false);
File output = new File(outputtree,
packdir+File.separator+"inheritance.dot");
//System.out.println(""+output);
if (makeDotFile(output, classes))
makeGifFile(output);
}
return true;
}
private static class NodeWriter extends PrintStream {
HashMap<Type, String> names = new HashMap();
int index = 0;
NodeWriter(FileOutputStream fos) {
super(fos);
}
private String getName(Type t) {
if (!names.containsKey(t)) {
index++;
String name = "c"+index;
String modstr = "";
if (t instanceof ProgramElementDoc)
modstr = ((ProgramElementDoc)t).modifiers() + "\\n";
println(" "+name+" [shape=box,label=\""+modstr+t+"\"]");
names.put(t,name);
}
return names.get(t);
}
void connect(Type superType, Type subType) {
String supername = getName(superType);
String subname = getName(subType);
println(" "+supername+" -> "+subname);
}
void assureShown(Type t) {
getName(t);
}
}
private static boolean makeDotFile(File out, ClassDoc classes[]) {
boolean success = true;
try {
NodeWriter writer = new NodeWriter(new FileOutputStream(out));
writer.println("digraph inheritance {");
// writer.println(" size = \"11,8\" ");
for (int i=0; i<classes.length; i++) {
writer.assureShown(classes[i]);
Type superType = classes[i].superclassType();
if (superType!=null &&
!"java.lang.Object".equals(""+superType))
writer.connect(superType, classes[i]);
for (Type t: classes[i].interfaceTypes()) {
writer.connect(t,classes[i]);
}
}
writer.println("}");
writer.close();
} catch (IOException e) {
System.err.println("Failed creating " + out);
success = false;
}
return success;
}
private static void makeGifFile(File out) {
Runtime runtime = Runtime.getRuntime();
try {
Process proc = runtime.exec(dotexe,null,out.getParentFile());
if (proc.waitFor() != 0) {
System.err.println("Failed making gif of " + out);
}
} catch (InterruptedException e) {
System.err.println(e);
} catch (IOException e) {
System.err.println(e);
}
}
private static final String outdirtag = "-output_directory_tree";
public static int optionLength(String option) {
if (option.equals(outdirtag)) {
return 2;
}
return 0;
}
public static boolean validOptions(String options[][],
DocErrorReporter reporter) {
int noutdirtags = 0;
for (int i = 0; i<options.length; i++) {
String[] opt = options[i];
if (opt[0].equals(outdirtag)) {
noutdirtags++;
} else {
//System.out.println("unused option: "+opt[0]);
}
}
switch (noutdirtags) {
case 1:
break;
case 0:
default:
reporter.printError(
"Usage: javadoc "+outdirtag+" $dir -doclet DotInheritance
....");
break;
}
return noutdirtags==1;
}
private static String readOptions(String[][] options) {
String tagValue = null;
for (int i = 0; i < options.length; i++) {
String[] opt = options[i];
if (opt[0].equals(outdirtag)) {
tagValue = opt[1];
}
}
return tagValue;
}
}
opalpa
opalpa@gmail.com
http://opalpa.info/