Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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.

like image 844
Ralph Avatar asked Oct 07 '11 13:10

Ralph


People also ask

Can classes be annotated in Java?

Annotations can be applied to declarations: declarations of classes, fields, methods, and other program elements. When used on a declaration, each annotation often appears, by convention, on its own line. As of the Java SE 8 release, annotations can also be applied to the use of types.

Where we can use annotation?

Annotations are used to provide supplemental information about a program. Annotations start with '@'. Annotations do not change the action of a compiled program. Annotations help to associate metadata (information) to the program elements i.e. instance variables, constructors, methods, classes, etc.

When a source code containing an annotation definition is compiled what is the extension?

class file and a source code file, the annotation file must redundantly specify the annotation's bytecode offset and source code index. This can be done in a single . jaif file or two separate . jaif files.


1 Answers

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).

like image 174
Dave Dopson Avatar answered Oct 02 '22 14:10

Dave Dopson