Article

Harness the Power of Javadoc...

...to track your team's code reviews

High-performing software teams use code reviews to improve the quality of their code and to keep team members informed of what's going on in the larger application. However, keeping track of which packages, classes, and methods have been reviewed can be a real headache, especially if a code review process is adopted for an existing codebase. Fortunately, Sun's Javadoc API documentation generator provides the basis for a simple but powerful tool that will help the team mark reviewed code and track the progress of reviews.

Most software teams recognize the virtues of code reviews. Though techniques vary in formality and thoroughness, all code reviews intend to find bugs as early in the development life cycle as possible. A developer who reads code that someone else wrote is likely to find issues that functional testing will not. Reviews also have a side benefit: developers get exposure to the techniques employed by their colleagues.

Going over the code is actually just the start of the code review process - someone has to keep track of which code has been reviewed and document the results of the reviews. This is not the typical developer's idea of a good time, but without this information the team will not be able to assess the effectiveness of their reviews. Spreadsheets, bug-tracking tools, and homegrown databases make adequate tools for tracking reviews, but they all suffer from a common weakness: they're separate from the code and therefore require an extra step after the review to keep the review statuses up-to-date. The ideal review-tracking tool would provide an easy way to mark the code as having been reviewed. As it happens, Sun provides just such a tool with the Java SDK: Javadoc.

Javadoc 101
Javadoc generates API documentation by parsing specially commented Java source code.

In the first versions of the JDK, Javadoc was hard coded to output HTML. Since JDK release 1.2, Javadoc has offered a public doclet API for producing custom output. Doclets control the format and content of Javadoc output. The standard doclet that ships with Javadoc generates the HTML-formatted documentation that you're probably familiar with, but third-party doclets are available to produce XML, PDF, and other formats. While doclets provide complete control over the output, sometimes they're overkill. As of J2SDK 1.3, Javadoc did not provide a means of making small alterations to the documentation content without having to rewrite the doclet.

In J2SDK 1.4, Sun introduced the taglet API for Javadoc. This lightweight API enabled the introduction of custom tags for use with any doclet, including the standard doclet. For the first time, developers had an easy means of customizing the standard HTML output. In the simplest cases, custom tags can be introduced on the command line, without any coding at all.

Below we'll see how to use both taglets and doclets to facilitate tracking of code reviews. The next section shows how to create a custom taglet that you can use to mark source code as reviewed. After that, we'll look at using a custom doclet to produce a management report.

Taglet, You're It
For a first pass at marking up source to reflect code reviews, let's define @review as a standalone tag, meaning that the Javadoc will look for it in the tag section that follows the main description. Taglets can also define custom inline tags, which can appear anywhere in the documentation comment. We want this tag to appear in type (class- or interface-level) documentation comments and method comments, since code is often reviewed in increments of methods or classes. The comment associated with the @review tag will specify the date of the review and who reviewed the code, so we'll have Javadoc output the string "Last Code Review:" as a title for the review comment.

The Easy Way:
The -Tag Command-Line Option

When you don't need a lot of control over output formatting, Javadoc provides a very simple way of defining custom tags. The Standard doclet uses com.sun.tools.doclets.standard. tags. SimpleTaglet to for-mat some of the standard Javadoc tags. If you don't need to modify SimpleTaglet's default output, you can implement a custom tag without writing any code at all. The -tag command-line option for the Javadoc tool allows you to specify the tag name, header, and valid loca-tions for the tags, which Javadoc then uses to construct a Simple Taglet to process your tag. The for-mat for this option is:

-tag tagname:Xaoptcmf:"taghead"

where tagname is the name of the tag (without the initial "@") and taghead is the header you want to appear in the output. Each letter in Xaoptcmf represents the set of elements where the tag should appear (see Table 1).

Executing the command:

Javadoc -tag review:tm "Last code review:"

on a source file with a class documentation comment containing an "@review" tag will produce output similar to that shown in Figure 1.

More Control: Extending SimpleTaglet
The previous example describes a fast way to implement custom tags but it does not provide any variation in output format. If you simply can't abide how SimpleTaglet formats your tags, you can create a class that extends SimpleTaglet and overrides its output methods. The main advantage to extending SimpleTaglet, instead of implementing the Taglet interface directly, is that SimpleTaglet takes care of the handful of trivial methods required by the taglet API that tell the doclet which elements the tag should appear in.

