Pluggable Annotation Processing API 使い方メモ

#Pluggable Annotation Processing API とは
Java 1.6 から追加された、コンパイル時にアノテーションを処理するための仕組み。

Lombok とか JPA の Metamodel とかで利用されている。
つまり、コンパイル時にアノテーションを読み込んでソースコードを自動生成したりできる。

Java 1.5 でアノテーションが追加されたときに、同様にアノテーションをコンパイル時に処理する仕組みとして Annotation Processing Tool (apt) が追加されていたが、これとは別物らしい。

#Hello World
##プロセッサーを実装する
```java:MyAnnotationProcessor.java
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.RoundEnvironment;

import javax.lang.model.element.TypeElement;
import javax.lang.model.SourceVersion;

import java.util.Set;
import java.util.HashSet;

public class MyAnnotationProcessor extends AbstractProcessor {

    private int round = 1;

    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment env) {
        System.out.println("Round : " + (this.round++));
        annotations.forEach(System.out::println);
        return true;
    }

    @Override
    public Set<String> getSupportedAnnotationTypes() {
        Set<String> supportedAnnotationTypes = new HashSet<>();
        supportedAnnotationTypes.add("*");

        return supportedAnnotationTypes;
    }

    @Override
    public SourceVersion getSupportedSourceVersion() {
        return SourceVersion.RELEASE_8;
    }
}
```

##処理対象のソースを作る
```java:Hoge.java
public class Hoge {
    @Deprecated
    public void deprecatedMethod() {}

    @Override
    public String toString() {
        return "hoge";
    }
}
```

##プロセッサーをコンパイルする
```bat:
> javac MyAnnotationProcessor.java

> dir /b
MyAnnotationProcessor.class
MyAnnotationProcessor.java
Hoge.java 
```

##プロセッサーを指定してコードをコンパイルする
```bat:
> javac -processor MyAnnotationProcessor Hoge.java
Round : 1
java.lang.Deprecated
java.lang.Override
Round : 2

> dir /b
Hoge.class
Hoge.java
MyAnnotationProcessor.class
MyAnnotationProcessor.java
```

##説明
###プロセッサーの作成
```java:MyAnnotationProcessor.java
import javax.annotation.processing.AbstractProcessor;

public class MyAnnotationProcessor extends AbstractProcessor {
```

- アノテーションプロセッサーを作るには、 [Processor][1] を実装したクラスを用意する。
- 標準で `Processor` を実装した抽象クラス [AbstractProcessor][2] が用意されているので、普通はそれを継承して作る。

###処理対象を定義する
```java:MyAnnotationProcessor.java
    @Override
    public Set<String> getSupportedAnnotationTypes() {
        Set<String> supportedAnnotationTypes = new HashSet<>();
        supportedAnnotationTypes.add("*");

        return supportedAnnotationTypes;
    }

    @Override
    public SourceVersion getSupportedSourceVersion() {
        return SourceVersion.RELEASE_8;
    }
```

- `getSupportedAnnotationTypes()` で、処理するアノテーションを定義する。
    - 戻り値の `Set<String>` では、処理対象となるアノテーションのパターンを返すようにする。
    - 通常は、 FQCN を指定する。
    - `*` を使ったワイルドカード表現も可能。
    - `*` だけの場合は、全てのアノテーションが処理対象になる。
- `getSupportedSourceVersion()` は、サポート限界となるバージョンを定義する。
    - [SourceVersion][3] 列挙型で指定する。
    - サポートバージョンより新しい JDK を使うと、警告メッセージが表示されるようになる。

```text:SourceVersion.RELEASE_7を返すようにした場合の警告メセージ例
警告: 注釈プロセッサ'MyAnnotationProcessor'から-source '1.8'より小さいソース・バージョン'RELEASE_7'がサポートされています
```

###コンパイル時にプロセッサーを指定する
```bat:
> javac -processor MyAnnotationProcessor Hoge.java
```

- `-processor` オプションで、使用するプロセッサーを指定する。

###ラウンド
```text:実行結果
Round : 1
java.lang.Deprecated
java.lang.Override
Round : 2
```

- プロセッサーによる処理（`process`）の呼び出しは、複数回行われる。
- それぞれの処理を **ラウンド** と呼ぶ。
- １回のラウンドの処理でソースコードを新規に生成したりすると、生成されたソースに使用されているアノテーションを処理するため、もう一度ラウンドが実行される。
- ソースの生成などをしなくても、最低２回はラウンドが実行される。


