Javadocから作成したXMLを読み込み、クラスと突き合わせてファイル出力してみる

typescript-generatorプラグインでJavadocからTSDocを生成したのち、XMLを直接読み込んだらいろいろできそうだなと思いついた。

Enumの値のJavadocと、フィールドで設定された値を出力したい場合があり、試してみたらできたのでメモ。

やりたいこと

エラーコードをEnumとして管理している。

どういった場合のエラーかをJavadoc、エラーコードをフィールドとして保持しているので、Enumの値に付与されたJavadocと、フィールドとして持っている値をファイル出力したい。

実装例

Java

以下のような列挙型を用意。値の参照のため、 @Getter でgetterを生成している。

package example;

@lombok.Getter
@lombok.RequiredArgsConstructor
public enum ExampleEnum {

    // Javadocなし
    ZERO("0"),
    /***/
    ONE("1"),
    /***/
    TWO("2"),
    /***/
    THREE("3"),

    ;

    private final String value;
}

build.gradle

以下のようなタスクを記述。 xmlDoclet タスクなどは前回と同様のため省略。

task enumToCsv() {
  dependsOn "compileJava", "xmlDoclet"

  doLast {
    def javadocXmlFile = "build/typescript-generator/xml-doclet/javadoc.xml" as File
    def targetClass = "example.ExampleEnum"
    def targetClassPackage = targetClass.substring(0, targetClass.lastIndexOf("."))

    // nameやqualifiedは@でフィールドアクセスしないとnullになる
    // enumをドットに続けて書くとエラーになるため、文字列にしている
    def targetClassNode = new XmlSlurper().parseText(javadocXmlFile.getText("UTF-8"))
        .package.find { it.@name == targetClassPackage }
        ."enum".find { it.@qualified == targetClass }

    // 列挙型の値を参照するため、クラスパスからクラスローダーを生成
    def urls = sourceSets.main.runtimeClasspath.files.collect { it.toURI().toURL() } as URL[]
    def classloader = new URLClassLoader(urls, null as ClassLoader)

    // 列挙型を読み込み、XMLと突き合わせ、MapのListを生成
    def enumClass = classloader.loadClass(targetClass) as Class<Enum>
    def enumContexts = enumClass.enumConstants.findResults { Enum enumValue ->
      def name = enumValue.name()
      def javadocNode = targetClassNode.constant.find { it.@name == name }
      def javadoc = (javadocNode.comment?.text() ?: "").trim()

      // Javadocが未設定であれば無視
      return javadoc ? [
          name   : name,
          value  : enumValue.value,
          javadoc: javadoc,
      ] : null
    }

    // MapからCSVを出力
    def csvText = "name,value,javadoc\r\n" + enumContexts.collect {
      "${it.name},${it.value},${it.javadoc}"
    }.join("\r\n") + "\r\n"

    def csvDir = "build/enum-csv"
    (csvDir as File).mkdirs()

    def csvFile = "${csvDir}/${enumClass.simpleName}.csv" as File
    csvFile.setText(csvText, "MS932")
  }
}

gradle enumToCsv すると、 build/enum-csv/ExampleEnum.csv に以下のファイルが出力される。

name,value,javadoc
ONE,1,一
TWO,2,二
THREE,3,三

振り返り

実際はもっと複雑なクラスだが、Groovy側で加工することで対応できた。

この手の方法(列挙型+Javadoc+フィールド)を使うことがままあり、その都度Excelスプレッドシートで別途管理していることもあったので、こうしたことができるのは個人的にはうれしい。

TypeScriptのインターフェース定義として出力したりと、今後の展開も考えている。

xml-docletが更新停止状態なのが惜しい...