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

はじめに

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

ドックレットが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
バージョン1.5では、このコードは適切に動作しません。プリミティブ型以外にも、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 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])
    } 
}

Copyright © 1993, 2020, Oracle and/or its affiliates. All rights reserved.