Spring framework has the very nice feature of supporting meta-annotations. I wanted to implement the same thing for jTransfo.
The idea is that you can write a custom annotation to replace one or more other annotations. For example in jTransfo, you can define a @ReadOnly annotation which replaces @MappedBy(readOnly = true).
You can define the custom annotation like this:
@MappedBy(readOnly = true)
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.ANNOTATION_TYPE})
@Documented
public @interface ReadOnly {
} |
Note that this annotation can itself also be used as meta-annotation thanks to the ANNOTATION_TYPE target.
To check whether an annotation exists on a field or method, you need to read through the chain. This can be done using this code:
/**
* Get the annotations of given type which are available on the annotated element. Considers both the annotations
* on the element and the meta-annotations (annotations on the annotations).
* The result is given in no specific order
*
* @param element annotated element
* @param annotation annotation to find
* @param <T> annotation type
* @return
*/
public <T extends Annotation> List<T> getAnnotationWithMeta(AnnotatedElement element, Class<T> annotation) {
return (List) Stream.of(element.getDeclaredAnnotations())
.flatMap(this::addMetaAnnotations)
.filter(a -> annotation.isAssignableFrom(a.annotationType()))
.collect(Collectors.toList());
}
private Stream<Annotation> addMetaAnnotations(Annotation a) {
Set<Annotation> res = new HashSet<>();
addMetaAnnotations(res, a);
return res.stream();
}
private void addMetaAnnotations(Set<Annotation> set, Annotation annotation) {
if (set.add(annotation)) { // set is needed or continues infinitely
for (Annotation meta : annotation.annotationType().getDeclaredAnnotations()) {
addMetaAnnotations(set, meta);
}
}
} |