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.

3 Answers 3

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;
}
Sign up to request clarification or add additional context in comments.

1 Comment

I assume that types is the result of invoking this.processingEnv.getTypeUtils() is that right?
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();
    }
}

1 Comment

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.
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;
    }

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.