2010-06-21 2 views
5

Я пытаюсь прочитать значение перечисления в аннотации, используя обработчик аннотации и зеркало аннотации, но я возвращаюсь в нуль. Я думаю, что это связано с тем, что AnnotationValue обертывает Enum как VariableElement. Документ для VariableElement # getConstantValue() говорит: «Возвращает значение этой переменной, если это конечное поле, инициализированное константой времени компиляции». Хорошо, но final не является допустимым модификатором для члена аннотации. Также следует отметить, что мне не трудно читать другие значения аннотации, просто Enums.Как захватить Enum из AnnotationValue в обработчике аннотации

Я сделал несколько следящих действий. Кажется, что AnnotationValue создается как символ. VarSymbol во время выполнения, но Symbol.VarSymbol # getConstantValue() выглядит так, что он должен просто вернуть объект.

Наконец, если я делаю toString() в AnnotationValue, я получаю правильное значение.

аннотацию:

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

    } 
    ... 
} 

EDIT: более полную версию процессора.

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

Я забыл упомянуть, что это версия обработки аннотации Java SE 6. –

ответ

3

Из документации getConstantValue:.

«В частности, константы перечислений не считается компилированиями времени константы Чтобы иметь постоянное значение, тип поля, должно быть либо примитивный типа или String ".

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

Чтобы получить значение перечисления постоянной либо использовать getAnnotation API или использовать AnnotationValueVisitor.

+0

Странно, что перечисления перечислены в VariableElement; from AnnotationValue: «VariableElement (представляющий константу перечисления)» В любом случае вы могли бы подробнее остановиться на AnnotationValueVisitor? Я попытался следующий: частный класс AnnotationValueVisitorImpl расширяет SimpleAnnotationValueVisitor6 { @Override защищенного DefaultAction Object (Объект O, Object р) {\t возврат O; \t} } Однако, когда я передаю его методу принятия AnnotationValue, я получаю Symbol.VarSymbol, а не перечисление, которое я искал. –

+0

FYI @ joe-darcy написал API аннотации Java –

1

У меня была схожая проблема с вашей (за исключением того, что я не имел дело с перечислениями, мне нужно значение константы non-String/non-primitive), и она была решена путем доступа к исходному коду через 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, сохранить ссылку на текущее дерево компиляции путем переопределения метода инициализации:

@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 (т. Е. Любой «константы», которая не является строкой или примитивом).

Для получения дополнительной информации и примеров прочтите это article: Source Code Analysis Using Java 6 APIs.

 Смежные вопросы

  • Нет связанных вопросов^_^