4

i'm working on my first annotation processor. I'm trying to get fields of class through annotation on declared object(that is a field of a method). For example:

public class Person {
    private String name;
    private String surname;
}

...
public void myMethod(@MyAnnotation Person person) { 
    /*...*/
}
...

Through @MyAnnotation i want to get 'name' and 'surname' fields.

Is that possible? I did something of similar with method's field:

...
for (Element element : roundEnvironment.getElementsAnnotatedWith(AnnotationOnMethod.class)) {
    ExecutableElement method = (ExecutableElement) element;
    List<? extends VariableElement> parameters = method.getParameters();
    parameters.forEach(p -> {
        /*code here for parameter*/
    });
}
...

Thanks in advance, Luca.

Solution: For my solution i am assuming that the annotation is on method and not on method's parameter. The correct answer to my question is the answer posted by rjeeb. You can see it below.


Set<? extends Element> elements = roundEnvironment.getElementsAnnotatedWith(AnnotationOnMethod.class);
for (Element element : elements) {
    ExecutableElement executableElement = (ExecutableElement) element; 
    List<? extends VariableElement> parameters = executableElement.getParameters();
    for (VariableElement parameter : parameters) {
        DeclaredType declaredType = (DeclaredType) parameter.asType();
        TypeElement clazz = (TypeElement) declaredType.asElement();
        clazz.getEnclosedElements().forEach(o -> {
                if (o.getKind().isField()) {
                    log.info("Simple name of field: " + o.getSimpleName());
                }
        });
    }
}
Luke
  • 516
  • 2
  • 10

1 Answers1

4

Your annotation is of type ElementType.PARAMETER so once you get the element that annotated with it then it should be TypeElement and therefore you can get the fields of this type element

for (Element element : roundEnv.getElementsAnnotatedWith(AnnotationOnMethod.class)) {
    TypeElement typeElement = elementUtils.getTypeElement(element.asType().toString());
    Set<? extends Element> fields = typeElement.getEnclosedElements()
            .stream()
            .filter(o -> o.getKind().isField())
            .collect(Collectors.toSet());

    // do something with the fields
}

you can get the helper classes from the AbstractProcessor

private Messager messager;
private Types typeUtils;
private Elements elementUtils;

@Override
public synchronized void init(ProcessingEnvironment processingEnv) {
    messager = processingEnv.getMessager();
    typeUtils = processingEnv.getTypeUtils();
    elementUtils = processingEnv.getElementUtils();
}
rjeeb
  • 461
  • 3
  • 11
  • Hi, thanks for the answer, can you tell me the package of "elementUtils" on you example? I cannot resolve it in my code. Anyway i resolved my problem, i will update this post :D – Luke Sep 20 '19 at 14:58
  • Thanks, your answer is really useful. I didn't know about that utilities. I will test your code to check if it works. If the code works i will accept the answer as solution. I edited this post, if you want let's check my solution :) – Luke Sep 20 '19 at 15:12
  • The only difference between my solution and yours is that you are assuming that the annotation will be in the method and not on method parameters – rjeeb Sep 20 '19 at 15:14
  • Yes, right. I just tested your code and it works like charm. Is good solution, thanks. – Luke Sep 20 '19 at 15:33
  • 1
    One word of caution: if your annotation processor is re-running incrementally, changes made to the `Person` class won't necessarily cause your processor to re-run, since your annotation is on the `myMethod` member, presumably in another class. Consider annotating the `Person` also so your annotation processor monitors changes there, or else advise your users that this may not work in gradle/eclipse/etc. – Colin Alworth Sep 21 '19 at 17:34