0

I am just starting to learn annotation processing in Java. I have @MyAnnotation which targets ElementType.FIELD, In my Processor I am limiting the annotation to only allow non-null unique value. Which is working as it should.

While logging errors in case where there is infact some duplicate value set to MyAnnotation.value, I want to provide the complete path of both the existing annotations in the source code and the new duplicate one.

My Annotation:

@Retention(RetentionPolicy.RUNTIME)
@Target(value = ElementType.FIELD)
public @interface   ProviderColumn {
    String content_id();
}

Example Parent Class.

public class EnclosingParent {
    @ProviderColumn(content_id="Hello")
    private String value1;

    @ProviderColumn(content_id="Hello")
    private String value2;
}

My AnnotationProcessor

@Override
public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment)
{
    UniqueColumnNameChecker columnNameChecker = new UniqueColumnNameChecker();

    try {

        for (Element annotatedElement : roundEnvironment.getElementsAnnotatedWith(ProviderColumn.class)) {

            // We can cast it, because we know that it is of ElementType.FIELD
            VariableElement variableElement = (VariableElement) annotatedElement;

            try {
                ProviderColumnId providerColumnId =
                        new ProviderColumnId(variableElement);

                // How can I get the EnclosingParent.class or even just a complete canonical class name?

                // throws a DuplicateAnnotationIdException when it detects duplicate entries.
                columnNameChecker.addAnnotationColumnName(providerColumnId);
            }
            catch (IllegalArgumentException e) {
                error(variableElement, e.getMessage());
                return true;
            }
        }
    }
    catch (DuplicateAnnotationIdException ex) {
        error(ex.getMessage());
        return true;
    }

    return true;
}

However I am having trouble figuring out how to obtain enclosing class information from a VariableElement. Since I'm only starting on AnnotationProcessing I'm not even sure if this is possible and I haven't been able to find any questions related to this issue on StackOverflow or anywhere else.

Expected error output

Duplicate content_id()='Hello' detected, Found at 'com.example.EnclosingParent.value1' and 'com.example.EnclosingParent.value2'.

Note: I realize I can get parent info if I define a new ElementType.TYPE Annotation and set to the Enclosing class, but I'm looking to avoid that as it adds additional responsibilities for the third party developer.

Abbas
  • 3,529
  • 5
  • 36
  • 64
  • can you please share the source code for the `error` method – Ahmad Bawaneh May 15 '19 at 12:17
  • @AhmadBawaneh I solved the problem as you can see from my answer below. Error method has nothing special, it just gives `Messager` the message obtained from the thrown exception who in turn logs it as `Kind.Error`. Are you referring to the composition of message itself? That is done in the `DuplicateAnnotationIdException`. – Abbas May 15 '19 at 16:01
  • Normally you also pass to the messeger the element responsible for the error, and in the IDE it will mark that element with error marks, and in the compilation log when the developer double cleck the error line it will jump to the element. – Ahmad Bawaneh May 15 '19 at 20:10
  • @AhmadBawaneh I see, do you know if passing two elements instead of one also does the same work for both of them? As I've explained in the question I'm looking to throw error on duplicate annotations, which means there are two that need to show as error. – Abbas May 16 '19 at 04:52
  • And there might be three or even more, so why not to print a messager message for each. – Ahmad Bawaneh May 16 '19 at 06:48

1 Answers1

1

Stupid question apparently. I was looking in all the wrong places for the enclosing element, found this excellent Annotation Processor Tutorial which states:

Elements and TypeMirrors

...

You can think of it like a XML file you try to parse (or an abstract syntax tree in compiler construction)

And I realized the Element itself might contain all the information I need.

Just need to call variableElement.getEnclosingElement() to get the parent Element

From there on it was easy, you can get enclosing class name like so:

element.getEnclosingElement().asType().toString();

This however doesn't guarantee the enclosing element to be of TypeElement. So just use instanceof to ensure that it is.

Abbas
  • 3,529
  • 5
  • 36
  • 64