SimpleTaglet offers a three-argument constructor that accepts the same inputs as the -tag command-line option - hardly surprising, since SimpleTaglet underlies the command line. The first argument is the name of the tag, the second is the header to use in the output, and the third is a string that contains a single-character code for each element that the tag may appear in. Listing 1 shows a class that implements the review tag by extending SimpleTaglet. The static method register(Map tagletMap) is called during Javadoc setup and makes the doclet aware of the taglet's existence.

By default, SimpleTaglet puts the tag header in an HTML <DT> element and the tag comment goes in a corresponding <DD> element. Comments from multiple tags of the same type are concatenated into a comma-separated list in a single <DD> element. If this doesn't meet your needs, you need to override the toString(Tag) and toString (Tag[]) methods to provide the output you want. Javadoc calls one of these methods when it encounters a documentation element containing a tag handled by the taglet. Sun's documentation is a bit unclear on this point, but as of JDK 1.4.2, Javadoc always calls toString (Tag[]) for standalone tags, even if the documentation element contains only one tag of the appropriate type. Javadoc only invokes toString(Tag) when handling inline tags. Since SimpleTaglet is defined as a standalone tag, Javadoc will never invoke its toString(Tag) method. If you want significant differences in your processing of single and multiple tags - a table for multiple tags, for instance - check the array size and call toString (Tag) if the array contains only one tag.

To compile the taglet, you'll need the J2SDK's tools.jar on your compile classpath, since it contains the taglet-related classes and interfaces. Once you have the class compiled, you can make Javadoc aware of the taglet with the following command:

javadoc -tagletpath tagletdir -taglet ReviewTaglet -sourcepath sourcedir

where tagletdir is the directory containing the taglet's class file and sourcedir is the directory holding the source code that you want to document.

Implementing the Taglet Interface
For the vast majority of standalone custom tags, subclassing SimpleTaglet should provide the functionality you need with minimal implementation hassle. However, if you need an inline tag, you'll need to create a class that implements the com.sun.tools. doclets.Taglet interface. In addition to the toString methods described earlier, this interface defines methods to indicate whether the class describes a standalone or inline tag, several methods that tell Javadoc where to look for the tag, and one method to return the name of the taglet. In addition to the methods defined in the interface, the class will need to implement a static register(Map tagletMap) method to tell the doclet which tag name is associated with this taglet.

Introducing Doclets
So far we've explored how to add a custom tag to the standard doclet's output. This approach has its uses, but it's fairly limited. For instance, the taglet receives no information about the context in which it's invoked. Also, the standard doclet is designed to produce HTML, so our taglet has to follow suit if it's to be of any use. Showing the review status of a class in its API documentation is nice, but a summary report on the review status of a set of classes would be a far more useful management tool. Such a thing is beyond the capabilities of a taglet; we'll need a custom doclet instead.

Doclets are the first link in the documentation-generation chain and provide total control over the content and format of Javadoc's output. Javadoc invokes a doclet every time the tool is run. If no doclet is specified on the command line (using the -doclet option), Javadoc invokes the standard doclet. Except for the taglet API, there's no easy way to extend or modify the behavior of the standard doclet. To create our report, we'll have to implement our own doclet.

API Overview
Since doclets provide so much flexibility, it's not surprising that most of the API's functionality is devoted to describing the source code input to Javadoc. In fact, Javadoc doesn't even require a doclet to extend a specific class or implement an interface. To qualify as a doclet, a class must provide a static boolean start(RootDoc root) method. Though it's not required, Sun provides the abstract class.com.sun.javadoc. Doclet as a starting point. In addition to the start method, this class defines two methods for processing custom command-line options for the doclet. The real action is in RootDoc, which is the first in a set of interfaces from the com.sun.javadoc package that describe the source code that Javadoc was instructed to process.

RootDoc provides access to all of the packages and classes that were specified in Javadoc command-line options. It declares methods returning PackageDoc or ClassDoc types, which (as you can probably imagine) describe the structure and documentation comments of a package, class, or interface. PackageDoc's methods return ClassDocs for the classes in the package, filtered in various ways depending on which method is invoked. ClassDoc models the contents of a class, and uses ConstructorDoc, MethodDoc, and FieldDoc to describe its constituent elements. All of these interfaces extend Doc, which provides access to such things as tags, text of a documentation comment, and information about the position and type of the element currently being processed.

Code Review Report
Listing 2 contains the code for ReviewDoclet, which generates a summary report on @review tags in a set of classes. (Listings 2-4 can be downloaded from www.sys-con.com/java/sourcec.cfm.) Its start method uses PackageDoc.allClasses() to retrieve the list of classes that meet the filter criteria specified on the Javadoc command line. PackageDoc also provides an allClasses (boolean) method that allows the invoker to ignore the command-line filters. For each class in the package, we retrieve its ClassDoc and look for an instance of our custom @review tag. If one is present, we output its first sentence using the firstSentenceTags() method.