#サポートバージョンと処理対象の絞り込みをアノテーションで指定する
```java:MyAnnotationProcessor.java
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.annotation.processing.SupportedSourceVersion;

import javax.lang.model.element.TypeElement;
import javax.lang.model.SourceVersion;

import java.util.Set;

@SupportedSourceVersion(SourceVersion.RELEASE_8)
@SupportedAnnotationTypes("*")
public class MyAnnotationProcessor extends AbstractProcessor {

    private int round = 1;

    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment env) {
        System.out.println("Round : " + (this.round++));
        annotations.forEach(System.out::println);
        return true;
    }
}
```

- メソッドの変わりに、 `@SupportedSourceVersion` と `@SupportedAnnotationTypes` アノテーションで定義することができる。

#アノテーションの情報を取得する
```java:MyAnnotationProcessor.java
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.annotation.processing.SupportedSourceVersion;

import javax.lang.model.element.TypeElement;
import javax.lang.model.element.Element;
import javax.lang.model.SourceVersion;

import java.util.Set;

@SupportedSourceVersion(SourceVersion.RELEASE_8)
@SupportedAnnotationTypes("*")
public class MyAnnotationProcessor extends AbstractProcessor {

    @Override
    public boolean process(Set<? extends TypeElement> typeElements, RoundEnvironment env) {

        for (TypeElement typeElement : typeElements) {

            for (Element element : env.getElementsAnnotatedWith(typeElement)) {

                Override override = element.getAnnotation(Override.class);

                if (override != null) {
                    System.out.println("@Override at " + element);
                }
            }
        }

        return true;
    }
}
```

```bat:動作確認
> javac -processor MyAnnotationProcessor Hoge.java
@Override at toString()
```

- `RoundEnvironment#getElementsAnnotatedWith(TypeElement)` メソッドで、実際にアノテートされている場所を表す `Element` の `Set` を取得できる。
- `Element#getAnnotation(Class)` メソッドで、実際のアノテーションを取得できる。

#メッセージ出力
##基本
```java:MyAnnotationProcessor.java
import java.util.Set;

import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.Messager;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.annotation.processing.SupportedSourceVersion;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.TypeElement;
import javax.tools.Diagnostic.Kind;

@SupportedAnnotationTypes("*")
@SupportedSourceVersion(SourceVersion.RELEASE_8)
public class MyAnnotationProcessor extends AbstractProcessor {
    
    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
        Messager messager = super.processingEnv.getMessager();
        
        messager.printMessage(Kind.OTHER, "Other");
        messager.printMessage(Kind.NOTE, "Note");
        messager.printMessage(Kind.WARNING, "Warning");
        messager.printMessage(Kind.MANDATORY_WARNING, "Mandatory Warning");
        messager.printMessage(Kind.ERROR, "Error");
        
        return true;
    }
}
```

```text:コンパイル実行結果
注意:Other
注意:Note
警告: Warning
警告: Mandatory Warning
エラー: Error
注意:Other
注意:Note
警告: Warning
警告: Mandatory Warning
エラー: Error
エラー2個
警告4個
```

- ※２回ずつ出力されているのは、ラウンドが２回実行されているため。
- `AbstractProcessor` クラスが持つ `processingEnv` というフィールドから、 `getMessager()` メソッドを使って [Messager][5] のインスタンスを取得する。
- `Messager#printMessage()` メソッドを使って、コンパイル時のメッセージ出力ができる。
- 第一引数にメッセージの種類を指定する（[Diagnostic.Kind][4]）。
- 第二引数に出力するメッセージを渡す。
- 種類を `Kind.ERROR` にしてメッセージを出力すると、コンパイル結果がエラーになる。

##アノテートされたコード上の位置情報を出力する
```java:MyAnnotationProcessor.java
import java.util.Set;

import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.Messager;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.annotation.processing.SupportedSourceVersion;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;
import javax.tools.Diagnostic.Kind;

@SupportedAnnotationTypes("*")
@SupportedSourceVersion(SourceVersion.RELEASE_8)
public class MyAnnotationProcessor extends AbstractProcessor {
    
    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
        Messager messager = super.processingEnv.getMessager();
        
        for (TypeElement typeElement : annotations) {
            for (Element element : roundEnv.getElementsAnnotatedWith(typeElement)) {
                messager.printMessage(Kind.NOTE, "hogehoge", element);
            }
        }
        
        return true;
    }
}
```

