argius note

プログラミング関連

ツールAPI(javax.tools)でJavadoc出力 #java8

ちょうど1ヶ月前に、カスタムDocletでJavadoc情報をCSV出力というエントリーを書きました。
今回は、Java8から使えるようになった、javax.tools.DocumentationToolを使ってJavadocを実行するプログラムを試します。



環境

  • Windows7 Professional 32bit
  • Java8 ea (b121)

概要

Java8の新機能「Javadoc APIのjavax.toolsへの追加」(Java SE 8 (5) - プラットフォーム、セキュリティー、他参照)により、tools.jarを使わなくてもJavadocをプログラムから呼び出すプログラムが書けるようになりました。
ただし、実行するときは、これまでと同じく、クラスパスにtools.jarを設定する必要があります。コンパイル時に不要になったということです。


プログラム例

  • javax.tools.DocumentationToolを使ったプログラム
// import java.io.File;
// import java.io.IOException;
// import java.io.Writer;
// import java.util.Arrays;
// import java.util.EnumSet;
// import javax.tools.Diagnostic;
// import javax.tools.DiagnosticCollector;
// import javax.tools.DocumentationTool;
// import javax.tools.JavaFileObject;
// import javax.tools.StandardJavaFileManager;
// import javax.tools.StandardLocation;
// import javax.tools.ToolProvider;

// throws IOException

// File outputDir = new File(...);
// File srcDir = new File(...);
// String rootPackage = ...;
if (!outputDir.exists())
    if (!outputDir.mkdirs())
        throw new IOException("can't make dir: " + outputDir);
DocumentationTool tool = ToolProvider.getSystemDocumentationTool();
DiagnosticCollector<JavaFileObject> diag = new DiagnosticCollector<>();
try (StandardJavaFileManager fileManager = tool.getStandardFileManager(diag, null, null)) {
    fileManager.setLocation(DocumentationTool.Location.DOCUMENTATION_OUTPUT, Arrays.asList(outputDir));
    fileManager.setLocation(StandardLocation.SOURCE_PATH, Arrays.asList(srcDir));
    Writer out = null; // use STDERR(System.err)
    Class<?> docletClass = null; // use default doclet
    Iterable<String> options = Arrays.asList("-private");
    Iterable<? extends JavaFileObject> compilationUnits = fileManager.list( //
        StandardLocation.SOURCE_PATH, // location
        rootPackage, // package name
        EnumSet.of(JavaFileObject.Kind.SOURCE), // kinds
        true); // recurse
    DocumentationTool.DocumentationTask task =
        tool.getTask(out, fileManager, diag, docletClass, options, compilationUnits);
    if (task.call())
        System.out.println("OK");
    else
        for (Diagnostic<? extends JavaFileObject> o : diag.getDiagnostics())
            System.out.println(o.getMessage(null));
}

入力パラメーターは、

  • outputDir: 出力先ディレクトリ(例 ./doc
  • srcDir: Javadoc生成対象ソースファイルのルートディレクトリ(相対パスでも可)
  • rootPackage: Javadoc生成対象クラスのルートパッケージ

を設定します。

このまま実行すると、

compiler message file broken: key=javadoc.err.msg arguments=javadoc, docletクラスcom.sun.tools.doclets.standard.Standardが見つかりません, {2}, {3}, {4}, {5}, {6}, {7}
エラー1個

というエラーが出てしまいます。(メッセージがちょっと変ですね。)
デフォルトドックレットは、tools.jarに含まれるcom.sun.tools.doclets.standard.Standardが使用されているので、実行時クラスパスにtools.jarを設定する必要があります。



この例では、オプション要素として、コマンドラインパラメーターと、カスタムドックレットの指定があります。
optionsは、コマンドラインから指定するのと同じように、文字列配列をコマンドラインパラメーターとして設定することができます。例えば、outputDirは、ここで設定することもできます。上記の例では、パッケージスコープを-privateに設定しています。
もう1つのカスタムドックレットは、従来の場合と同様に、カスタムドックレットのクラスを指定します。ただし、カスタムドックレットをコンパイルする場合は、tools.jarなどのドックレット実装がクラスパスに設定されている必要があります。


その他補足。
compilationUnitsは、StandardJavaFileManager#getJavaFileObjectsメソッドで相対パス文字列を使って取得することもできます。

  • パス文字列を使ってIterable<? extends JavaFileObject>を取得する
// import省略
Function<String, String> f = x -> "../anotherproj/src/net/argius/" + x + ".java";
String[] paths = Stream.of("Class1", "Class3").map(f).toArray(String[]::new);
Iterable<? extends JavaFileObject> compilationUnits = fileManager.getJavaFileObjects(paths);

以上です。