1

How to extract the value Class[] value() in the annotation

package com.example;

public @interface ExampleAnnotation {
    Class[] value();
}

Without the annotation begin in the runtime of the Annotation Processor.

LSafer
  • 344
  • 3
  • 7

3 Answers3

1

I use the following utility i built for my own annotation processors:

public List<TypeMirror> getClassArrayValueFromAnnotation(Element element, Class<? extends Annotation> annotation, String paramName) {
    Elements elements = this.processingEnv.getElementUtils();
    Types types = this.processingEnv.getTypeUtils();

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

    for (AnnotationMirror am : element.getAnnotationMirrors()) {
        if (types.isSameType(am.getAnnotationType(), elements.getTypeElement(annotation.getCanonicalName()).asType())) {
            for (Map.Entry<? extends ExecutableElement, ? extends AnnotationValue> entry : am.getElementValues().entrySet()) {
                if (paramName.equals(entry.getKey().getSimpleName().toString())) {
                    List<AnnotationValue> classesTypes = (List<AnnotationValue>) entry.getValue().getValue();
                    Iterator<? extends AnnotationValue> iterator = classesTypes.iterator();

                    while (iterator.hasNext()) {
                        AnnotationValue next = iterator.next();
                        values.add((TypeMirror) next.getValue());
                    }
                }
            }
        }
    }
    return values;
}
ktul
  • 183
  • 9
Ahmad Bawaneh
  • 1,014
  • 8
  • 23
  • I assume that `types` is the result of invoking `this.processingEnv.getTypeUtils()` is that right? – LSafer Dec 17 '20 at 13:55
1

In many cases, you can get the TypeMirror from the exception that is thrown when you try to access a Class or Class[] parameter (see this answer).

When you access a class parameter, a MirroredTypeException is thrown, while when you access a class array parameter, a MirroredTypesException is thrown. Both provide the TypeMirrors.

In the following example, the methods mirror and mirrorAll wrap the verbose try-catch pattern and provide the respective TypeMirrors. They accept the respective getter via a method reference.

public @interface ExampleAnnotation {
    Class<?> type();
    Class<?>[] types();
}

private void process(TypeElement typeElement){
    ExampleAnnotation annotation = typeElement.getAnnotation(ExampleAnnotation.class);
    TypeMirror type = mirror(annotation::type);
    List<? extends TypeMirror> types = mirrorAll(annotation::types);
}

public static TypeMirror mirror(Supplier<Class<?>> classValue) {
    try {
        var ignored = classValue.get();
        throw new IllegalStateException("Expected a MirroredTypeException to be thrown but got " + ignored);
    } catch (MirroredTypeException e) {
        return e.getTypeMirror();
    }
}

public static List<? extends TypeMirror> mirrorAll(Supplier<Class<?>[]> classValues) {
    try {
        var ignored = classValues.get();
        throw new IllegalStateException("Expected a MirroredTypesException to be thrown but got " + ignored);
    } catch (MirroredTypesException e) {
        return e.getTypeMirrors();
    }
}
ktul
  • 183
  • 9
  • nice! I think is the cleanest, but you might need to be careful if you are mixing custom class types with native java types like String. I've read elsewhere that Java actually gives you the real Class type for native types, but just a TypeMirror for custom types. – takanuva15 Aug 10 '23 at 15:29
0

This is my best way. I use the java stream API for more simplicity.

Write this in your processor class:

    public static final String TARGET = "com.example.ExampleAnnotation";

    @Override
    public boolean process(Set<? extends TypeElement> types, RoundEnvironment environment) {
        //process once!
        if (!environment.processingOver())
            //For us to not depend on a class in this runtime
            //try to find the TypeElement of it
            types.stream()
                    .filter(type -> type.getQualifiedName().contentEquals(TARGET))
                    .findAny()
                    .ifPresent(type ->
                            //it exists!
                            //let us see elements annotated with it!
                            environment.getElementsAnnotatedWith(type)
                                    .forEach(element ->
                                            //got an element!
                                            //let us iterate over the annotation mirrors it has got
                                            //then search for the annotation with the targeted type
                                            element.getAnnotationMirrors()
                                                    .stream()
                                                    .filter(annotation -> this.processingEnv.getTypeUtils().isSameType(type.asType(), annotation.getAnnotationType()))
                                                    .findAny()
                                                    .ifPresent(annotation -> {
                                                        //bingo!
                                                        //lets get its values
                                                        Map<? extends ExecutableElement, ? extends AnnotationValue> values = annotation.getElementValues();

                                                        //lets find the annotation value `Class[] value()`
                                                        //any inappropriate value will be simply IGNORED (do not die strategy)
                                                        //this block trusts the javax documentation only!
                                                        //see javax.lang.model.element.AnnotationValue
                                                        List<TypeMirror> value = values.entrySet()
                                                                .stream()
                                                                .filter(entry -> entry.getKey().getSimpleName().contentEquals("value"))
                                                                .findAny()
                                                                .map(entry -> entry.getValue().getValue())
                                                                .filter(List.class::isInstance)
                                                                .<List<AnnotationValue>>map(List.class::cast)
                                                                .map(list ->
                                                                        list.stream()
                                                                                .map(AnnotationValue::getValue)
                                                                                .filter(TypeMirror.class::isInstance)
                                                                                .map(TypeMirror.class::cast)
                                                                                .collect(Collectors.toList())
                                                                )
                                                                .orElseGet(Collections::emptyList);

                                                        //Operate below ---------------------------------
                                                        //Operate above --------------------------------
                                                    })
                                    )
                    );

        return false;
    }
LSafer
  • 344
  • 3
  • 7