Java 6 annotation processing -- getting a class from an annotation

Ralph picture Ralph · Oct 7, 2011 · Viewed 15.7k times · Source

I have an custom annotation called @Pojo which I use for automatic wiki documentation generation:

package com.example.annotations;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.SOURCE)
@Target(ElementType.METHOD)
public @interface Pojo {
    Class<?> value();
}

I use it like this:

@Pojo(com.example.restserver.model.appointment.Appointment.class)

to annotation a resource method so that the annotation processor can automatically generate a wiki page describing the resource and type that it expects.

I need to read the value of the value field in an annotation processor, but I am getting a runtime error.

In the source code for my processor I have the following lines:

final Pojo pojo = element.getAnnotation(Pojo.class);
// ...
final Class<?> pojoJavaClass = pojo.value();

but the actual class in not available to the processor. I think I need a javax.lang.model.type.TypeMirror instead as a surrogate for the real class. I'm not sure how to get one.

The error I am getting is:

javax.lang.model.type.MirroredTypeException: Attempt to access Class object for TypeMirror com.example.restserver.model.appointment.Appointment

The Appointment is a class mentioned in one of my @Pojo annotation.

Unfortunately, document and/or tutorials on Java annotation processing seems scarce. Tried googling.

Answer

Dave Dopson picture Dave Dopson · Apr 16, 2012

I came here to ask the EXACT same question. ... and found the same blog link posted by Ralph.

It's a long article, but very rich. Summary of the story -- there's two ways to do it, the easy way, and the "more right" way.

This is the easy way:

private static TypeMirror getMyValue1(MyAnnotation annotation) {
    try
    {
        annotation.myValue(); // this should throw
    }
    catch( MirroredTypeException mte )
    {
        return mte.getTypeMirror();
    }
    return null; // can this ever happen ??
}

The other more tedious way (without exceptions):

private static AnnotationMirror getAnnotationMirror(TypeElement typeElement, Class<?> clazz) {
    String clazzName = clazz.getName();
    for(AnnotationMirror m : typeElement.getAnnotationMirrors()) {
        if(m.getAnnotationType().toString().equals(clazzName)) {
            return m;
        }
    }
    return null;
}

private static AnnotationValue getAnnotationValue(AnnotationMirror annotationMirror, String key) {
    for(Entry<? extends ExecutableElement, ? extends AnnotationValue> entry : annotationMirror.getElementValues().entrySet() ) {
        if(entry.getKey().getSimpleName().toString().equals(key)) {
            return entry.getValue();
        }
    }
    return null;
}


public TypeMirror getMyValue2(TypeElement foo) {
    AnnotationMirror am = getAnnotationMirror(foo, MyAnnotation.class);
    if(am == null) {
        return null;
    }
    AnnotationValue av = getAnnotationValue(am, "myValue");
    if(av == null) {
        return null;
    } else {
        return (TypeMirror)av.getValue();
    }
}

Of course, once you get a TypeMirror, you (at least in my experience) pretty much always want a TypeElement instead:

private TypeElement asTypeElement(TypeMirror typeMirror) {
    Types TypeUtils = this.processingEnv.getTypeUtils();
    return (TypeElement)TypeUtils.asElement(typeMirror);
}

... that last little non-obvious bit took me an hour of hair pulling before I sorted it out the first time. These annotation processors are actually not that hard to write at all, the API's are just super confusing at first and mindbendingly verbose. I'm tempted to put out a helper class that makes all the basic operations obvious ... but that's a story for another day (msg me if you want it).