5

注釈プロセッサと注釈ミラーを使用して注釈の列挙型の値を読み取ろうとしていますが、null が返されます。これは、Enum を VariableElement としてラップする AnnotationValue に関係していると思います。VariableElement#getConstantValue() のドキュメントには、「これがコンパイル時の定数に初期化された最終フィールドである場合、この変数の値を返す」と書かれています。わかりましたが、final は注釈メンバーの有効な修飾子ではありません。また、列挙型だけで、他の注釈値の読み取りに問題がないことにも注意してください。

実行時に AnnotationValue が Symbol.VarSymbol としてインスタンス化されているように見えますが、Symbol.VarSymbol#getConstantValue() はオブジェクトを返すだけのように見えます。

最後に、AnnotationValue で toString() を実行すると、適切な値が得られます。

注釈:

package annotation;
public @interface AnAnnotation
{
    String value();
    Behavior defaultBehavior() default Behavior.NEW;

    public static enum Behavior
    {
        NEW, NULL;
    }
}

私のプロセッサの一部であり、適切な AnnotaionMirror を取得するために多数のループ内にネストされています。

Map<? extends ExecutableElement, ? extends AnnotationValue> annotationValues = elemUtils.getElementValuesWithDefaults(annotationMirror);
for (ExecutableElement method : annotationValues.keySet())
{
    ...
    else if ("defaultBehavior".equals(method.getSimpleName().toString()))
    {

        defaultBehavior = (Behavior)( (VariableElement)annotationValues.get(method).getValue()).getConstantValue();

        // This prints "NEW" or "NULL" correctly
        processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE,annotationValues.get(method).toString());
        // This prints null incorrectly (expect "NEW" or "NULL")
        processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE, defaultBehavior + "");

    }
    ...
}

編集: プロセッサのより完全なバージョン。

package annotation.processor;

import java.util.*;

import javax.annotation.processing.*;
import javax.lang.model.element.*;
import javax.lang.model.type.*;
import javax.lang.model.util.*;
import javax.tools.*;

import annotation.AnAnnotation;
import annotation.AnAnnotation.Behavior;

@SupportedAnnotationTypes("annotation.AnAnnotation")
public class AnAnnotationProcessor extends AbstractProcessor
{
    Types typeUtils;
    Elements elemUtils;

    @Override
    public void init(ProcessingEnvironment processingEnv)
    {
        super.init(processingEnv);
        typeUtils = processingEnv.getTypeUtils();
        elemUtils = processingEnv.getElementUtils();
    }

    @Override
    public boolean process(Set<? extends TypeElement> annotations,
                           RoundEnvironment roundEnv)
    {
        processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE,
            "Entering AnnotationNullableClassProcessor");

        /****** Iterate over all annotaions being processed (only AnAnnotation) ******/
        for (TypeElement annotation : annotations)
        {
            /****** Iterate over all elements that are annotated with the annotation ******/
            for (Element element : roundEnv.getElementsAnnotatedWith(annotation))
            {
                /****** Iterate over all the declared annotations of the element ******/
                for (AnnotationMirror annotationMirror :  element.getAnnotationMirrors())
                {
                    final String annotationTypeName = annotationMirror.getAnnotationType().toString();

                    // Process annotations of type AnAnnotation
                    if (annotationTypeName.equals(AnAnnotation.class.getName()))
                    {
                        Map<? extends ExecutableElement, ? extends AnnotationValue> annotationValues = elemUtils.getElementValuesWithDefaults(annotationMirror);

                        /****** Iterate over the annotation's values. ******/
                        for (ExecutableElement method : accessorValues.keySet())
                        {
                            if ("defaultBehavior".equals(method.getSimpleName().toString()))
                            {
                                Behavior defaultBehavior = (Behavior)( (VariableElement)annotationValues.get(method).getValue()).getConstantValue();

                                // This prints "NEW" or "NULL" correctly
                                processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE,annotationValues.get(method).toString());
                                // This prints null incorrectly (expect "NEW" or "NULL")
                                processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE, defaultBehavior + "");
                            }
                        }
                    }
                }
            }
        }

        return true;
    }
}
4

2 に答える 2

3

getConstantValue のドキュメントから:

「特に、列挙型定数はコンパイル時の定数とは見なされません。定数値を持つには、フィールドの型がプリミティブ型または文字列である必要があります。」

http://java.sun.com/javase/6/docs/api/javax/lang/model/element/VariableElement.html#getConstantValue()

enum 定数の値を取得するには、getAnnotation API を使用するか、AnnotationValueVisitor を使用します。

于 2010-06-22T01:47:07.430 に答える
1

あなたと同様の問題があり(列挙型を扱っていなかったことを除いて、非文字列/非プリミティブ定数の値が必要でした)、Compiler Tree APIを介してソースコードにアクセスすることで解決しました。

一般的なレシピは次のとおりです。

1. カスタム 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. AbstractProcessor で、init メソッドをオーバーライドして現在のコンパイル ツリーへの参照を保存します。

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

3. VariableElement の初期化ソース コードを取得します (この場合は列挙型)。

// 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();

以上です!これにより、通常は VariableElement.getContantValue を使用して取得できない注釈付きフィールドの初期化値 (つまり、文字列またはプリミティブではない「定数」) を取得できるはずです。

詳細と例については、Java 6 API を使用したソース コード分析に関する記事を参照してください。

于 2012-03-21T05:16:40.597 に答える