firstSentenceTags() returns an array of tags that represent the first sentence in the comment. If the first sentence contains any inline tags, the returned array contains one tag of kind = "Text" for each sentence fragment and one tag of the appropriate kind for each inline tag. This allows us to output a readable, plain-text version of the first sentence even if it contains markup.

Once ReviewDoclet is compiled, it can be invoked with the following command:

javadoc -docletpath docletdir -doclet ReviewDoclet -sourcepath sourcedir

Custom Command-Line Options:
Date Filtering on Reports

The Doclet API's flexibility carries over to the command line. Javadoc allows a doclet author to specify custom command-line options. Javadoc looks for two methods in the doclet class to parse and validate custom command-line options. The first, public static int optionLength (String option), is required and must be implemented to return the number of tokens for the option specified by the argument, or zero if the option is unrecognized. The second, public static Boolean validOptions (String[][], DocErrorReporter), is an optional method for integrity-checking of the arguments for custom options. The two-dimensional string array contains the names of all command-line options (not just custom options) and the arguments passed to each. The DocErrorReporter can be used by the method to print messages concerning validation errors.

This feature provides a useful extension to our code-review reports: date filtering. Some organizations specify that all code must be reviewed periodically. A report that shows which classes have been reviewed since a specified date will alert the reviewers to the parts of the codebase that need attention. By specifying a new custom tag called @reviewdate, we can use the doclet's custom command-line option to find out which classes have been reviewed since a specified date. Listing 3 shows the new methods and member variables for ReviewDoclet. The optionLength method is implemented to return 1 if a "-date" option is passed, and 0 otherwise. validOptions looks for a -date option and confirms that the associated argument contains a well-formed date. Since the doclet does not require a -date option, validOptions returns true if none is found.

Listing 4 contains a modified inner loop for the start method, which looks for a reviewdate tag and parses the text associated with the tag as a date. If the review date is after the date specified on the command line, the report simply outputs "OK." If the review date is absent, not parseable, or before the specified date, ReviewDoclet prints an appropriate message.

Conclusion: The Future of Javadoc
The biggest impending change related to Javadoc isn't in Javadoc itself, but rather in the extension of the concept of source code markup. The forthcoming J2SE SDK 1.5 includes an implementation of JSR 175, A Program Annotation Facility for the Java Programming Language. This JSR defines a syntax and API for source code annotations that can be read at compile time or runtime, instead of just by the documentation tool. The resulting metadata could be used for purposes ranging from source-code generation to compiler checking and code verification at runtime. JSR 175 is already somewhat controversial due to its "macro-y" nature and adoption of features seen in .NET, but it will doubtless have significant impact on the way large Java applications are written and constructed. Of course, Javadoc will also receive extensions so that the new annotations can be documented.

The code review documentation tools described here are a useful extension of Javadoc, but they represent only one extension of Javadoc's functionality. I encourage you to find ways to apply Javadoc to the problems facing your team. No longer will you be "working for Javadoc" - inserting the missing tags and malformed comments that Javadoc complains about. You now know how to make Javadoc work for you.

Resources

  • Javadoc Tool page: http://java.sun.com/j2se/1.4.2/docs/tooldocs/javadoc/
  • Doclet overview: http://java.sun.com/j2se/1.4.2/docs/tooldocs/javadoc/overview.html
  • Taglet overview: http://java.sun.com/j2se/1.4.2/docs/tooldocs/javadoc/taglet/overview.html
  • Doclet.com (directory of third-party doclets and doclet information): www.doclet.com
  • JSR 175 homepage: www.jcp.org/en/jsr/detail?id=175
  • More Stories By James Scott

    James Scott is the software architect for Boston-based MedAptus, Inc. He has over 10 years of experience in designing and implementing mission-critical applications, and has worked with Java since 1999.

    Comments (0)

    Share your thoughts on this story.

    Add your comment
    You must be signed in to add a comment. Sign-in | Register

    In accordance with our Comment Policy, we encourage comments that are on topic, relevant and to-the-point. We will remove comments that include profanity, personal attacks, racial slurs, threats of violence, or other inappropriate material that violates our Terms and Conditions, and will block users who make repeated violations. We ask all readers to expect diversity of opinion and to treat one another with dignity and respect.