Transitioning to 1.5 Doclet API

Introduction Notes

It is assumed that the doclet developer has read the documentation on new 1.5 language features and is aware of the doclet changes required to support the new features. This document only assists the developer in making those changes. It does not define all the new language features in 1.5.

Indicate That Your Doclet Supports 1.5 Source

Class com.sun.javadoc.Doclet has a new method that allows doclets to indicate which version of Java Programming Language is supported:

Doclet.languageVersion()

By default, this method returns LanguageVersion.JAVA_1_1. You should override this method and return LanguageVersion.JAVA_1_5.

Ensure That Your Doclet Handles Primitives Properly

Given a Type, most doclets distinguish between classes and primitives by using the following check:
   // If true, Type must be a primitive
   Type.asClassDoc() == null
In 1.5, this no longer works because there are other non-classdoc types besides primitives. For example, the asClassDoc method would return null for annotation types as well. You should find all occurances of ?Type.asClassDoc() == null? and replace it with Type.isPrimitive().

Use ClassDoc.superclassType() Instead of ClassDoc.superclass()

ClassDoc.superclass() cannot accommodate certain generic type constructs. The superclassType() method should be used instead.

Use ClassDoc.interfaceTypes() Instead of ClassDoc.interfaces()

ClassDoc.interfaces() cannot accommodate certain generic type constructs. The interfaceTypes() method should be used instead.

Type Parameters

When processing ClassDocs and ExecutableMemberDocs, call method typeParameters() to retrieve the formal type parameters. Each parameter can be processed in a similar way that regular types are processes. The only difference is that type parameters have bounds that need to be documented. The bounds are retrieved by calling TypeVariable.bounds().

Here is the interface that represents a type variable:

com.sun.javadoc.TypeVariable

Here is the algorithm that the standard doclet uses to process type parameters:

if (type.asTypeVariable()!= null) {
    Doc owner = type.asTypeVariable().owner();
    if (owner instanceof ClassDoc) {
        // Generate Link to type Parameter
    } else {
        // No need to link method type parameters.
        // Append the name of the type parameter
    }
           
    Type[] bounds = type.asTypeVariable().bounds();
    if (! linkInfo.excludeTypeBounds) {
        for (int i = 0; i < bounds.length; i++) {
            // If i greater than 0, append " & ".  Otherwise, append " extends "
            // Generate a link to the bound
        }
    }
}

Param Tags For Type Parameters

A doclet retrieves @param tags from constructors and methods by calling ExecutableMemberDoc.paramTags(). Since classes, constructors and methods can have type parameters, the @param tag may be used to document these type parameters. They are distinguished by having angle brackets around the names. For example:
/**
 * @param <E> the type parameter for this class.
 */
public class Foo<E>
To retrieve these type parameter @param tags, call ClassDoc. typeParamTags() or ExecutableMemberDoc.typeParamTags().

Type parameter tags can be distinguished from regular parameter tags by calling ParamTag.isTypeParameter().

Annotation Types

An annotation type is distinguished from classes and interfaces by calling the isAnnotationType() method. Here is the interface that represents an annotation type:

com.sun.javadoc.AnnotationTypeDoc

Retrieve the annotation type elements by calling method elements(). There are two types of elements:

There is no way to retrieve an exactly list of optional and required elements from the doclet API. The doclet must manually interate through the array of elements and check the default value is null. If it is null, the element is required.

Annotation Type Usage

When processing a ProgramElementDoc, the annotation() method should be called to retrieve the annotations used. Here is the interface that encapsulates annoation usage information:

com.sun.javadoc.AnnotationDesc

Iterate through the list of AnnotationDesc objects and process each one. Here is the algorithm that the standard doclet uses to process AnnotationDesc objects:

