Customizing Javadoc's Output

Um die Ausgabe des Javadoc-tools den eigenen Wünschen anzupassen, muß man nur eigene Doclets schreiben. Dadurch kann man dann eigene Tags und eigene Kommandozeilenoptionen verarbeiten, als auch andere Ausgabeformate als Html erzeugen, ohne das man die Quelldatei ändern muß.
Um eigene Doclets zu verwenden, müssen diese (natürlich) zuerst compiliert werden, dabei aber nicht vergessen den Classpath richtig zu setzen (bei mir: e:\jdk1.2.2\lib\tools.jar ,die tool.jar enthält Javadoc und die Implementierungen der Interfaces), und dann werden sie mit -doclet an Javadoc übergeben.

javac -classpath e:\jdk1.2.2\lib\tool.jar MyDoc.java

javadoc -doclet MyDoc -classpath e:\jdk1.2.2\lib\tool.jar MyClass.java

Doclets programmieren

Doclets sind normale Javaprogramme, die eine spezielle Doclet API benutzen, die das Erkennen und Zerlegen des Quelltextes übernimmt und die Informationen weiterliefert. Die Funktionen liegen im Paket sun.com.javadoc.
Doclets besitzen keine main() oder init() Methode, sondern werden über public static boolean start(Root root) gestartet.

Ein einfaches Beispiel für ein eigenes Doclet:

     import com.sun.javadoc.*;

     public class MyDoc {
         public static boolean start(RootDoc root) {
             ClassDoc[] classes = root.classes();
             for (int i = 0; i < classes.length; ++i) {
                 System.out.println(classes[i]);
             }
             return true;
         }
     }

Dieses einfache Doclet, welches nur aus der start(RootDoc root) Methode besteht, gibt die Klassen mit denen Javadoc arbeitet auf StandardOut aus.

ein weiteres das Tags verarbeitet:

     import com.sun.javadoc.*;

     public class ListTags {
         public static boolean start(RootDoc root){
             String tagName = "mytag";
             writeContents(root.classes(), tagName);
             return true;
         }

         private static void writeContents(ClassDoc[] classes, String tagName) {
             for (int i=0; i < classes.length; i++) {
                 boolean classNamePrinted = false;
                 MethodDoc[] methods = classes[i].methods();
                 for (int j=0; j < methods.length; j++) {
                     Tag[] tags = methods[j].tags(tagName);
                     if (tags.length > 0) {
                         if (!classNamePrinted) {
                             System.out.println("\n" + classes[i].name() + "\n");
                             classNamePrinted = true;
                         }
                         System.out.println(methods[j].name());
                         for (int k=0; k < tags.length; k++) {
                             System.out.println("   " + tags[k].name() + ": "
                                 + tags[k].text());
                         }
                     }
                 }
             }
         }
     }

Dieses Doclet sucht im Quelltext nach dem im String tagname angegebenen Tag. Die Ausgabe erfolgt auf StandardOut.

und eins für Kommandozeilenoptionen:

     import com.sun.javadoc.*;

     public class ListTags {
         public static boolean start(RootDoc root){
             String tagName = readOptions(root.options());
             writeContents(root.classes(), tagName);
             return true;
         }

         private static void writeContents(ClassDoc[] classes, String tagName) {
             for (int i=0; i < classes.length; i++) {
                 boolean classNamePrinted = false;
                 MethodDoc[] methods = classes[i].methods();
                 for (int j=0; j < methods.length; j++) {
                     Tag[] tags = methods[j].tags(tagName);
                     if (tags.length > 0) {
                         if (!classNamePrinted) {
                             System.out.println("\n" + classes[i].name() + "\n");
                             classNamePrinted = true;
                         }
                         System.out.println(methods[j].name());
                         for (int k=0; k < tags.length; k++) {
                             System.out.println("   " + tags[k].name() + ": " + tags[k].text());
                         }
                     }
                 }
             }
         }

         private static String readOptions(String[][] options) {
             String tagName = null;
             for (int i = 0; i < options.length; i++) {
                 String[] opt = options[i];
                 if (opt[0].equals("-tag")) {
                     tagName = opt[1];
                 }
             }
             return tagName;
         }

         public static int optionLength(String option) {
             if(option.equals("-tag")) {
                 return 2;
             }
             return 0;
         }

         public static boolean validOptions(String options[][], DocErrorReporter reporter) {
             boolean foundTagOption = false;
             for (int i = 0; i < options.length; i++) {
                 String[] opt = options[i];
                 if (opt[0].equals("-tag")) {
                     if (foundTagOption) {
                         reporter.printError("Only one -tag option allowed.");
                         return false;
                     } else {
                         foundTagOption = true;
                     }
                 }
             }
             if (!foundTagOption) {
                 reporter.printError("Usage: javadoc -tag mytag -doclet ListTags ...");
             }
             return foundTagOption;
         }
     }

Dies ist das vorherige Doclet erweitert um die Möglichkeit den String tagname über die Kommandozeilenfunktion -tag zu setzen.

Wie man an diesen Beispielen sieht, muß das eigene Objekt, das als Doclet dienen soll,  nur die  public static boolean start(RootDoc root) Methode implementieren, da diese von Javadoc aufgerufen wird. Außerdem sieht man hier, daß man kein Interfaces implementieren oder von einer bestimmten Klasse abstammen muß.

Von dem RootDoc - Objekt kann man dann die Objekte bekommen, die die Pakete, Klassen, Methoden, Parameter und Typen repräsentieren. (siehe Skizze)

Die Methoden public static int optionLength(String option) und  public static boolean validOptions(String options[][], DocErrorReporter reporter) sind optional und werden implizit aufgerufen, wenn sie vorhanden sind.

Wenn man das Standardformat beibehalten will, kann man als Ausgangspunkt das Standard-Doclet nehmen,
indem man Unterklassen der verwandten Klassen bildet und in diesen Methoden überschreibt und/oder hinzufügt.

weiter

zurück zur Hauptseite