```text:コンパイル実行結果
Hoge.java:5: 注意:hogehoge
    public String toString() {
                  ^
```

- `Messager#printMessage()` メソッドの第三引数に `Element` を渡すと、その要素のコード上の位置情報が一緒に出力される。

##アノテーションの位置情報を出力する
```java:MyAnnotationProcessor.java
import java.util.Set;

import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.Messager;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.annotation.processing.SupportedSourceVersion;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;
import javax.tools.Diagnostic.Kind;

@SupportedAnnotationTypes("java.lang.Override")
@SupportedSourceVersion(SourceVersion.RELEASE_8)
public class MyAnnotationProcessor extends AbstractProcessor {
    
    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
        Messager messager = super.processingEnv.getMessager();
        
        for (TypeElement typeElement : annotations) {
            for (Element element : roundEnv.getElementsAnnotatedWith(typeElement)) {
                AnnotationMirror override = element.getAnnotationMirrors().get(0);
                messager.printMessage(Kind.NOTE, "hogehoge", element, override);
            }
        }
        
        return true;
    }
}
```

```text:コンパイル実行結果
Hoge.java:4: 注意:hogehoge
    @Override
    ^
```

- `Messager#printMessage()` メソッドの第四引数に `AnnotationMirror` を渡すと、そのアノテーションのコード上の位置情報が一緒に出力される。
- 第五引数に `AnnotationValue` を渡せば、アノテーションの引数の位置情報も出力できそう（試してない）。

#アノテートされている要素の情報を取得する
```java:MyAnnotation.java
import java.lang.annotation.ElementType;
import java.lang.annotation.Target;

@Target({
    ElementType.TYPE, ElementType.METHOD, ElementType.LOCAL_VARIABLE,
    ElementType.FIELD, ElementType.PARAMETER, ElementType.TYPE_PARAMETER
})
public @interface MyAnnotation {
    
    String value();
}
```

```java:MyAnnotationProcessor.java
import java.util.Set;

import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.annotation.processing.SupportedSourceVersion;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;

@SupportedAnnotationTypes("MyAnnotation")
@SupportedSourceVersion(SourceVersion.RELEASE_8)
public class MyAnnotationProcessor extends AbstractProcessor {
    
    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
        for (TypeElement typeElement : annotations) {
            for (Element element : roundEnv.getElementsAnnotatedWith(typeElement)) {
                MyAnnotation anno = element.getAnnotation(MyAnnotation.class);
                
                String msg =
                    "<<@MyAnnotation(\"" + anno.value() + "\")>>\n"
                  + "  Kind : " + element.getKind() + "\n"
                  + "  SimpleName : " + element.getSimpleName() + "\n"
                  + "  Modifiers : " + element.getModifiers() + "\n"
                  + "  asType : " + element.asType() + "\n"
                  + "  EnclosedElements : " + element.getEnclosedElements() + "\n"
                  + "  EnclosingElement : " + element.getEnclosingElement() + "\n"
                  + "  AnnotationMirrors : " + element.getAnnotationMirrors() + "\n"
                  ;
                
                System.out.println(msg);
            }
        }
        
        return true;
    }
}
```

```java:Hoge.java
@MyAnnotation("クラス")
public class Hoge<@MyAnnotation("型引数") T> {
    
    @MyAnnotation("フィールド")
    private double d;
    
    @MyAnnotation("メソッド")
    public void method(@MyAnnotation("引数") int i) {
        @MyAnnotation("ローカル変数")
        String str;
    }
}
```

```text:コンパイル実行結果
<<@MyAnnotation("クラス")>>
  Kind : CLASS
  SimpleName : Hoge
  Modifiers : [public]
  asType : Hoge<T>
  EnclosedElements : Hoge(),d,method(int)
  EnclosingElement : 名前のないパッケージ
  AnnotationMirrors : @MyAnnotation("\u30af\u30e9\u30b9")

<<@MyAnnotation("型引数")>>
  Kind : TYPE_PARAMETER
  SimpleName : T
  Modifiers : []
  asType : T
  EnclosedElements :
  EnclosingElement : Hoge
  AnnotationMirrors : @MyAnnotation("\u578b\u5f15\u6570")

<<@MyAnnotation("フィールド")>>
  Kind : FIELD
  SimpleName : d
  Modifiers : [private]
  asType : double
  EnclosedElements :
  EnclosingElement : Hoge
  AnnotationMirrors : @MyAnnotation("\u30d5\u30a3\u30fc\u30eb\u30c9")

<<@MyAnnotation("メソッド")>>
  Kind : METHOD
  SimpleName : method
  Modifiers : [public]
  asType : (int)void
  EnclosedElements :
  EnclosingElement : Hoge
  AnnotationMirrors : @MyAnnotation("\u30e1\u30bd\u30c3\u30c9")

<<@MyAnnotation("引数")>>
  Kind : PARAMETER
  SimpleName : i
  Modifiers : []
  asType : int
  EnclosedElements :
  EnclosingElement : method(int)
  AnnotationMirrors : @MyAnnotation("\u5f15\u6570")
```

