Java

Doclet Overview

Contents

The Basics

Doclets are programs written in the JavaTM programming language that use the doclet API to specify the content and format of the output of the Javadoc tool. By default, the Javadoc tool uses the "standard" doclet provided by SunTM to generate API documentation in HTML form. However, you can supply your own doclets to customize the output of Javadoc as you like. You can write the doclets from scratch using the doclet API, or you can start with the standard doclet and modify it to suit your needs.

Here are the basic steps you need to follow to create and use your own doclet:

  1. Write the Java program that constitutes your doclet. Your program should import com.sun.javadoc.* in order to use the doclet API. The entry point of your program is a class with a public static boolean start method that takes a RootDoc as a parameter.
  2. Compile your doclet. You can use the compiler in the Java Development Kit, javac.
  3. Run the javadoc tool using the -doclet <YourDoclet> option to produce the output specified by your doclet.
If you run javadoc without the -doclet command-line option, Javadoc will default to the standard doclet to produce HTML-format API documentation.

Package com.sun.javadoc consists of interfaces that define the doclet API. The lib/tools.jar file in the JDKTM software contains these interfaces and also a private package with classes that implement the interfaces. The tools.jar file also contains the classes implementing the standard doclet.

A Simple Example

You can get a feeling for the way doclets work by looking at this simple example doclet that consists of one short class:
import com.sun.javadoc.*;

public class ListClass {
    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;
    }
}
As you might be able to guess by looking at the code, this doclet takes the classes upon which Javadoc is operating and prints out their names.

This first thing to notice about the doclet is that it imports the com.sun.javadoc package in order to use the doclet APIs. As with all doclets, the entry point is the public static boolean start method. The start method takes a RootDoc as a parameter. This parameter carries information about any options specified on the command line when javadoc is run, and also about the classes and packages upon which javadoc is operating.

RootDoc defines a classes method that returns a ClassDoc array whose elements represent the classes that Javadoc parses. The for loop then prints out the names of each class in the array. (Passing a ClassDoc to println results in the printing of the name of the class that the ClassDoc represents.)

To run this doclet, you first have to compile it. You can compile it with the JDK javac compiler. The doclet API classfiles are in the file lib/tools.jar in the JDK software, and you therefore need to include tools.jar on the compiler's classpath, as in this example:

javac -classpath C:\jdk1.2\lib\tools.jar ListClass.java 
To run the ListClass doclet, you point to the compiled doclet with Javadoc's -doclet tag. For example, to run the doclet on a file called MyClass.java, you could use this command:
% javadoc -doclet ListClass MyClass.java
The output will be the string "MyClass".

To generate API documentation, a doclet will have to be considerably more complex than this simple example. If you want to customize the format of the API documentation generated by Javadoc, you may want to start with the default standard doclet and modify it as necessary, rather than write your own doclet from scratch.

Customizing Javadoc's Output

To customize the output of the Javadoc tool, you need to write your own doclet that specifies the content and format of the output that you desire. If you want HTML output with roughly the same format as the default output, you can use the standard doclet as a starting point for creating your doclet. You can subclass appropriate classes in the standard doclet and then add or override methods as necessary to produce the output you want. Or you can copy the whole standard doclet and modify it. If you use a copy of the standard doclet as your starting point, you may want to remove the package statements at the beginning of each source file and replace them with the name of your own, new package.

Example - Creating and handling custom tags

Suppose, for example, that you want use a custom tag, say @mytag, in your documentation comments in addition to the standard tags like @param and @return. To make use of the information in your custom tags, you need to have your doclet use instances of Tag that represent your custom tags. One of the easiest ways to do that is to use the tags(String) method of Doc or one of Doc's subclasses. This method returns an array of Tag's representing any tags whose name matches the string argument. For example, if method is an instance of MethodDoc, then
method.tags("mytag")
would return an array of Tag objects representing any @mytag tags in the method's documentation comment. You can then access the information in your @mytag tags with Tag's text method. That method returns a string representing the content of the tag which you can parse or use as needed. For example, if a documentation comment contained one of your custom tags like this:
@mytag Some dummy text.
then the text method would return the string "Some dummy text.".

Here's a doclet that uses these ideas to print out the text associated with all instances of a specified tag that it finds:

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());
                    }
                } 
            }
        }
    }
}
The tag for which this doclet searches is specified by the variable tagName. The value of the tagName string can be any tag name, custom or standard. This doclet writes to standard out, but its output format could be modified, for example, to write HTML output to a file.

Example - Using custom command-line options

You can write doclets that accept custom command-line options. To see how that works, let's augment the example doclet above so that it allows you to use a command-line option to specify the tag name to search for.

Any doclet that uses custom options must have a method called optionLength(String option) that returns an int. For each custom option that you want your doclet to recognize, optionLength must return the number of separate pieces or tokens in the option. For our example, we want to be able to use the custom option of the form -tag mytag. This option has two pieces, the -tag option itself and its value, so the optionLength method in our doclet must return 2 for the -tag option. The optionsLength method should return 0 for unrecognized options.

Here's the full, augmented doclet:

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;
    }
}
In this modified doclet, the variable tagName is set with the command-line option -tag. It has a optionLength method returns two for our custom option. Note that an explicit call to optionLength isn't required.

This doclet also adds the readOptions methods that actually parses the command-line options looking for the -tag option. It makes use of the fact that the Rootdoc.options method returns a two-dimensional String array containing option information. For example, given the command

javadoc -foo this that -bar other ...
the RootDoc.options method will return
options()[0][0] = "-foo"
options()[0][1] = "this"
options()[0][2] = "that"
options()[1][0] = "-bar"
options()[1][1] = "other"
The number of elements in the array's second index is determined by the optionLength method. In this example, optionLength returns 3 for option -foo and returns 2 for option -bar.

The validOptions method is an optional method that you can use to test the validity of the usage of command-line tags. If the validOptions method is present, it is automatically invoked; you don't have to explicitly call it. It should return true if the option usage is valid, and false otherwise. You can also print appropriate error messages from validOptions when improper usages of command-line options are found. The validOptions method in this example doclet checks that the -tag option is used once and only once.


Copyright © 1995-98 Sun Microsystems, Inc. All Rights Reserved.

Please send comments to: javadoc@sun.com
Sun
JavaSoft