for (int i = 0; i < descList.length; i++) {
    AnnotationTypeDoc annotationDoc = descList[i].annotationType();
    if (/**annotationDoc does not have @documented annotation**/){
        // ONLY document annotations if they have @documented.
        continue;
    }
    // Generate a link to the annotation.  Start with the ?@? character>
    AnnotationDesc.ElementValuePair[] pairs =  
        descList[i].elementValues();           
    if (pairs.length > 0) {
        // Append '(' to indicate start of element list>
        for (int j = 0; j < pairs.length; j++) {
              if (j > 0) {
                // Append ',' to separate previous element from the next>
            }
            // Generate a link to the annotation element>
            // Append '=' to separate element name from value>
            AnnotationValue annotationValue = pairs[j].value();
            List annotationTypeValues = new ArrayList();
            if (annotationValue.value() instanceof 
                     AnnotationValue[]) {
                 AnnotationValue[] annotationArray =
                 (AnnotationValue[]) annotationValue.value();
                 for (int k = 0; k < annotationArray.length; k++) {                             
                    annotationTypeValues.add(annotationArray[k]);
                 }
           } else {
               annotationTypeValues.add(annotationValue);
           }
           // Append '{' if this is an array of values
           for (Iterator iter = 
               annotationTypeValues.iterator();
               iter.hasNext(); ) {
               // Append string representation of value
               // Append ?,? if there there is more to append
           }
           // Append '}' if this is an array of values
       }
       // Append ')' if this is an array of values
    }
}
Here is a sample of this output:

java.lang.annotation.Target

An annotation value can be any of the following:

Here is the algorithm that the standard doclet uses to convert an annotation to a string:
if (annotationValue.value() instanceof Type) {
    Type type = (Type) annotationValue.value();
    if (type.asClassDoc() != null) {
        LinkInfoImpl linkInfo = new LinkInfoImpl(
           LinkInfoImpl.CONTEXT_ANNOTATION, type);
        linkInfo.label = (type.asClassDoc().isIncluded() ?
            type.typeName() :
            type.qualifiedTypeName()) + type.dimension() + ".class ";
        return getLink(linkInfo);
    } else {
        return type.typeName() + type.dimension() + ".class";
    }
} else if (annotationValue.value() instanceof AnnotationDesc) {
    // Handle nested annotations using recursion.
    List list = getAnnotations(
        new AnnotationDesc[]{(AnnotationDesc) annotationValue.value()},
            false);
    StringBuffer buf = new StringBuffer();
    for (Iterator iter = list.iterator(); iter.hasNext(); ) {
        buf.append(iter.next());  
    }
    return buf.toString();
} else if (annotationValue.value() instanceof MemberDoc) {
    // Simply link to the member being references in the annotation.
    return getDocLink(LinkInfoImpl.CONTEXT_ANNOTATION,
        (MemberDoc) annotationValue.value(),
        ((MemberDoc) annotationValue.value()).name(), false);
} else {
    return annotationValue.toString();
}   

Enums

An enum is distinguished from classes and interfaces by calling the isEnum() method. To retrieve the list of enum constants to be documented, call the enumConstants() method. The enum constants are returned as an array of FieldDoc objects. An enum constant can be distinguished from a regular field by calling isEnumConstant().

Here is a sample of Enum documentation:

java.lang.management.MemoryType.html

Varable Arguments

When processing an ExecutableMemberDoc, call isVarArg() to determine if the last constructor/method parameter is a var arg. If it is, the last parameter requires special handling. Here is the extra code that the standard doclet executes for var args:
if (isVarArg) {
    if (type.dimension().length() > 2) {
        // Doclet API returns var args as array.
        // Strip out the first [] from the var arg.
        // Append type.dimension().substring(2)
    }
    // Append "..."
} else {
    // Append type.dimension()
}
This code should be inserted in the place where you normally document array dimensions. Please note the comment that var args are returned as arrays by the doclet API. We strip out the first ?[]? because it is just a part of the internal representation of the var arg and should not appear in the documetation.

Wild Cards

Here is the interface that represents the wild card type:

com.sun.javadoc.WildcardType

When a wild card is encountered, iterate through the lists of extends and super bounds. Process each bound the same way you would process a regular type. Here is the algorithm the standard doclet uses to process wild cards:

if (type.asWildcardType() != null) {
    WildcardType wildcardType = type.asWildcardType();
    Type[] extendsBounds = wildcardType.extendsBounds();
    for (int i = 0; i < extendsBounds.length; i++) {
        // If i greater than 0, append " , ".  Otherwise, append " extends "
        // Generate link to extendsBounds[i])
    }
    Type[] superBounds = wildcardType.superBounds();
    for (int i = 0; i < superBounds.length; i++) {
        // If i greater than 0, append " , ".  Otherwise, append " super "
        // Generate link to superBounds[i])
    } 
}