読者です 読者をやめる 読者になる 読者になる

Pluggable Annotation Processor APIでimport文を取る

APT、Pluggable Annotation Processorは皆さんお使いでしょうか。

タイトルの通りですが、APTでimport文を取ってみます。

gradleはとりあえずこんな感じです。
今回はAPTのProcessorを作るプロジェクトですがAPTを使います(グルグル目)

準備

apply plugin: 'java'
apply plugin: "net.ltgt.apt"

buildscript {
  repositories {
    maven {
      url "https://plugins.gradle.org/m2/"
    }
  }
  dependencies {
    classpath "net.ltgt.gradle:gradle-apt-plugin:0.9"
  }
}

sourceCompatibility = 1.8
repositories {
    jcenter()
}

dependencies {
    compileOnly group: 'com.google.auto.service', name: 'auto-service', version: '1.0-rc2'
    compileOnly files("${System.properties['java.home']}/../lib/tools.jar")
    testCompile 'junit:junit:4.12'
}

少し解説:AutoServiceについて

AutoServiceGoogleのライブラリのautoの中の一つです。
ServiceLoaderを楽に扱うためのライブラリです。

今回作るAnnotation ProcessorのコードはServiceLoader経由でロードされて使用されます。

META-INF/servicesの配下にServiceのインターフェース、もしくは抽象クラスのFQCNと同じ名前のファイルに
ロードしたいプロバイダクラス(ざっくりいうと実装クラス)を改行区切りで記述します。
Javadoc見る感じだと#でコメントも書けるそうです。

本来は自分でファイルを記述しないといけないところをAutoServiceを使って楽をします。
@AutoServiceをつけることでMETA-INF/services/(インターフェース名・抽象クラス名)に
自動的にアノテーションを付けたクラスを書いてくれる形になります。

では今回実装したソースはこちら。

import java.util.Set;

import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.Processor;
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 com.google.auto.service.AutoService;
import com.sun.source.util.TreePath;
import com.sun.source.util.Trees;

@AutoService(Processor.class)
@SupportedSourceVersion(SourceVersion.RELEASE_8)
@SupportedAnnotationTypes("*")
public class ImportShowProcessor extends AbstractProcessor {

  @Override
  public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
    roundEnv.getRootElements()
      .stream()
      .map(Trees.instance(this.processingEnv)::getPath)
      .map(TreePath::getCompilationUnit)
      .flatMap((unit) -> unit.getImports()
        .stream())
      .forEach(System.out::println);
    return true;

  }

}

そんなに長くはありませんね。

クラスについてるAnnotationの説明から

まず@AutoServiceですが、これは先ほど説明したAutoServiceですね。
説明は省きます。

@SupportedSourceVersion(SourceVersion.RELEASE_8)は
このAnnotation ProcessorはJava8からサポートしてますよ、というメタ要素ですね。
このアノテーションを付けてない、かつ親クラスの実装を変更してなければ、6からサポートしてます、という形になります。

@SupportedAnnotationTypes("*")
これは全てのコンパイル要素を対象とするようです。

上記二つのAnnotationはそのアノテーション名と似たような名前のメソッドがAbstractProcessorに定義されています。

もう一つSupportedOptionsというアノテーションもあって、こちらはコンパイル時のオプションから値を注入できるような仕組みだそう。
今回は使いません。

processメソッドについて

こちらが今回実装した部分ですね、と言っても
そんな大したことはしてなくて
まぁimport文取り出して、表示してるだけですね。

じゃあこちらのAnnotationProcessorを使ってコンパイルしてみます。
gradleのjarタスクで生成されたjarを使います。

javac -cp lombok-processor-example.jar Test.java

コンパイルするソースはこちらです。

import java.util.List;
import java.awt.*;

class Test{
	List<String> yyy;
	String test;
} 

出力は・・・

import java.util.List;

import java.awt.*;

import文が出ましたね。はい。

というわけで

d.hatena.ne.jp

jdeps使いましょう。

今回書いたソースはこちらに置いてあります。
github.com


参考
fits.hatenablog.com