21

Java Annotation Processor 内から型の実際の元のソース コードにアクセスしようとしています。これはどういうわけか可能ですか?ありがとう!

4

5 に答える 5

25

I had a problem where I had to access some source code (the initializer code for a non-String/non-primitive constant) and got it solved by accessing the source code via the Compiler Tree API.

Here's the general recipe:

1. Create a custom TreePathScanner:

private static class CodeAnalyzerTreeScanner extends TreePathScanner<Object, Trees> {

private String fieldName;

private String fieldInitializer;

public void setFieldName(String fieldName) {
    this.fieldName = fieldName;
}

public String getFieldInitializer() {
    return this.fieldInitializer;
}

@Override
public Object visitVariable(VariableTree variableTree, Trees trees) {
    if (variableTree.getName().toString().equals(this.fieldName)) {
        this.fieldInitializer = variableTree.getInitializer().toString();
    }

    return super.visitVariable(variableTree, trees);
}

2. In your AbstractProcessor, save a reference to the current compilation tree by overriding the init method:

@Override
public void init(ProcessingEnvironment pe) {
    super.init(pe);
    this.trees = Trees.instance(pe);
}

3. Get the initialization source code for the VariableElement (in your case an enum):

// assuming theClass is a javax.lang.model.element.Element reference
// assuming theField is a javax.lang.model.element.VariableElement reference
String fieldName = theField.getSimpleName().toString();
CodeAnalyzerTreeScanner codeScanner = new CodeAnalyzerTreeScanner();
TreePath tp = this.trees.getPath(theClass);

codeScanner.setFieldName(fieldName);
codeScanner.scan(tp, this.trees);
String fieldInitializer = codeScanner.getFieldInitializer();

And that's it! In the end the fieldInitiliazer variable is going to contain the exact line(s) of code used to initialize my constant. With some tweaking you should be able to use the same recipe to access the source code of other element types in the source tree (i.e. methods, package declarations, etc)

For more reading and examples read this article: Source Code Analysis Using Java 6 APIs.

于 2012-03-20T19:27:59.943 に答える
3

簡単な答えはそれが不可能だということです。

SunのSDK5のアノテーション処理で使用されるMirrorAPIJavaDocから:

Mirror APIは、プログラムのセマンティック構造をモデル化するために使用されます。クラス、メソッド、フィールドなど、プログラムで宣言されたエンティティの表現を提供します。 個々のステートメントや式など、メソッドレベルより下の構成は表示されません

Java 6アノテーション処理は新しいAPIに基づいていますが、それでもコード構造に関する詳細は提供されていません。

于 2011-06-16T15:44:57.480 に答える
3

Mirror API は Reflection API と同等ですが、コンパイル時に行われます。この API を使用してメソッドの内部コンテンツを読み取ることはできません。それ以外は問題ないはずです。

本当にこれを行いたい場合は、読み取りたいソース ファイルで入力ストリームを取得するためのハックが存在する可能性があります。

  • Hibernate Metamodel Generator は、 XmlParser.getInputStreamForResource() でFiler.getResource () を使用して XML ファイルを読み取ります。問題は、CLASS_OUTPUT と SOURCE_OUPUT しかサポートされていないため、適切ではない可能性があることです。

  • もう 1 つの解決策は、ソース ファイルのパスを見つけてから、通常の入力ストリームを開くことです。コンパイル時に AndroidManifest.xml ファイルを読み取るために、AndroidAnnotations に対してこの種の汚いハックを行いました。AndroidManifestFinder.findManifestFile()を参照してください。

于 2011-06-17T08:27:56.680 に答える
1

コンパイラ ツリー API ( http://download.oracle.com/javase/6/docs/jdk/api/javac/tree/index.html )を試すことができます。
この API は、Java コンパイラが Java の抽象構文ツリーを操作するために使用します。プログラム。ステートメント、ループ、式などの Java 言語構造にまで及びます。JDK ディレクトリ (tools.jar という名前) で jar ライブラリを見つけることができます。

于 2011-11-01T17:58:27.317 に答える