- [Element][6] インスタンスから、アノテートされた要素の情報をいろいろ取得できる。

#jar にパッケージして -processor オプション無しでも使えるようにする
毎回 `javac` のオプションで `-processor` を指定するのは疲れる。
アノテーションプロセッサーを所定の方法で jar にパッケージすると、コンパイル時に jar をクラスパスに指定するだけでプロセッサーの処理を挟むことができるようになる。

##プロセッサーを含む jar を作成する
```java:MyOverrideProcessor.java
package sample.processor;

import java.util.Set;

import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.annotation.processing.SupportedSourceVersion;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.TypeElement;

@SupportedAnnotationTypes("java.lang.Override")
@SupportedSourceVersion(SourceVersion.RELEASE_8)
public class MyOverrideProcessor extends AbstractProcessor {
    
    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
        System.out.println("Override!!");
        return true;
    }
}
```

`@Override` アノテーションを処理するプロセッサー。

これを、以下の構成になるように jar にパッケージする。

```text:
|-sample/processor/
|  `-MyOverrideProcessor.class
`-META-INF/services/
   `-javax.annotation.processing.Processor
```

この `META-INF/services/javax.annotation.processing.Processor` はただのテキストファイルで、内容は以下のように使用するプロセッサーの FQCN を記述しておく。

```javax.annotation.processing.Processor
sample.processor.MyOverrideProcessor
```

この jar を仮に `processor.jar` とする。

##実行する
```java:Hoge.java
public class Hoge {
    @Override
    public String toString() {
        return super.toString();
    }
}
```

```bat:
> javac -cp processor.jar Hoge.java
Override!!
Override!!
```

##説明
- アノテーションプロセッサーを含む jar ファイルの中に、 `META-INF/services/javax.annotation.processing.Processor` という形でテキストファイルを配置する。
- `javax.annotation.processing.Processor` の中には、使用するプロセッサーの FQCN を記述する。
- この jar をクラスパスに含めた状態でコンパイルを実行すると、 `javax.annotation.processing.Processor` で指定されたプロセッサーがコンパイル時に利用される。
- `javax.annotation.processing.Processor` には、改行区切りで複数のプロセッサーを記述することができる。
- この `META-INF/services` の下に使用するクラスの情報を配置するという方法は、 Java の [ServiceLoader][7] という仕組みを利用したものと思われる。

#Eclipse で利用する
##基本
###プロセッサー側のプロジェクトを作成する
![processor.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/81c13173-2a46-ae32-2555-dffbaa3ffc9c.jpeg)

```java:MyAnnotationProcessor.java
package sample.processor;

import java.util.Set;

import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.Messager;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.annotation.processing.SupportedSourceVersion;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.TypeElement;
import javax.tools.Diagnostic.Kind;

@SupportedAnnotationTypes("*")
@SupportedSourceVersion(SourceVersion.RELEASE_8)
public class MyAnnotationProcessor extends AbstractProcessor {

    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
        Messager messager = super.processingEnv.getMessager();
        messager.printMessage(Kind.NOTE, "Hello Annotation Processor!!");
        
        return true;
    }
}
```

```text:javax.annotation.processing.Processor
sample.processor.MyAnnotationProcessor
```

- 普通に java プロジェクトを作る。
- ServiceLoader を利用した形式にする（`javax.annotation.processing.Processor`）。

####jar ファイルを出力する
- プロジェクトを右クリックして、 [エクスポート] を選択する。
- [Java] > [JAR ファイル] を選択し、 [次へ] をクリック。
- 任意の場所に jar ファイルを出力する。

###プロセッサーを利用する側のプロジェクトを作成する
![processor.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/fa4fe36a-3112-06d4-3d7b-ef76184dd9ce.jpeg)

```java:Hoge.java
public class Hoge {
    
