Issue
I've struggled with this for a while now, relying on various arcane casts and manipulation of toString to get it working, but there has to be a proper way of doing this.
I've got an annotation processor that looks like this:
@SupportedAnnotationTypes("processor.EchoFields")
@SupportedSourceVersion(SourceVersion.RELEASE_11)
public class TestProcessor extends AbstractProcessor {
@Override
public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
roundEnvironment.getElementsAnnotatedWith(EchoFields.class)
.stream()
.filter(e -> e.getKind() == ElementKind.CLASS)
.map(TypeElement.class::cast)
.forEach(this::echoFields);
return false;
}
private void echoFields(TypeElement element) {
log("Fields of class %s", element);
element.getEnclosedElements().stream()
.filter(e -> e.getKind() == ElementKind.FIELD)
.map(VariableElement.class::cast)
.forEach(this::echoField);
}
private void echoField(VariableElement element) {
log("\tField %s of type %s", element.getSimpleName().toString(), element.asType().toString());
for (AnnotationMirror annotationMirror : element.getAnnotationMirrors()) {
log("\t\t%s", annotationMirror);
}
}
private void log(String format, Object... parameters) {
processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE, String.format(format, parameters));
}
}
When run on my test class that looks like this:
@EchoFields
public class Pojo {
@TypeAnnotation
@FieldAnnotation
private List<String> aListOfStrings;
}
(@TypeAnnotation, and @FieldAnnotation have targets TYPE(_USE) and FIELD respectively)
I get the following output:
Note: Fields of class consumer.Pojo
Note: Field aListOfStrings of type @processor.TypeAnnotation java.util.List<java.lang.String>
Note: @processor.FieldAnnotation
Whereas what I want is this:
Note: Fields of class consumer.Pojo
Note: Field aListOfStrings of type java.util.List<java.lang.String>
Note: @processor.TypeAnnotation
Note: @processor.FieldAnnotation
With the important part being java.util.List<java.lang.String>
without @processor.TypeAnnotation
- Listing @processor.TypeAnnotation
with the annotations is just a bonus and not something I need.
I can't seem to find any way of doing this though. Every time I find something that removes the annotation I also lose the type parameters (java.lang.String
). I've poked around in the classes implementing the annotation processing API and there is definitely methods there that do what I want - but I can't find any way of invoking them through the public API and I don't want to rely on implementation details (especially since I got vastly different implementations when I tried this on JDK 8).
There's a runnable Gradle project at https://github.com/Raniz85/processor-test, just clone and run ./gradlew build
Solution
Since TypeAnnotation
is marked as TYPE+TYPE_USE (and note that TYPE isn't relevant for this issue) and not FIELD, it isn't an annotation on the field at all, but is actually on the declared type of the field. You can put TYPE_USE annotations on other things as well, like local variables - but you cannot read them from a normal annotation processor. From How to access TypeUse annotation via AnnotationProcessor:
The TYPE_USE annotations are a bit tricky, because the compiler treats them differently, than the "old usage" annotations.
So as you correctly observed, they are not passed to annotation processor, and your process() method will never receive them.
Without knowing specifically what problem you are trying to solve, it is hard to say if you actually want TYPE_USE in the first place, or if you are just experimenting with the different possible targets that an annotation may have. Probably you just want to use FIELD instead here, since you are writing an annotation processor (which traverses your API, and not the source code itself, so you'll miss any TYPE_USE on local vars, etc).
--
Also, be very careful of using TypeMirror.toString() or Element.toString(), in some environments these will not output what you are expecting. Either use the Name type, or something like javapoet's TypeName hierarchy.
Answered By - Colin Alworth
Answer Checked By - David Goodson (JavaFixing Volunteer)