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

コンパイル時に特定のAnnotationが付いているClassのmodifierをチェックする方法

java msgpack

先日、


という事例を聞いたので何とかコンパイル時に検知出来ないかぼーっと考えてて、プリプロセス的にチェックできないか思いついたので試してみました。

#ちなみにTweet上ではリフレクションとあったけど、多分実際はJavassistを使ってる気がする


今回サンプルとして動かしてみたのは、@Helloというannotationがついたクラスはstaticじゃないとエラーにするやつ。

org.komamitsu.pptest.annotation.Hello

package org.komamitsu.pptest.annotation;

import java.lang.annotation.*;

@Retention(RetentionPolicy.RUNTIME)
public @interface Hello {}

org.komamitsu.pptest.Main

package org.komamitsu.pptest;

import org.komamitsu.pptest.annotation.Hello;

public class Main {
    @Hello
    static class A {
    }
    
    @Hello
    class B {
    }

    public static void main(String argv[]) {
        System.out.println("helloworld");
    }
}

こんな感じになっている場合に、コンパイル時にclass Bをエラーとしたい。


で、プリプロセス的なタイミングでチェックするやつがこれ

org.komamitsu.pptest.preprocessor.Hello

package org.komamitsu.pptest.preprocessor;

import java.util.Set;

import javax.lang.model.element.Element;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.TypeElement;
import javax.annotation.processing.*;
import javax.tools.Diagnostic.*;

@SupportedAnnotationTypes(value= {"*"})
public class Hello extends AbstractProcessor {
    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
        for (Element elm : roundEnv.getElementsAnnotatedWith(org.komamitsu.pptest.annotation.Hello.class)) {
            Set<Modifier> modifiers = elm.getModifiers();
            if (!modifiers.contains(Modifier.STATIC)) {
                processingEnv.getMessager().printMessage(Kind.ERROR, "@Hello: Only accepts static class. " + elm);
            }
        }
        return true;
    }
}

一応、これも。

pom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>org.komamitsu</groupId>
  <artifactId>pptest</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  
  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <configuration>
          <source>1.6</source>
          <target>1.6</target>
        </configuration>
      </plugin>
    </plugins>
  </build>
</project>

Main classとかをコンパイルするときにorg.komamitsu.pptest.preprocessor.Helloを挟んであげれば良さそうなのだけど、スマートな方法が思いつかず以下のような手順で...

$ pwd
/Users/komamitsu/Dev/workspace/pptest
$ mvn clean package
    :
$ cd src/main/java/
$ javac -cp ../../../target/pptest-0.0.1-SNAPSHOT.jar -processor 
error: @Hello: Only accepts static class. org.komamitsu.pptest.Main.B
1 error

本当はちょこっとwarningが出てるんだけど、見辛いし本筋と関係ないので省略。

とまあ、こんな感じでコンパイル時にチェックできそうなんだけども、これをどうmsgpackに組み込むか(ユーザーが特に何もせずに当該チェックが作動するか)が思いついてない...