2010-06-21 5 views
5

Ich versuche, den Wert einer Enumeration in einer Annotation mithilfe eines Annotation Processor und Annotation Mirror zu lesen, aber ich bekomme null zurück. Ich denke, das hat mit dem AnnotationValue zu tun, das eine Enum als VariableElement umschließt. Das doc für VariableElement # getConstantValue() sagt "Gibt den Wert dieser Variablen zurück, wenn es sich um ein letztes Feld handelt, das mit einer Kompilierzeitkonstante initialisiert wird." Okay, aber final ist kein gültiger Modifikator für ein Anmerkungselement. Außerdem habe ich keine Probleme andere Annotationswerte zu lesen, nur Enums.So erfassen Sie eine Enum aus einem AnnotationValue in einem Annotation Processor

Ich habe einige Nachforschungen gemacht und es scheint, dass der AnnotationValue zur Laufzeit als Symbol.VarSymbol instanziiert wird, aber Symbol.VarSymbol # getConstantValue() sieht so aus, als sollte es nur das Objekt zurückgeben.

Schließlich, wenn ich eine toString() auf den AnnotationValue mache, bekomme ich den richtigen Wert.

Die Annotation:

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

    public static enum Behavior 
    { 
     NEW, NULL; 
    } 
} 

Teil meines Prozessors und in einer Vielzahl von verschachtelten Schleifen an der richtigen AnnotaionMirror zu bekommen:

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 + ""); 

    } 
    ... 
} 

EDIT: eine vollständigere Version des Prozessors.

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; 
    } 
} 
+0

Ich habe vergessen zu erwähnen, dass dies die Java SE 6 Version der Annotationsverarbeitung ist. –

Antwort

3

Aus der Dokumentation für getConstantValue.

„Insbesondere Enum Konstanten sind keine Konstanten sein kompilieren Zeit als einen konstanten Wert zu haben, geben Sie das ein Feld muss entweder ein primitiver Typ sein oder Zeichenfolge. "

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

Um den Wert der Enumeration Konstante zu erhalten entweder die getAnnotation API verwenden oder eine AnnotationValueVisitor verwenden.

+0

Es ist dann seltsam, dass Enums in VariableElement gesetzt werden; from AnnotationValue: "VariableElement (repräsentiert eine Enum-Konstante)" Wie könnten Sie AnnotationValueVisitor genauer erläutern? Ich habe versucht, die folgenden: private Klasse AnnotationValueVisitorImpl erstreckt SimpleAnnotationValueVisitor6 { @Override geschützt Object default (Object o, Objekt p) {\t Rückkehr o; \t} } Aber wenn ich es an die AnnotationValue akzeptieren Methode erhalte ich ein Symbol.VarSymbol und nicht die Enum, die ich gesucht habe. –

+0

FYI @ joe-darcy hat die Java Annotation API geschrieben –

1

Ich hatte ein ähnliches Problem mit Ihrem (außer ich hatte nicht mit enums zu tun, ich brauchte den Wert einer Nicht-String/nicht primitive Konstante) und löste es durch den Zugriff auf den Quellcode über die Compiler Tree API.

Hier ist das allgemeine Rezept:

1. Erstellen Sie einen benutzerdefinierten 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 Ihrem AbstractProcessor, einen Verweis auf den aktuelle Zusammenstellung Baum speichert durch Überschreiben der init-Methode:

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

3. Holen Sie die Initialisierung Quellcode für die VariableElement (in Ihrem Fall eine Enumeration):

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

Und das ist es! Damit sollten Sie in der Lage sein, den Initialisierungswert für ein mit Anmerkungen versehenes Feld zu erreichen, den Sie normalerweise nicht mit VariableElement.getContantValue erhalten können (d. H. Jede "Konstante", die kein String oder Primitiv ist).

Für mehr lesen und Beispiele lesen Sie diese article: Source Code Analysis Using Java 6 APIs.

Verwandte Themen