views:

54

answers:

1

I am trying to read the value of an enum in an annotation using an annotation processor and annotation mirror, but I am getting back null. I think this has to do with the AnnotationValue wrapping an Enum as a VariableElement. The doc for VariableElement#getConstantValue() says "Returns the value of this variable if this is a final field initialized to a compile-time constant." Okay, but final isn't a valid modifier for an annotation member. Also of note is I have no trouble reading other annotation values, just Enums.

I've done some sleuthing an it appears the AnnotationValue is instantiated as a Symbol.VarSymbol at run-time, but Symbol.VarSymbol#getConstantValue() looks like it should just return the object.

Finally if I do a toString() on the AnnotationValue I get the proper value.

The Annotation:

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

    public static enum Behavior
    {
        NEW, NULL;
    }
}

Part of my Processor and nested inside a plethora of loops to get at the proper 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: a more complete version of the Processor.

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;
    }
}
A: 

From the documentation for getConstantValue:

"In particular, enum constants are not considered to be compile-time constants. To have a constant value, a field's type must be either a primitive type or String."

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

To get the value of the enum constant either use the getAnnotation API or use an AnnotationValueVisitor.

Joe Darcy
It is strange then that enums are put in VariableElement; from AnnotationValue: "VariableElement (representing an enum constant)"Anyway could you elaborate on AnnotationValueVisitor? I tried the following: private class AnnotationValueVisitorImpl extends SimpleAnnotationValueVisitor6<Object, Object> { @Override protected Object defaultAction(Object o, Object p) { return o; } }However when I pass it to the AnnotationValue's accept method I get a Symbol.VarSymbol and not the enum I was looking for.
J Hall