2010-06-21 5 views
5

Sto provando a leggere il valore di un enum in un'annotazione utilizzando un processore di annotazione e un mirror di annotazione, ma sto tornando a zero. Penso che questo abbia a che fare con AnnotationValue che avvolge un Enum come VariableElement. Il doc per VariableElement # getConstantValue() dice "Restituisce il valore di questa variabile se questo è un campo finale inizializzato a una costante in fase di compilazione." Ok, ma finale non è un modificatore valido per un membro di annotazione. Inoltre, non ho problemi a leggere altri valori di annotazione, solo Enums.Come acquisire un Enum da un AnnotationValue in un processore di annotazione

Ho fatto qualche investigazione e sembra che AnnotationValue sia istanziato come Symbol.VarSymbol in fase di esecuzione, ma Symbol.VarSymbol # getConstantValue() sembra che dovrebbe restituire l'oggetto.

Infine se faccio un toString() su AnnotationValue ottengo il valore corretto.

La nota:

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

    public static enum Behavior 
    { 
     NEW, NULL; 
    } 
} 

Parte del mio processore e annidato all'interno di una pletora di loop per arrivare alla giusta 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 + ""); 

    } 
    ... 
} 

EDIT: una versione più completa del processore.

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

Ho dimenticato di menzionare questa è la versione Java SE 6 dell'elaborazione delle annotazioni. –

risposta

3

Dalla documentazione per getConstantValue:.

"In particolare, le costanti enum non sono considerati in fase di compilazione delle costanti di avere un valore costante, il tipo di un campo deve essere o un tipo primitivo o stringa. "

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

Per ottenere il valore della costante di enumerazione utilizzare l'API getAnnotation o utilizzare un AnnotationValueVisitor.

+0

È strano che le enumerazioni siano inserite in VariableElement; da AnnotationValue: "VariableElement (che rappresenta una costante enum)" Ad ogni modo è possibile elaborare su AnnotationValueVisitor? Ho provato quanto segue: classe AnnotationValueVisitorImpl privato estende SimpleAnnotationValueVisitor6 { @Override protetta DefaultAction Object (oggetto o, Object p) {\t ritorno o; \t} } Tuttavia, quando lo passo al metodo di accettazione di AnnotationValue, ricevo Symbol.VarSymbol e non l'enumerazione che cercavo. –

+0

FYI @ joe-darcy ha scritto l'API di annotazione Java –

1

Ho avuto un problema simile al tuo (tranne che non avevo a che fare con le enumerazioni, avevo bisogno del valore di una costante non string/non primitiva) e l'ho risolto accedendo al codice sorgente tramite Compiler Tree API.

Ecco la ricetta generale:

1. Creare un TreePathScanner personalizzato:

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. Nel vostro AbstractProcessor, salvare un riferimento all'albero compilation corrente eseguendo l'override del metodo init:

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

3. ottenere il codice sorgente di inizializzazione per il VariableElement (nel tuo caso un 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(); 

E questo è tutto! Con questo si dovrebbe essere in grado di ottenere il valore di inizializzazione, per un campo annotato, che normalmente non si può ottenere usando VariableElement.getContantValue (cioè qualsiasi "costante" che non sia una stringa o una primitiva).

Per ulteriori letture ed esempi, leggere questo article: Source Code Analysis Using Java 6 APIs.