    @Override
    public String toString() {
        return super.toString();
    }
}
```

- 普通に java プロジェクトを作成する。

####注釈処理を有効にする
- プロジェクトを右クリックして、 [プロパティー] を選択。
- [Java コンパイラー] > [注釈処理] を選択。
- [プロジェクト固有の設定を可能にする] にチェックを入れる。
- 続いて、 [注釈処理を使用可能にする] と [エディターでの処理を使用可能にする] のチェックを入れる。

![processor.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/7014de5f-4fc5-2ea7-c6c6-bbe309337d59.jpeg)

- さらに、 [注釈処理] > [ファクトリー・パス] を選択する。
- [プロジェクト固有の設定を可能にする] にチェックを入れる。
- [外部 Jar 追加] をクリックし、先ほど生成した `MyAnnotationProcessor` プロジェクトの jar ファイルを選択する。

![processor.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/a4a9d50e-0f53-deca-77be-76924c18bef7.jpeg)

これで、注釈処理が有効になる。

####出力結果を確認する
- [ウィンドウ] > [ビューの表示] > [エラー・ログ] を選択し、「エラー・ログ」ウィンドウを表示させる。

![processor.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/acc6698d-66f6-895b-4dc8-cadfb8d6dfe7.jpeg)

- うまく処理されていると、 `Messager` で出力した内容が表示されている。
- Eclipse で動かす場合、標準出力（`System.out`）で出力した内容は確認できないので、 `Messager` で出力して「エラー・ログ」ウィンドウで確認する。
- プロセッサー側の実装を修正した場合は、 jar を出力しなおした上で利用側の「ファクトリー・パス」の設定をし直す（一回外して、再度設定し直す）必要がある。

##警告やエラーのメッセージはエディター上に表示される
```java:MyAnnotationProcessor.java
package sample.processor;

import java.util.Set;

import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.Messager;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.annotation.processing.SupportedSourceVersion;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;
import javax.tools.Diagnostic.Kind;

@SupportedAnnotationTypes("java.lang.Override")
@SupportedSourceVersion(SourceVersion.RELEASE_8)
public class MyAnnotationProcessor extends AbstractProcessor {

    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
        Messager messager = super.processingEnv.getMessager();
        
        for (TypeElement typeElement : annotations) {
            for (Element element : roundEnv.getElementsAnnotatedWith(typeElement)) {
                messager.printMessage(Kind.ERROR, "オーバーライドさせぬ！", element);
            }
        }
        
        return true;
    }
}
```

**利用側の様子**

![processor.JPG](https://qiita-image-store.s3.amazonaws.com/0/28302/e8fa90ff-e2ee-5908-a32f-82823f705acf.jpeg)

エディター上でエラー表示されてる！

#Gradle で使用する
##基本
###フォルダ構成
```text:フォルダ構成
|-settings.gradle
|-build.gradle
|-processor/
|  `-src/main/
|     |-java/sample/processor/
|     |  `-MyAnnotationProcessor.java
|     `-resources/META-INF/services/
|        `-javax.annotation.processing.Processor
|
`-client/
   |-build.gradle
   `-src/main/java/sample/processor/
      `-Hoge.java
```

- マルチプロジェクトの構成。
- `processor` と `client` というプロジェクトがある。
    - `processor` プロジェクトでは、アノテーションプロセッサーを実装する。
    - `client` プロジェクトでは、 `processor` プロジェクトで作成したアノテーションプロセッサーを利用する。

###実装等
```groovy:settings.gradle
include 'processor', 'client'
```

```groovy:build.gradle
subprojects {
    apply plugin: 'java'
}
```

```groovy:client/build.gradle
dependencies {
    compile project(':processor')
}
```

```java:MyAnnotationProcessor.java
package sample.processor;

import java.util.Set;

import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.annotation.processing.SupportedSourceVersion;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.TypeElement;

@SupportedAnnotationTypes("*")
@SupportedSourceVersion(SourceVersion.RELEASE_8)
public class MyAnnotationProcessor extends AbstractProcessor {
    
    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
        System.out.println("Hello!!");
        return true;
    }
}
```

```java:Hoge.java
package sample.processor;

public class Hoge {
    @Override
    public String toString() {
        return "hoge";
    }
}
```

###動作確認
```bat:
> gradle compileJava
:processor:compileJava
:processor:processResources
:processor:classes
:processor:jar
:client:compileJava
Hello!!
Hello!!

BUILD SUCCESSFUL

Total time: 3.423 secs
```

