mike-neckのブログ

Java or Groovy or Swift or Golang

アノテーションプロセッサーで自動生成するときにハマったあたりの話 #ソースコード自動生成

こんにちわ、みけです。

昨日に引き続いてアノテーションプロセッサーのあれをあれするあれです。

概要

  1. プロセッサーを指定する
  2. アノテーションプロセッサーにパラメーターを渡す
  3. プロセス対象のプロジェクトのリソースは取得できない
1. プロセッサーを指定する

プロセッサーを指定する方法は次のとおり二つあります。

  • javac-processorオプションで指定する
  • src/main/resource/META-INF/services/javax.annotation.processing.Processorファイルで指定する

-processorオプションで指定する場合は次のように、

javac -processor jp.hoge.foo.BarProcessor,jp.hoge.foo.BasProcessor -classpath ...

カンマ区切りでプロセッサーのクラス名を指定します。

gradleで記述する場合は次のようになります。

compileJava {
    options.compilerArgs += ['-processor', 'jp.hoge.foo.BarProcessor,jp.hoge.foo.BasProcessor']
}

プロセッサーの数が増えてくると、プロセッサーの実装の分だけ、

指定しないといけなくなるので、若干つらいかもしれません。

そこで、プロセッサー用のプロジェクトで、

サービスプロバイダーコンフィギュレーションファイル

に記述しておいた方がコンパイラーオプションが増えないので便利かもしれません。

サービスプロバイダーコンフィギュレーションファイルは、

META-INF/services/javax.annotation.processing.Processorというファイルで

プロセッサーのパッケージ名を含めた正規名を一行に一つ記入します。

jp.hoge.foo.BarProcessor
jp.hoge.foo.BazProcessor

このファイルがある場合は、引数でプロセッサーを指定しなくても構いません。

2. アノテーションプロセッサーにパラメーターを渡す

プロセッサーの実装クラスには@SupportedOptionsアノテーションによって、

パラメーターを渡すことができます。

import javax.annotation.processing.SupportedOptions;

@SupportedOptions({"foo", "bar"})
public class SampleProcessor extends AbstractProcessor {
    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
        Map<String, String> options = processingEnv.getOptions();
        String foo = options.getOrDefault("foo", "FOO");
        String bar = options.getOrDefault("bar", "BAR");
    }
}

実際に渡されたパラメーターは、AbstractProcessorで宣言してある、

ProcessingEnvironment型のprocessingEnv変数から

#getOptions()メソッドで取得することができます。

パラメーターが指定されていない場合には、#getOptions()メソッド

返されるMap<String, String>のキーに対して値は設定されていません。

例えば、上記の例でオプションfooに値が設定されていない場合は、

変数fooには"FOO"が設定されます。

パラメーターの設定方法は、javacコマンドの引数に-Aargument=valueの形で指定します。

例えば、foobarに値を指定する場合は、次のようになります。

javac -Afoo=foofoo -Abar=barara ...

これをgradleで記述する場合は次のようになります。

compileJava {
    options.compilerArgs += ['Afoo=foofoo', 'Abar=barara']
}
3. プロセス対象のプロジェクトのリソースは取得できない

これは単なる僕のポカミスで、アノテーションプロセッサーから、

コンパイル中のプロジェクトのリソースにアクセスできると

何故か思い込んでいて、

ずっと、「できない、できない…」と思ってたというだけの話です。

ファイルオブジェクトへのアクセスを可能にする

javax.annotation.processing.Filerという

インターフェースがあります。

そのインターフェースの下記の4つのメソッドによって、

ファイルへアクセスすることができます。

  • #createSourceFile(String, Element...) - ソースファイルの作成
  • #createClassFile(String, Element...) - クラスファイルの作成
  • #createResource(JavaFileManager.Location, String, String, Element...) - リソースファイルの作成
  • #getResource(JavaFileManager.Location, String, String) - リソースファイルの取得

このときに、リソースファイルの取得メソッドがあるので、

勝つると思い込んでいたわけですが、

これはソースかクラスの出力先のディレクトリーにあるファイルに

アクセスできるだけで、

maven形式のディレクトリー構成のリソースファイル(src/main/resources)には

アクセスできません。

javadocちゃんと嫁という話ですね…(´・ω・`)

おわり