AnnotationProcessorでプログラムの自動生成をするときに役に立つかもしれない記事

## AnnotationProcessorでプログラムの自動生成
:::note warn
本記事はあまりにも情報が少ないため書いた記事です。本なら情報があると思うのですが、ネットでは見つけられませんでした。
そして、書いてる人は使うことをあきらめました。
:::

プログラムを一から生成する場合こちらで紹介する方法ではなく、テンプレートなどから`JavaFileObject`を生成したほうが楽です。
## 代替手段
- リフレクションで実行時に何とかする － 動作速度は遅いですが情報が多いです。
- 部分的に`JavacParser`で作成して差し替え - 圧倒的に楽になると思います。

### 環境
- JDK corretto-11
- Gradle gradle 7.4
- Intellij IDEA 2022.1

## gradleの設定
`AnnotationProcessor`を作成するプロジェクトにいかを追加します。
```gradle
dependencies {
    annotationProcessor 'com.google.auto.service:auto-service:1.0.1'
    compileOnly 'com.google.auto.service:auto-service:1.0.1'

    if (targetCompatibility.toString().startsWith("1."))
        implementation files("${System.properties['java.home']}/../lib/tools.jar")
}

compileJava {
    if (!targetCompatibility.startsWith("1.")) {


        doFirst {
            options.compilerArgs = ["--module-path", classpath.asPath,
                                    "--add-modules", "com.google.auto.service",
                                    "--add-exports", "jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED",
                                    "--add-exports", "jdk.compiler/com.sun.tools.javac.model=ALL-UNNAMED",
                                    "--add-exports", "jdk.compiler/com.sun.tools.javac.processing=ALL-UNNAMED",
                                    "--add-exports", "jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED",
                                    "--add-exports", "jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED",
                                    "--add-exports", "jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED",
                                    "--add-exports", "jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED"]
            classpath = files()
        }
    }
}

```

具体的に何をしているかというと、使用するクラスが`JDKのバージョンが8以下の場合tools.jar`に、それ以外の場合モジュールシステムで隠蔽されているので強制的にexportしています。
また、本来なら自分で`META-INF/service/javax.annotation.processing.Processor`にAnnotationProcessorの情報を記述しないといけないのですが、googleの` auto.service`の`@AutoService`アノテーションをつけると自動で記述してくれます。

## クラス,メソッドを取得する。
アノテーションがつけられているクラスやメソッドを取得します。
```java
package com.example
import com.google.auto.service.AutoService;
import com.sun.tools.javac.code.*;
import com.sun.tools.javac.model.*;
import com.sun.tools.javac.tree.*;
import com.sun.tools.javac.tree.JCTree.*;
import com.sun.tools.javac.util.*;
import javax.annotation.processing.*;
import javax.lang.model.*;
import javax.lang.model.element.*;
import com.sun.tools.javac.processing.JavacProcessingEnvironment;

@AutoService("Processor.class")
@SupportedAnnotationTypes("*")
public class AnnotationProcessor extends AbstractProcessor{

  JavacElements elementUtils;
  Context context;
  TreeMaker maker;
  Names names;

  @Override
  pulbic synchronized void init(ProcessingEnvironment processingEnv){
  elementUtils = ((JavacElements)processingEnv.getElementUtils());
  context = ((JavacElements)processingEnv.getContext());
  maker = TreeMaker.instance(context);
  names = Names.instance(context);
}

  @Override
  public boolean process(Set<? extends TypeElement> annotations,RoundEnvironment roundEnv){
    for (Element element : roundEnv.getElementsAnnotatedWith(Override.class){
      JCTree tree elementUtils.getTree(element);
      JCMethodDecl jcMethodDecl = (JCMethodDecl)tree;
      //             ↑これ
    }
  }
}
```
:::note
なお以下からprocessの中身または一部のみだけ書きます。
:::
## 一つ上のTreeを取得する。
メソッドならメソッドが所属するクラスを取得します。
```java
for (Element element : roundEnv.getElementsAnnotatedWith(Override.class){
  Element enclosingElement = element.getEnclosingElement();
  //           ↑これ
}
```

## クラスにメソッドを作成する。

```java
classDecl.defs = classDecl.defs.prepend(maker.MethodDef(maker.Modifiers(Flags.PUBLIC),
names.fromString("methodName"), //メソッド名
Maker.Ident(names.fromString("String")), //返り値の型
List.nil(),//ジェネリクスの<T,K>←これ
List.nil(),// パラメータ(引数)
List.nil(),// throwされる例外
null, //Block(後述)
null //初期化(用途不明)
));
```

## メソッドを実行する

```java

maker.exec(
  maker.Apply(
    null, //ジェネリクス
    maker.Select( //実行するメソッドのインスタンス？
      maker.Ident(
        names.fromString("Integer") //インスタンス等を名前で指定
      ),
      names.fromString("parseInt") //メソッド名を指定
    ),
    args //メソッドに渡す引数 例えば List.of(maker.Literal("100"))
  )
);
```

## 変数の作成

```java
maker.VarDef(
  maker.Modifiers(Flags.PUBLIC), // PublicやParameterなど
  names.fromString("varName"), // 変数名
  maker.Ident(names.fromString("int")), //型
  exec.getExpression() //初期化 この場合↑の結果を代入している
)
```

## その他
- いろいろな変数がPublicなのをいいことに直接編集するとエラーが多発するので普通に作成したほうがいいです。