- ServiceLoader を使う形式にしておけば、普通に利用できた。

##Eclipse プロジェクト化
### Gradle プラグイン作りました

**2016/01/08 追記**

以下の設定ファイルを自動生成する Gradle プラグインを作りました。

- [Gradle - Plugin: com.github.opengl-BOBO.annotation-processing-eclipse](https://plugins.gradle.org/plugin/com.github.opengl-BOBO.annotation-processing-eclipse)
- [説明 | GitHub](https://github.com/opengl-8080/annotation-processing-eclipse-gradle-plugin#日本語の説明)

---------------------

`gradle eclipse` したら、自動で注釈処理の設定が有効になるようにする。

###フォルダ構成
```text:フォルダ構成
|-build.gradle
|-lib/
|  `-processor.jar
`-src/main/java/sample/processor/
   `-Hoge.java
```

プロセッサーをパッケージングした jar ファイルは、 `lib` フォルダの下の配置しておく。

###build.gradle
```groovy:client/build.gradle
apply plugin: 'java'
apply plugin: 'eclipse'

ext {
    eclipseAptPrefsFile = '.settings/org.eclipse.jdt.apt.core.prefs'
    eclipseFactoryPathFile = '.factorypath'
    processorJarPath = 'lib/processor.jar'
}

dependencies {
    compile files(processorJarPath)
}

eclipse {
    project.name = 'MyAnnotationProcessorClient'

    classpath.file.withXml {
        it.asNode().appendNode('classpathentry', [kind: 'src', path: '.apt_generated'])
    }

    jdt.file.withProperties { properties ->
        properties.put 'org.eclipse.jdt.core.compiler.processAnnotations', 'enabled'
    }
}

eclipseJdt << {
    file(eclipseAptPrefsFile).write """\
        |eclipse.preferences.version=1
        |org.eclipse.jdt.apt.aptEnabled=true
        |org.eclipse.jdt.apt.genSrcDir=.apt_generated
        |org.eclipse.jdt.apt.reconcileEnabled=true
        |""".stripMargin()

    file(eclipseFactoryPathFile).write """\
        |<factorypath>
        |    <factorypathentry kind="PLUGIN" id="org.eclipse.jst.ws.annotations.core" enabled="true" runInBatchMode="false"/>
        |    <factorypathentry kind="EXTJAR" id="${file(processorJarPath).absolutePath}" enabled="true" runInBatchMode="false"/>
        |</factorypath>
        |""".stripMargin()
}

cleanEclipse << {
    file(eclipseAptPrefsFile).delete()
    file(eclipseFactoryPathFile).delete()
}
```

###説明
やっていることは単純で、 `eclipse` タスクを実行したら注釈処理を有効化するのに必要となる設定ファイルを追加したりしている。

 **org.eclipse.jdt.apt.core.prefs の作成**

```groovy:
ext {
    eclipseAptPrefsFile = '.settings/org.eclipse.jdt.apt.core.prefs'
    ...
}

...

eclipseJdt << {
    file(eclipseAptPrefsFile).write """\
        |eclipse.preferences.version=1
        |org.eclipse.jdt.apt.aptEnabled=true
        |org.eclipse.jdt.apt.genSrcDir=.apt_generated
        |org.eclipse.jdt.apt.reconcileEnabled=true
        |""".stripMargin()

...
```

 **.factorypath の作成**

```groovy:
ext {
    eclipseFactoryPathFile = '.factorypath'
    ...
}

...

eclipseJdt << {
    ...

    file(eclipseFactoryPathFile).write """\
        |<factorypath>
        |    <factorypathentry kind="PLUGIN" id="org.eclipse.jst.ws.annotations.core" enabled="true" runInBatchMode="false"/>
        |    <factorypathentry kind="EXTJAR" id="${file(processorJarPath).absolutePath}" enabled="true" runInBatchMode="false"/>
        |</factorypath>
        |""".stripMargin()
}
```

 **.classpath の編集**

```groovy:
eclipse {
    ...

    classpath.file.withXml {
        it.asNode().appendNode('classpathentry', [kind: 'src', path: '.apt_generated'])
    }

    ...
}
```

 **org.eclipse.jdt.core.prefs の編集**

```groovy:
eclipse {
    ...

    jdt.file.withProperties { properties ->
        properties.put 'org.eclipse.jdt.core.compiler.processAnnotations', 'enabled'
    }
}
```

 **cleanEclipse に削除対象を追加**

```groovy:
ext {
    eclipseAptPrefsFile = '.settings/org.eclipse.jdt.apt.core.prefs'
    eclipseFactoryPathFile = '.factorypath'
    ...
}

...

cleanEclipse << {
    file(eclipseAptPrefsFile).delete()
    file(eclipseFactoryPathFile).delete()
}
```

#Java のソースコードを生成する
アノテーションプロセッサーの処理の中で、ソースコードを動的に生成してみる。

##実装
```java:MyAnnotation.java
package sample.processor;

import java.lang.annotation.ElementType;
import java.lang.annotation.Target;

@Target(ElementType.TYPE)
public @interface MyAnnotation {
    boolean value() default false;
}
```

```java:Hoge.java
package sample.processor;

@MyAnnotation(true)
public class Hoge {
}
```

```java:MyAnnotationProcessor.java
package sample.processor;

import java.io.IOException;
import java.io.Writer;
import java.util.Set;

import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.Filer;
import javax.annotation.processing.Messager;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.annotation.processing.SupportedSourceVersion;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;
import javax.tools.Diagnostic.Kind;
import javax.tools.JavaFileObject;

@SupportedAnnotationTypes("sample.processor.MyAnnotation")
@SupportedSourceVersion(SourceVersion.RELEASE_8)
public class MyAnnotationProcessor extends AbstractProcessor {
    
    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
        if (annotations.size() == 0) {
            return true;
        }
        
        Messager messager = super.processingEnv.getMessager();
        
        for (TypeElement typeElement : annotations) {
            for (Element element : roundEnv.getElementsAnnotatedWith(typeElement)) {
                MyAnnotation anno = element.getAnnotation(MyAnnotation.class);
                
                messager.printMessage(Kind.NOTE, element.getSimpleName() + " class is annotated by @MyAnnotation.");
                
                if (!anno.value()) {
                    messager.printMessage(Kind.NOTE, "@MyAnnotation value is false. no generate.");
                    break;
                }
                
                String src =
                    "package sample.processor.generated;\r\n"
                  + "import sample.processor.MyAnnotation;\r\n"
                  + "@MyAnnotation\r\n"
                  + "public class Fuga {\r\n"
                  + "    public void hello() {\r\n"
                  + "        System.out.println(\"Hello World!!\");\r\n"
                  + "    }\r\n"
                  + "}\r\n"
                  ;
                
                try {
                    Filer filer = super.processingEnv.getFiler();
                    JavaFileObject javaFile = filer.createSourceFile("Fuga");
                    
                    try (Writer writer = javaFile.openWriter()) {
                        writer.write(src);
                    }
                    
                    messager.printMessage(Kind.NOTE, "generate source code!!");
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        
        return true;
    }
}
```

##動作確認
```text:コンパイル実行結果
注意:Hoge class is annotated by @MyAnnotation.
注意:generate source code!!
注意:Fuga class is annotated by @MyAnnotation.
注意:@MyAnnotation value is false. no generate.
```

```java:自動生成されたFugaクラス
package sample.processor.generated;
import sample.processor.MyAnnotation;
@MyAnnotation
public class Fuga {
    public void hello() {
        System.out.println("Hello World!!");
    }
}
```

- `Fuga.java` は、 `.class` ファイルが出力されるフォルダのルートに出力された。
- `Fuga.class` は、同じく `.class` ファイルの出力先の中に、 `package` 宣言で定義した通りの場所に出力された。

```text:出力されたファイルの様子
|-Fuga.java
`-sample/processor/
   |-Hoge.class
   `-generated/
      `-Fuga.class
```

##説明
```java:MyAnnotationProcessor.java（一部）
try {
    Filer filer = super.processingEnv.getFiler();
    JavaFileObject javaFile = filer.createSourceFile("Fuga");
    
    try (Writer writer = javaFile.openWriter()) {
        writer.write(src);
    }
    
    messager.printMessage(Kind.NOTE, "generate source code!!");
} catch (IOException e) {
    e.printStackTrace();
}
```

- Java のソースコードを生成するには、まず次の方法で [JavaFileObject][9] を取得する。
    - `AbstractProcessor#processingEnv` の `getFiler()` メソッドで [Filer][8] のインスタンスを取得する。
    - `Filer#createSourceFile(String)` メソッドで、 `JavaFileObject` のインスタンスを取得する（引数は生成するクラスの名前）。
- `JavaFileObject` を取得したら、 `openWriter()` や `openOutputStream()` を使ってライターやストリームを取得し、そこにソースコードを書き出す。
- 生成されたファイルは自動的にコンパイルされる。
- 生成したソースコードにアノテーションが含まれていた場合は、次のラウンドが実行され、再びプロセッサーの `process()` メソッドが実行される。
- 同じクラスのソースコードを複数回出力するとエラーになる。
    - このため、処理すべきアノテーションが無くなったかどうかを確認して、なければすぐに処理を終了させている（判定には、 `process()` メソッドの第一引数のサイズを利用する）。

```java:処理すべきアノテーションが無ければ、すぐに処理を終了させているところ
    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
        if (annotations.size() == 0) {
            return true;
        }
```

#参考

- [Java技術最前線 - 「Java SE 6完全攻略」第94回 アノテーションを処理する その1：ITpro](http://itpro.nikkeibp.co.jp/article/COLUMN/20081219/321816/)
- [Java技術最前線 - 「Java SE 6完全攻略」第95回 アノテーションを処理する その2：ITpro](http://itpro.nikkeibp.co.jp/article/COLUMN/20090115/322957/?ST=develop)
- [Java技術最前線 - 「Java SE 6完全攻略」第96回 アノテーションを処理する その3：ITpro](http://itpro.nikkeibp.co.jp/article/COLUMN/20090122/323258/)
- [Java技術最前線 - 「Java SE 6完全攻略」第97回 アノテーションを処理する その4：ITpro](http://itpro.nikkeibp.co.jp/article/COLUMN/20090129/323775/)
- [Java技術最前線 - 「Java SE 6完全攻略」第98回 アノテーションを処理する その5：ITpro](http://itpro.nikkeibp.co.jp/article/COLUMN/20090204/324186/)
- [Java技術最前線 - 「Java SE 6完全攻略」第99回 アノテーションを処理する その6：ITpro](http://itpro.nikkeibp.co.jp/article/COLUMN/20090212/324605/)
- [Java技術最前線 - 「Java SE 6完全攻略」第100回 アノテーションを処理する その7：ITpro](http://itpro.nikkeibp.co.jp/article/COLUMN/20090218/324994/)
- [アノーテーションプロセッサーをEclipseで動かすまでに豪華メンバーにサポートを受けるなど - Togetterまとめ](http://togetter.com/li/517171)
- [eclipseでカスタムアノテーションを作るためのメモ: mitsuruogの日記](http://se35over.seesaa.net/article/283647859.html)
- [javax.annotation.processing (Java Platform SE 8 )](http://docs.oracle.com/javase/jp/8/docs/api/javax/annotation/processing/package-summary.html)
- [Java技術最前線 - 「Java SE 6完全攻略」第11回　コンポーネントのロードを行うServiceLoader：ITpro][7]
- [vvakame / build.gradle | GitHub Gist](https://gist.github.com/vvakame/5176993#file-build-gradle-L196)
- [第38章 Eclipse プラグイン](http://gradle.monochromeroad.com/docs/userguide/eclipse_plugin.html)
- [Node (Groovy 2.4.3)](http://docs.groovy-lang.org/latest/html/api/groovy/util/Node.html)
- [GradleでEclipseの設定をカスタマイズする - @ikikko のはてなブログ](http://ikikko.hatenablog.com/entry/20120214/1329239103)


[1]: http://docs.oracle.com/javase/jp/8/docs/api/javax/annotation/processing/Processor.html
[2]: http://docs.oracle.com/javase/jp/8/docs/api/javax/annotation/processing/AbstractProcessor.html
[3]: http://docs.oracle.com/javase/jp/8/docs/api/javax/lang/model/SourceVersion.html
[4]: http://docs.oracle.com/javase/jp/8/docs/api/javax/tools/Diagnostic.Kind.html
[5]: http://docs.oracle.com/javase/jp/8/docs/api/javax/annotation/processing/Messager.html
[6]: http://docs.oracle.com/javase/jp/8/docs/api/javax/lang/model/element/Element.html
[7]: http://itpro.nikkeibp.co.jp/article/COLUMN/20061215/257003/
[8]: http://docs.oracle.com/javase/jp/8/docs/api/javax/annotation/processing/Filer.html
[9]: http://docs.oracle.com/javase/jp/8/docs/api/javax/tools/JavaFileObject.html