5

I am trying to generate some code using Java annotation processing tools, I have nested annotations where the parent annotation value is an array of the child annotation, and the child annotation value is an array of classes.

Annotations:

public @interface ParentAnnotation {
    ChildAnnotation[] value();
}
public @interface ChildAnnotation {
    Class<?>[] value();
}

Usage:

@ParentAnnotation(
{
      @ChildAnnotation({Foo.class, Bar.class}),
      @ChildAnnotation({Goo.class, Doo.class})
})
public class Sample{
}

calling value() on the annotation with my Processor subtype fails with this exception:

Error:java: error while creating source file javax.lang.model.type.MirroredTypeException: Attempt to access Class object for TypeMirror org.dominokit.samples.layout.shared.extension.LayoutEvent
    at com.sun.tools.javac.model.AnnotationProxyMaker$MirroredTypeExceptionProxy.generateException(AnnotationProxyMaker.java:308)
    at sun.reflect.annotation.AnnotationInvocationHandler.invoke(AnnotationInvocationHandler.java:84)
    at com.sun.proxy.$Proxy28.value(Unknown Source)
    at org.dominokit.domino.apt.client.processors.module.client.presenters.PresenterProxySourceWriter.generateFireActivationEvent(PresenterProxySourceWriter.java:238)
    at org.dominokit.domino.apt.client.processors.module.client.presenters.PresenterProxySourceWriter.asTypeBuilder(PresenterProxySourceWriter.java:64)
    at org.dominokit.domino.apt.client.processors.module.client.presenters.PresenterProxyProcessingStep.generateProxy(PresenterProxyProcessingStep.java:66)
    at org.dominokit.domino.apt.client.processors.module.client.presenters.PresenterProxyProcessingStep.process(PresenterProxyProcessingStep.java:53)
    at org.dominokit.domino.apt.client.processors.module.client.presenters.PresenterProxyProcessor.process(PresenterProxyProcessor.java:61)
    at com.sun.tools.javac.processing.JavacProcessingEnvironment.callProcessor(JavacProcessingEnvironment.java:794)
    at com.sun.tools.javac.processing.JavacProcessingEnvironment.discoverAndRunProcs(JavacProcessingEnvironment.java:705)
    at com.sun.tools.javac.processing.JavacProcessingEnvironment.access$1800(JavacProcessingEnvironment.java:91)
    at com.sun.tools.javac.processing.JavacProcessingEnvironment$Round.run(JavacProcessingEnvironment.java:1035)
    at com.sun.tools.javac.processing.JavacProcessingEnvironment.doProcessing(JavacProcessingEnvironment.java:1176)
    at com.sun.tools.javac.main.JavaCompiler.processAnnotations(JavaCompiler.java:1170)
    at com.sun.tools.javac.main.JavaCompiler.compile(JavaCompiler.java:856)
    at com.sun.tools.javac.main.Main.compile(Main.java:523)
    at com.sun.tools.javac.api.JavacTaskImpl.doCall(JavacTaskImpl.java:129)
    at com.sun.tools.javac.api.JavacTaskImpl.call(JavacTaskImpl.java:138)
    at org.jetbrains.jps.javac.JavacMain.compile(JavacMain.java:196)
    at org.jetbrains.jps.incremental.java.JavaBuilder.compileJava(JavaBuilder.java:448)
    at org.jetbrains.jps.incremental.java.JavaBuilder.compile(JavaBuilder.java:318)
    at org.jetbrains.jps.incremental.java.JavaBuilder.doBuild(JavaBuilder.java:243)
    at org.jetbrains.jps.incremental.java.JavaBuilder.build(JavaBuilder.java:201)
    at org.jetbrains.jps.incremental.IncProjectBuilder.runModuleLevelBuilders(IncProjectBuilder.java:1327)
    at org.jetbrains.jps.incremental.IncProjectBuilder.runBuildersForChunk(IncProjectBuilder.java:1007)
    at org.jetbrains.jps.incremental.IncProjectBuilder.buildTargetsChunk(IncProjectBuilder.java:1074)
    at org.jetbrains.jps.incremental.IncProjectBuilder.buildChunkIfAffected(IncProjectBuilder.java:968)
    at org.jetbrains.jps.incremental.IncProjectBuilder.buildChunks(IncProjectBuilder.java:797)
    at org.jetbrains.jps.incremental.IncProjectBuilder.runBuild(IncProjectBuilder.java:375)
    at org.jetbrains.jps.incremental.IncProjectBuilder.build(IncProjectBuilder.java:178)
    at org.jetbrains.jps.cmdline.BuildRunner.runBuild(BuildRunner.java:138)
    at org.jetbrains.jps.cmdline.BuildSession.runBuild(BuildSession.java:302)
    at org.jetbrains.jps.cmdline.BuildSession.run(BuildSession.java:135)
    at org.jetbrains.jps.cmdline.BuildMain$MyMessageHandler.lambda$channelRead0$0(BuildMain.java:229)
    at org.jetbrains.jps.service.impl.SharedThreadPoolImpl.lambda$executeOnPooledThread$0(SharedThreadPoolImpl.java:42)
    at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
    at java.util.concurrent.FutureTask.run(FutureTask.java:266)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
    at java.lang.Thread.run(Thread.java:748)

