Reputation: 15
I have a question according to the API AnnotationProcessing. Here's a little Example.
@Retention(RetentionPolicy.SOURCE)
@Target(ElementType.FIELD)
public @interface MyAnnotation
{
String strNumberOne() default "";
String strNumberTwo() default "";
String strNumberThree() default "";
}
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv){
// I do not know how to beginn here
return true;
}
}
public class MyClass
{
@MyAnnotation(strNumberOne="one", strNumberTwo= "two")
private String strARandomString;
}
Now I want to read the fields declared in the annotation and if there's a field not declared my program should take the default-value.
I want to write the values in a list. At the end my list shoud look like that:
LinkedList<String> s = new LinkedList<>();
s.add(strNumberOne); // "one"
s.add(strNumberTwo); // "two"
s.add(strNumberThree); // (default) ""
How do I do this? I found a method that could help. It's in the Interface "Elements" the methods name is "getElementValuesWithDefaults()". But I don't know how to use it.. Also I want to know what's the difference betweens TypeElement and Element. I am thankful for every answer! :)
Best regards!
Upvotes: 0
Views: 1146
Reputation: 37835
If MyAnnotation
is the annotation which your processor supports, then you'd just write something like this:
@Override
public boolean process(Set<? extends TypeElement> annotations,
RoundEnvironment env) {
if (shouldClaim(annotations)) {
for (Element e : env.getElementsAnnotatedWith(MyAnnotation.class)) {
MyAnnotation a = e.getAnnotation(MyAnnotation.class);
String str1 = a.strNumberOne();
String str2 = a.strNumberTwo();
String str3 = a.strNumberThree();
// Add them to a List or whatever you need.
}
return true;
}
return false;
}
private boolean shouldClaim(Set<? extends TypeElement> annotations) {
Set<String> supported = getSupportedAnnotationTypes();
for (TypeElement a : annotations) {
if (supported.contains(a.getQualifiedName().toString()))
return true;
}
return false;
}
The logic of the shouldClaim
method is explained by the documentation for process
. It would be more complicated if your annotation supports e.g. *
or a type of the form name.*
, but in general you don't. (See getSupportedAnnotationTypes
for a description of what those mean.)
If MyAnnotation
isn't the annotation which your processor supports, then you would need to go through getElementValuesWithDefaults
if it's a type declared in the package you're compiling. Because annotation processing happens during compilation, class files don't exist yet for the source files being compiled, which is why we use the Element
API instead.
Element
represents a declaration of some kind, for example a class, method or variable. TypeElement
represents a class, interface, enum or annotation type declaration. TypeElement
is similar to Class
in terms of what we can do with it, except that we can use a TypeElement
for a class which is not necessarily compiled.
To get annotation values through the element API, you'd do something like this:
Elements elements = processingEnv.getElementUtils();
TypeElement myAnnotation = elements.getTypeElement("com.example.MyAnnotation");
for (Element e : env.getElementsAnnotatedWith(myAnnotation)) {
for (AnnotationMirror mirror : e.getAnnotationMirrors()) {
DeclaredType annotationType = mirror.getAnnotationType();
Element annotationDecl = annotationType.asElement();
if (myAnnotation.equals(annotationDecl)) {
Map<? extends ExecutableElement, ? extends AnnotationValue> values =
elements.getAnnotationValuesWithDefaults(mirror);
String str1 = (String) getValue(values, "strNumberOne");
String str2 = (String) getValue(values, "strNumberTwo");
String str3 = (String) getValue(values, "strNumberThree");
// ...
}
}
}
private Object getValue(Map<? extends ExecutableElement,
? extends AnnotationValue> values,
String name) {
for (Map.Entry<? extends ExecutableElement,
? extends AnnotationValue> e : values.entrySet()) {
if (name.contentEquals(e.getKey().getSimpleName()))
return e.getValue().getValue();
}
return null;
}
That's a pain, but we only need to use the element API if the annotation we're interested in is one of the classes being compiled.
We might also want to find the AnnotationMirror
and/or AnnotationValue
to cause a message of some kind on a particular element using one of the Messager.printMessage
overloads which takes one of the aforementioned objects as an argument.
Upvotes: 3