Reputation: 182
I have not used java.lang.annotation much at all, but for the simplest way of metadata. I was wondering if there is a way to setup within an annotation or a group of annotations such that if conditional A is meet, then annotation A.B or B needs to be present.
For example
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Independent {
public boolean isDependent() default false;
public String[] dependency() default "";
}
So if isDependent
is set to true
then the developer/user would need to set dependency
with some values (ideally, what the dependency(-ies) is).
Is this possible to capture this meta-data here? If so, how to enforce it?
Restriction: Project has it's own "framework", so cannot import Spring Framework, which had some @Required annonations.
Did see this for Conditionally required property using data annotations but it's for C# not Java.
Thank you.
Upvotes: 0
Views: 1223
Reputation: 273
You can create "meta-annotations" to annotate your annotations.
For example this annotation can be used on an annotation to list the dependent annotations it requires.
package com.acme;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* If your annotation requires other annotations to be present as well,
* you can list them in your annotation declaration using this annotation.
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.ANNOTATION_TYPE})
public @interface RequiresAnnotation {
String[] annotations();
}
A sample processor for this @RequiresAnnotation meta-annotation:
@Processor("com.acme.RequiresAnnotation")
void processRequiresAnnotation(Target target, Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
RequiresAnnotation requiresAnnotation = target.getAnnotation()
.<RequiresAnnotation>getAnnotation(RequiresAnnotation.class);
List<String> requiredAnnotations = Arrays.asList(requiresAnnotation.annotations());
for (Element element : target.getElements()) {
debug(target.getAnnotation().getQualifiedName() + " -> " + RequiresAnnotation.class.getName()
+ " is found on [" + element.getSimpleName() + "]");
List<? extends AnnotationMirror> annos = element.getAnnotationMirrors();
List<String> foundAnnos = new LinkedList<String>();
for (AnnotationMirror anno : annos) {
String name = ((TypeElement) anno.getAnnotationType().asElement()).getQualifiedName().toString();
debug("found [" + name + "] annotation in [" + element + "]");
foundAnnos.add(name);
}
for (String required : requiredAnnotations) {
if (!foundAnnos.contains(required))
throw new RuntimeException("[" + ((TypeElement) element).getQualifiedName()
+ "] must be annotated with [" + required + "]");
}
}
}
...
void debug(Object message) {
this.processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE, message.toString());
}
To use it, assume you have a MyAnnotation
that requires another annotation called MyOtherAnnotation
.
package com.acme;
import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import com.acme.RequiresAnnotation;
@Documented
@Retention(RUNTIME)
@Target(TYPE)
@RequiresAnnotation(annotations = {"com.acme.MyOtherAnnotation" })
public @interface MyAnnotation {
}
package com.acme;
import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
@Documented
@Retention(RUNTIME)
@Target(TYPE)
public @interface MyOtherAnnotation {
}
Now if you annotate a class with MyAnnotation
, you will have to add MyOtherAnnotation
as well:
package com.acme;
@MyAnnotation
@MyOtherAnnotation
public class MyClass {
...
}
If you don't, you will get compilation error:
[ERROR] Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.1:compile (default-compile) on project annotations-tests: Fatal error compiling: java.lang.RuntimeException: java.lang.reflect.InvocationTargetException: [com.acme.MyClass] must be annotated with [com.acme.MyOtherAnnotation] -> [Help 1]
I have extracted the above info from a small meta-annotations library that i had written some time ago, but it is not available publicly unfortunately. I am sure there are some other libraries out there as well though..
Upvotes: 1