so how i can read these classes from the annotation as a List<TypeMirror> ?

Colin Alworth
  • 17,801
  • 2
  • 26
  • 39
Ahmad Bawaneh
  • 1,014
  • 8
  • 23

1 Answers1

4

After lots of debugging and trying one thing at a time i found the solution for this, the idea was in how to use the AnnotationMirror to obtain AnnotationValue, there was some sort of confusion at some point where the expected return types to be AnnotationValue[] was actually List<AnnotationValue>, below is a utility method i wrote to read the classes as a List<List<TypeMirror>> :

private List<List<TypeMirror>> getNestedAnnotationClassesValue(Element element){

        List<List<TypeMirror>> classesInAnntation = new ArrayList<>();
        if (nonNull(element.getAnnotation(ParentAnnotation.class))) {
            List<? extends AnnotationMirror> annotations = element.getAnnotationMirrors();
            for (AnnotationMirror annotationMirror : annotations) {
                if (types.isSameType(annotationMirror.getAnnotationType(), elements.getTypeElement(ParentAnnotation.class.getName()).asType())) {
                    Map<? extends ExecutableElement, ? extends AnnotationValue> elementValues = annotationMirror.getElementValues();

                    elementValues.values()
                            .stream()
                            .findFirst()
                            .ifPresent(annotationValue -> {
                                List<AnnotationMirror> childAnnotations = (List<AnnotationMirror>) annotationValue.getValue();
                                childAnnotations.stream()
                                        .forEach(childAnnotationMirror -> {
                                            Collection<? extends AnnotationValue> values = childAnnotationMirror.getElementValues()
                                                    .values();
                                            AnnotationValue childAnnotationValue = values.stream().findFirst().get();
                                            List<AnnotationValue> classesInNestedAnnotation = (List<AnnotationValue>) childAnnotationValue.getValue();
                                            Iterator<? extends AnnotationValue> iterator = classesInNestedAnnotation.iterator();

                                            List<TypeMirror> typeMirrorsInNestedAnnotation = new ArrayList<>();

                                            while (iterator.hasNext()) {
                                                AnnotationValue next = iterator.next();
                                                typeMirrorsInNestedAnnotation.add((TypeMirror) next.getValue());
                                            }

                                            if (!typeMirrorsInNestedAnnotation.isEmpty()) {
                                                classesInAnntation.add(typeMirrorsInNestedAnnotation);
                                            }
                                        });
                            });
                }
            }
        }
        return classesInAnntation;
    }
Ahmad Bawaneh
  • 1,014
  • 8
  • 23
  • i got `lambada expression not supported language level 7` syntax error . i am in the same situation except i have `Class[] classes();` in my annotation parameter . could you help me with it? it's really confusing! – Alireza Sharifi Jul 17 '19 at 04:21
  • @lvl4fi4 You need to compile with a langauge level 8 or above , or you can replace the lambdas in my code with standard code. – Ahmad Bawaneh Jul 17 '19 at 06:05
  • never mind i fount more simple solution : https://stackoverflow.com/a/52793839/2979171 thanks anyway. – Alireza Sharifi Jul 17 '19 at 06:09
  • I would not prefer to depend on an exception to solve this problem, things might behave in a different way with different environments. – Ahmad Bawaneh Jul 17 '19 at 06:20
  • well, it's suppose to work in Source ,not runtime so, i'm fine with it for now. – Alireza Sharifi Jul 17 '19 at 06:25
  • Yes i mean environment like Intellij vs Eclipse or different java versions. i am not saying it is wrong, but might need more testing to confirm it works. – Ahmad Bawaneh Jul 17 '19 at 07:32
  • I really appreciate your concerns , but it's kind of personal use not commercial but unless it breaks my code then i have to fix it .I'm using it to generate source code of my project which is `mvp` architect and so far it works for all of my classes .However if it breaks my source code, I will consider using your method which I'm pretty sure is the right way. – Alireza Sharifi Jul 17 '19 at 08:58