5.0 ドックレット API への移行

はじめに

このドキュメントは、ドックレットの開発者が新機能のサポートに必要なドックレットの変更点に関するドキュメントを読んで理解していることを前提にしています。 このドキュメントの目的は、これらの変更を行う開発者の支援に限定されており、5.0 で定義されているすべての新しい言語機能を取り上げているわけではありません。

ドックレットが 5.0 ソースをサポートしていることを示す

クラス com.sun.javadoc.Doclet には、ドックレットがサポートしている Java プログラミング言語のバージョンを示すための、次の新しいメソッドが追加されています。

Doclet.languageVersion()

デフォルトでは、このメソッドは LanguageVersion.JAVA_1_1 を返します。このメソッドが LanguageVersion.JAVA_1_5 を返すようメソッドをオーバーライドしてください。

ドックレットがプリミティブ型を適切に処理するよう保証する

Type の場合、ほとんどのドックレットでは、クラスとプリミティブ型を区別するために次のようなチェックを使用しています。
   // If true, Type must be a primitive
   Type.asClassDoc() == null
バージョン 5.0 では、このコードは適切に動作しません。プリミティブ型以外にも、ClassDoc 型ではない型が存在するようになったからです。たとえば、注釈型に対しても asClassDoc メソッドは null を返します。したがって、「Type.asClassDoc() == null」というコードをすべて、「Type.isPrimitive()」に置き換える必要があります。

ClassDoc.superclass() ではなく ClassDoc.superclassType() を使用

ClassDoc.superclass() は、ある種の総称の構造に対応できません。そのため、代わりに superclassType() メソッドを使用する必要があります。

ClassDoc.interfaces() ではなく ClassDoc.interfaceTypes() を使用

ClassDoc.interfaces() は、ある種の総称の構造に対応できません。そのため、代わりに interfaceTypes() メソッドを使用する必要があります。

型パラメータ

ClassDocs および ExecutableMemberDocs を処理するときには、メソッド typeParameters() を呼び出して型の仮パラメータを取得します。各パラメータは、通常の型やプロセスと同様の方法で処理できます。唯一の違いは、型パラメータには、ドキュメント化の必要な境界があるということです。境界を取得するには、TypeVariable.bounds() を呼び出します。

次のインタフェースが、型変数を表しています。

com.sun.javadoc.TypeVariable

次に示すのは、型パラメータを処理するために標準ドックレットが使用するアルゴリズムです。

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 タグ

ドックレットは、コンストラクタやメソッドから @param タグを取得するために ExecutableMemberDoc.paramTags() を呼び出します。クラス、コンストラクタ、およびメソッドは型パラメータを持てるため、@param タグはそれらの型パラメータをドキュメント化するために使用されることもあります。それらのパラメータは、名前が山括弧で囲まれていることで区別されます。次に例を示します。
/**
 * @param <E> the type parameter for this class.
 */
public class Foo<E>
これらの型パラメータの @param タグを取得するには、ClassDoc.typeParamTags() または ExecutableMemberDoc.typeParamTags() を呼び出します。

型パラメータのタグを通常のパラメータのタグと区別するには、ParamTag.isTypeParameter() を呼び出します。

注釈型

注釈型をクラスやインタフェースと区別するには、isAnnotationType() メソッドを呼び出します。次のインタフェースが、注釈型を表しています。

com.sun.javadoc.AnnotationTypeDoc

注釈型の要素を取得するには、メソッド elements() を呼び出します。要素には次の 2 種類があります。

省略可能な要素と必須の要素の完全なリストをドックレット API から取得する手段はありません。ドックレットでは、要素の配列で繰り返し処理を手動で実行して、デフォルト値が null かどうかをチェックする必要があります。もし null であれば、その要素は必須です。

注釈型の使用

ProgramElementDoc を処理するときには、annotation() メソッドを呼び出して、使用されている注釈を取得する必要があります。次のインタフェースが、注釈の使用状況の情報をカプセル化しています。

com.sun.javadoc.AnnotationDesc

AnnotationDesc オブジェクトのリストを繰り返し処理して、各オブジェクトを処理します。次に示すのは、AnnotationDesc オブジェクトを処理するために標準ドックレットが使用するアルゴリズムです。

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
    }
}
次に示すのは、出力の例です。

java.lang.annotation.Target

注釈の値は、次のいずれかになります。

次に示すのは、注釈を文字列に変換するために標準ドックレットが使用するアルゴリズムです。
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();
}   

列挙型

列挙型をクラスやインタフェースと区別するには、isEnum() メソッドを呼び出します。ドキュメント化する列挙型の定数のリストを取得するには、enumConstants() メソッドを呼び出します。列挙型の定数は、FieldDoc オブジェクトの配列として返されます。列挙型の定数を通常のフィールドと区別するには、isEnumConstant() を呼び出します。

次に示すのは、列挙型の文書の例です。

java.lang.management.MemoryType.html

可変数の引数

ExecutableMemberDoc を処理するときには、VarArg() を呼び出して、コンストラクタやメソッドの最後のパラメータが var arg かどうかを判断します。もしそうであれば、最後のパラメータには特別な処理が必要になります。次に示すのは、var arg に対して標準ドックレットが実行する追加コードです。
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()
}
このコードは、配列の大きさを通常どおりにドキュメント化する箇所に挿入する必要のあるコードです。コメントに、var arg が配列としてドックレット API から返されると書かれていることに注目してください。最初の「[]」を除去しているのは、それが var arg の内部表現の一部にすぎず、文書に含める必要がないからです。

ワイルドカード

次のインタフェースが、ワイルドカード型を表しています。

com.sun.javadoc.WildcardType

ワイルドカードに遭遇したときは、拡張クラスとスーパークラスの境界のリストを繰り返し処理します。各境界は、通常の型を処理するのと同じ方法で処理します。次に示すのは、ワイルドカードを処理するために標準ドックレットが使用するアルゴリズムです。

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])
    } 
}