Consider the following code:
public class SimpleTest {
private Map<@JSON Integer,Map<@Frozen Integer,@Enumerated(value = Enumerated.Encoding.NAME, test = "123") String>> map;
}
With the latest JDK8 API for annotation processing, how can I access the list of annotations (@JSON, @Frozen & @Enumerated) and their corresponding attributes (value & test for @Enumerated) from the VariableElement ?
final VariableElement mapElm = els.stream().filter(x -> x.getSimpleName().contentEquals("map")).findFirst().get();
???
???
I've tried many tricks, like mapElm.getTypeArguments().get(0)
for the @Json Integer
but I never succeed to get my hand on the annotation @JSON...
Edit: By accessing internal classes of the JDK, I can have access to those annotations but it's so hacky and sensitive to impl change that I'm wondering whether there is a better way
public static class SimpleEntityCodecFactoryTest {
private Map<@JSON Integer,Map<@Frozen Integer,@Enumerated(value = Enumerated.Encoding.NAME, test = "123") String>> map;
}
final TypeElement typeElement = elementUtils.getTypeElement(SimpleEntityCodecFactoryTest.class.getCanonicalName());
final List<VariableElement> els = ElementFilter.fieldsIn(typeElement.getEnclosedElements());
final VariableElement mapElt = els.stream().filter(x -> x.getSimpleName().contentEquals("map")).findFirst().get();
final com.sun.tools.javac.util.List<Attribute.TypeCompound> typeAttributes = ((Symbol.VarSymbol) mapElt).getMetadata().getTypeAttributes();
for (Attribute.TypeCompound typeAttribute : typeAttributes) {
final DeclaredType annotationType = typeAttribute.getAnnotationType();
System.out.println(format("Accessing annotation '%s' at location : %s",annotationType.toString(),typeAttribute.getPosition().location));
for (Map.Entry<Symbol.MethodSymbol,Attribute> entry : typeAttribute.getElementValues().entrySet()) {
final Symbol.MethodSymbol methodSymbol = entry.getKey();
final Attribute attribute = entry.getValue();
System.out.println(format("Attribute '%s' for annotation '%s' : %s", methodSymbol.name, annotationType.toString(), attribute.toString()));
}
}
The output display:
Accessing annotation 'info.archinnov.achilles.annotations.JSON' at location : TYPE_ARGUMENT(0)
Accessing annotation 'info.archinnov.achilles.annotations.Frozen' at location : TYPE_ARGUMENT(1),TYPE_ARGUMENT(0)
Accessing annotation 'info.archinnov.achilles.annotations.Enumerated' at location : TYPE_ARGUMENT(1),TYPE_ARGUMENT(1)
Attribute 'value' for annotation 'info.archinnov.achilles.annotations.Enumerated' : info.archinnov.achilles.annotations.Enumerated.Encoding.NAME
Attribute 'test' for annotation 'info.archinnov.achilles.annotations.Enumerated' : "123"
The above code is working fine in IntelliJ, but because of the dirty cast ((Symbol.VarSymbol) mapElt).getMetadata(), it is working with Oracle JDK but fails miserably with Eclipse compiler.
Right now, I don't find any other solution than the dirty cast to access annotations in generic types. Any idea is welcomed
Solution:
Thanks to Werner (wmdietl), I can access the nested annotations using the Tree API instead of Elements or TypeMirror
However I'm quite stuck because once I get there, it is not possible to convert any subclass of Tree back to Element or TypeMirror (my real target).
All of my annotation processing is using heavily JavaPoet (https://github.com/square/javapoet) to generate clean source code and this framework only handles TypeMirror, not Tree
In the https://github.com/typetools/checker-framework/blob/master/javacutil/src/org/checkerframework/javacutil/TreeUtils.java class, there are some methods to convert Tree back to Element but it is relying on InternalUtils, which I can't use because it won't be compatible with Eclipse ECJ compiler.
I guess I will have to wait for JDK 9 before having an usable Element API that will be compatible with ECJ compiler
Edit: To make the type annotation work for Eclipse Compiler, I had to cast to internal compiler classes like here: https://github.com/doanduyhai/Achilles/blob/master/achilles-core/src/main/java/info/archinnov/achilles/internals/parser/AnnotationTree.java#L83-L85. It's ugly but that is the only way for now until JDK9.