Java allows enum as values for annotation values. How can I define a kind of generic default enum value for an enum annotation value?
I have considered the following, but it won't compile:
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.FIELD)
public <T extends Enum<T>> #interface MyAnnotation<T> {
T defaultValue();
}
Is there a solution to this issue or not?
BOUNTY
Is does not seem like there is a direct solution to this Java corner case. So, I am starting a bounty to find the most elegant solution to this issue.
The ideal solution should ideally meet the following criteria:
One annotation reusable on all enums
Minimum effort/complexity to retrieve the default enum value as an enum from annotation instances
BEST SOLUTION SO FAR
By Dunes:
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.FIELD)
public #interface MyAnnotation {
// By not specifying default,
// we force the user to specify values
Class<? extends Enum<?>> enumClazz();
String defaultValue();
}
...
public enum MyEnumType {
A, B, D, Q;
}
...
// Usage
#MyAnnotation(enumClazz=MyEnumType.class, defaultValue="A");
private MyEnumType myEnumField;
Of course, we can't force the user to specify a valid default value at compile time. However, any annotation pre-processing can verify this with valueOf().
IMPROVEMENT
Arian provides an elegant solution to get rid of clazz in annotated fields:
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.FIELD)
public #interface MyAnnotation {
}
...
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.FIELD)
#MyAnnotation()
public #interface MyEnumAnnotation {
MyEnumType value(); // no default has user define default value
}
...
#MyEnumAnnotation(MyEnum.FOO)
private MyEnumType myValue;
The annotation processor should search for both MyEnumAnnotation on fields for the provided default value.
This requires the creation of one annotation type per enum type, but guarantees compile time checked type safety.
Not entirely sure what you mean when you say get a default value if said value wasn't provided in the constructor args, but not be caring about the generic type at runtime.
The following works, but is a bit of an ugly hack though.
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
public class Main {
#MyAnnotation(clazz = MyEnum.class, name = "A")
private MyEnum value;
public static v oid main(String[] args) {
new Main().printValue();
}
public void printValue() {
System.out.println(getValue());
}
public MyEnum getValue() {
if (value == null) {
value = getDefaultValue("value", MyEnum.class);
}
return value;
}
private <T extends Enum<?>> T getDefaultValue(String name, Class<T> clazz) {
try {
MyAnnotation annotation = Main.class.getDeclaredField(name)
.getAnnotation(MyAnnotation.class);
Method valueOf = clazz.getMethod("valueOf", String.class);
return clazz.cast(valueOf.invoke(this, annotation.value()));
} catch (SecurityException e) {
throw new IllegalStateException(e);
} catch (NoSuchFieldException e) {
throw new IllegalArgumentException(name, e);
} catch (IllegalAccessException e) {
throw new IllegalStateException(e);
} catch (NoSuchMethodException e) {
throw new IllegalStateException(e);
} catch (InvocationTargetException e) {
if (e.getCause() instanceof RuntimeException) {
throw (RuntimeException) e.getCause();
/* rethrow original runtime exception
* For instance, if value = "C" */
}
throw new IllegalStateException(e);
}
}
public enum MyEnum {
A, B;
}
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.FIELD)
public #interface MyAnnotation {
Class<? extends Enum<?>> clazz();
String name();
}
}
edit: I changed the getDefaultValue to work via the valueOf method of enums, thus giving a better error message if the value given is not reference instance of the enum.
I'm not sure what your use case is, so I have two answers:
Answer 1:
If you just want to write as little code as possible, here is my suggestion extending Dunes' answer:
public enum ImplicitType {
DO_NOT_USE;
}
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.FIELD)
public #interface MyAnnotation {
Class<? extends Enum<?>> clazz() default ImplicitType.class;
String value();
}
#MyAnnotation("A");
private MyEnumType myEnumField;
When clazz is ImplicitType.class, use the fields type as enum class.
Answer 2:
If you want to do some framework magic and want to maintain compiler checked type safety, you can do something like this:
/** Marks annotation types that provide MyRelevantData */
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.ANNOTATION_TYPE)
public #interface MyAnnotation {
}
And in the client code, you would have
/** Provides MyRelevantData for TheFramework */
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.FIELD)
#MyAnnotation
public #interface MyEnumAnnotation {
MyEnumType value(); // default MyEnumType.FOO;
}
#MyEnumAnnotation(MyEnum.FOO)
private MyEnumType myValue;
In this case you would scan the field for annotations which again are annotated with MyAnnotation. You will have to access the value via reflection on the annotation object, though. Seems like this approach is more complex on the framework side.
Simply put, you can not do that. Enums can not easily be used as generic types; with perhaps one exception, which is that Enums can actually implement interfaces which allows somewhat dynamic usage. But that won't work with annotations as set of types that can be used is strictly limited.
Your generic type syntax is a little off. It should be:
public #interface MyAnnotation<T extends Enum<T>> {...
but compiler gives error:
Syntax error, annotation declaration cannot have type parameters
Nice idea. Looks like it's not supported.
Frameworks using annotations can really profit from using apt. It's a preprocesor contained in javac, which will let you analyse declarations and their annotations (but not local declarations inside methods).
For your problem you would need to write an AnnotationProcessor (a class used as starting point for preprocessing) to analyse the annotation by using the Mirror API. Actually Dunes' annotation is pretty close to what is needed here. Too bad enum names aren't constant expressions, otherwise Dunes' solution would be pretty nice.
#Retention(RetentionPolicy.SOURCE)
#Target(ElementType.FIELD)
public #interface MyAnnotation {
Class<? extends Enum<?>> clazz();
String name() default "";
}
And here's an example enum: enum MyEnum { FOO, BAR, BAZ, ; }
When using a modern IDE, you can display errors on directly on the annotation element (or the annotation's value), if the name isn't a valid enum constant. You can even provide auto complete hints, so when a user writes #MyAnnotation(clazz = MyEnum.class, name = "B") and hits the hotkeys for auto-completion after writing B, you can provide him a list to choose from, containing all the constants starting with B: BAR and BAZ.
My suggestion to implement the default value is to create a marker annotation to declare an enum constant as default value for that enum. The user would still need to provide the enum type, but could omit the name. There are probably other ways though, to make a value the default one.
Here's a tutorial about apt and here the AbstractProcessor which should be extended to override the getCompletions method.
My suggestion is similar to kapep's suggestion. The difference is that I propose using the annotation processor for code creation.
A simple example would be if you intended to use this only for enums that you yourself wrote. Annotate the enum with a special enum. The annotation processor will then generate a new annotation just for that enum.
If you work with a lot of enums that you did not write, then you could implement some name mapping scheme: enum name -> annotation name. Then when the annotation processor encountered one of these enums in your code, it would generate the appropriate annotation automatically.
You asked for:
One annotation reusable on all enums ... technically no, but I think the effect is the same.
Minimum effort/complexity to retrieve the default enum value as an enum from annotation instances ... you can retrieve the default enum value without any special processing
I had a similar need and came up with the following pretty straightforward solution:
The actual #Default interface:
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.FIELD)
public #interface Default {}
Usage:
public enum Foo {
A,
#Default B,
C;
}
Finding the default:
public abstract class EnumHelpers {
public static <T extends Enum<?>> T defaultEnum(Class<T> clazz) {
Map<String, T> byName = Arrays.asList(clazz.getEnumConstants()).stream()
.collect(Collectors.toMap(ec -> ec.name(), ec -> ec));
return Arrays.asList(clazz.getFields()).stream()
.filter(f -> f.getAnnotation(Default.class) != null)
.map(f -> byName.get(f.getName()))
.findFirst()
.orElse(clazz.getEnumConstants()[0]);
}
}
I've also played around with returning an Optional<T> instead of defaulting to the first Enum constant declared in the class.
This would, of course, be a class-wide default declaration, but that matches what I need. YMMV :)
Related
I have the following annotation:
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.METHOD)
public #interface IdentifiableMethod {
String id() default "";
}
I will have to loop through a list of annotations and for each of them, perform a annotation.id().
Hence, I would have liked to use this "base" annotation to make it extended by other annotations:
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.METHOD)
public #interface SpecificMethod extends IdentifiableMethod{
//invalid: annotation cannot have extends list
}
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.METHOD)
public #interface OtherSpecificMethod extends IdentifiableMethod{
//invalid: annotation cannot have extends list
}
... and then generically access the .id() method in a loop by getting in parameter a List<A extends IdentifiableMethod>, so that the compiler always makes me access that method.
However, I've just found out that in the Java specification, all Java annotations extend natively the interface Annotation and they cannot have an extends list [Source: this Stack Overflow question and its answers].
Is there any way to reach something similar?
Just to clarify the need, I need to get all the methods of all the classes within my package by reflection and scan for these annotations. They may be different (they may have more or less properties, different usages etc.), but they all need to have a String id field:
List<Class<?>> classes = getClasses(packageName);
for (Class<?> clazz : classes) {
for (Method method : clazz.getMethods()) {
for (Class<A> annotation : annotations) { //<-- annotations is a Collection<Class<A extends Annotation>>
if (method.isAnnotationPresent(annotation)) {
A targetAnnotation = method.getAnnotation(annotation);
String id = targetAnnotation.id(); //<-- this is not valid because A extends Annotation, not IdentifiableMethod
//rest of code (not relevant)
}
}
}
}
P.s. I already did this but I was looking for something cleaner:
String id = targetAnnotation.getClass().getMethod("id").invoke(targetAnnotation).toString();
Why do I get java.lang.annotation.AnnotationFormatError: Attempt to create proxy for a non-annotation type. when I'm trying to get the annotation list from the field?
My annotation:
#Target({ ElementType.FIELD })
#Retention(RetentionPolicy.RUNTIME)
private #interface TypicalAnnotation
{
int size();
}
Usage:
public static class MockAnnotatedClass
{
#TypicalAnnotation(size = 3)
public Integer number = 2;
}
Call:
ReflectionUtils.getAllFields(clazz1).getAnnotations() <- got the exception.
I had the same problem when I used Jdeveloper 12.2.1.0.0 and integrated Weblogic with enabled Fast Swap feature.
if you look at the source code (github) you will see that an exception is thrown if the type is either not an annotation or implements more than one interface or does not implement the java.lang.annotation.Annotation interface:
AnnotationInvocationHandler(Class<? extends Annotation> type, Map<String, Object> memberValues) {
Class<?>[] superInterfaces = type.getInterfaces();
if (!type.isAnnotation() ||
superInterfaces.length != 1 ||
superInterfaces[0] != java.lang.annotation.Annotation.class)
throw new AnnotationFormatError("Attempt to create proxy for a non-annotation type.");
this.type = type;
this.memberValues = memberValues;
}
Using this code, I checked which interfaces my annotation implements:
Annotation declaration:
#Target(ElementType.METHOD)
#Retention(RetentionPolicy.RUNTIME)
public #interface MyAnnotation {}
Checking of type:
Class<?> type = MyAnnotation.class;
System.out.println("type.isAnnotation() " + type.isAnnotation());
System.out.println("type.getInterfaces().length " + type.getInterfaces().length);
for (Class<?> superType : type.getInterfaces()) {
System.out.println("type.getInterfaces()[i] " + superType);
}
Output:
type.isAnnotation() true
type.getInterfaces().length 2
type.getInterfaces()[i] interface java.lang.annotation.Annotation
type.getInterfaces()[i] interface com.bea.wls.redef.Redefinable // << ??????
As it turned out, another interface is added to the annotation.
I did not find information specifically about com.bea.wls.redef.Redefinable. After a bit of searching, I realized that the package com.bea.wls.redef is used by the Fast Swap function.
I see 2 solutions:
Disable Fast Swap function.
Move all custom annotations to separate jar. It is what I did.
Perhaps there is a way to turn off wrapping for annotations. But I did not find it.
I would like to use goolge/guice inject a value based on a class i provide with the annotation.
AutoConfig annotation
#BindingAnnotation
#Retention(RetentionPolicy.RUNTIME)
#Target({ ElementType.PARAMETER, ElementType.FIELD })
public #interface AutoConfig {
// default null not possible
Class<? extends Provider<? extends ConfigLoader<?>>> provider() default XMLAutoConfigProvider.class;
}
This is my annotation which allows configuring the type of config, that should be used for the annotated fields.
Usecase:
#AutoConfig()
ConfigLoader<?> defaultConfig;
#AutoConfig(provider = JsonConfigProvider)
ConfigLoader<?> jsonConfig;
I want to have two configs, one default/xml one and a json one. They will probably never occur in the same class at the same time. But i don't know when the one or the other is used. I used the approach with a class because they are provided by some dependencies/libs and this annotation will be used for some (plugable) submodules.
MyGuiceModule
public class MyGuiceModule extends AbstractModule {
#Override
protected void configure() {
bind(new TypeLiteral<ConfigLoader<?>>() {})
.annotatedWith(AutoConfig.class)
.toProvider(autoConfig.provider());
}
}
This the critical part, i just cannot imagine how to implement it.
So basically i just want to use the provider class specified in the annotation.
Its not necessary to use the provider class here too. Because autoConfig.provider().newInstance() is basically all i need. (I need to use a setter on the new instance but thats all i want to do at this place)
To sum it up all i really want to do is push the annotation (or its values to the provider) either using the get(AutoConfig autoConfig) or in the constructor.
Currently i only use the constructor to inject the configFile value i want to set on the newly generated config instance.
If you know that #AutoConfig(provider = JsonConfigProvider) ConfigLoader<?> jsonConfig is going to return you exactly the results of jsonConfigProvider.get(), and JsonConfigProvider obviously has a public parameterless constructor for newInstance to work, why wouldn't you just ask for a JsonConfigProvider in the first place?
Fundamentally Guice is just a Map<Key, Provider> with fancy wrapping. The bad news is that this makes variable bindings like "bind Foo<T> for all T" impossible to express concisely, and that includes your "bind #Annotation(T) Foo for all T". The good news is that you still have two options.
Bind each provider separately
Though you can't inspect annotations during provision (or tell Guice to do so for you), Guice will compare annotations using their equals methods if you bind an annotation instance rather than an annotation class (the way you would with Names.named("some-name")). This means that you can bind a ConfigLoader<?> with each expected annotation in a Module. Of course, this also means you'll have to have a list of possible ConfigLoader Providers available at configuration time, but they have to be compile-time constants anyway if you're using them as annotation parameters.
This solution works with constructor injection as well, but for fields you'll need both #Inject and #AutoConfig(...), and AutoConfig will need to keep its #BindingAnnotation meta-annotation.
To do this, you're going to have to write an implementation of your annotation, the way Guice does with NamedImpl. Note that the implementations of equals and hashCode must match the ones Java provides in java.lang.Annotation. Then it's just a matter of (redundantly) binding like this:
for(Class<ConfigLoader<?>> clazz : loaders) {
bind(ConfigLoader.class).annotatedWith(new AutoConfigImpl(clazz))
.toProvider(clazz);
}
The definition of equals is up to you, which means you can (and should) bind #AutoConfig(ConfigEnum.JSON) and keep the Guice bindings in your modules rather than specifying your requested implementation all over your codebase.
Use custom injections
You can also use custom injections to search your injected types for custom annotations like #AutoConfig. At this point, you'd be using Guice as a platform to interpret #AutoConfig instead of #Inject, which means that constructor injection won't work but that you can control your injection based on the injected instance, field name, field annotation, annotation parameters, or any combination thereof. If you choose this style, you can drop #BindingAnnotation from AutoConfig.
Use the example in the wiki article linked above as your template, but at minimum you'll need to:
Use bindListener on Binder or AbstractModule to match types that need this custom injection.
In the TypeListener you bind, search injected types for #AutoConfig-annotated fields, and if they have any matching methods then bind those matching methods to a MembersInjector or InjectionListener. You'll probably want to tease the class literal out of the annotation instance here, and pass in the Field and Class as constructor arguments to the MembersInjector/InjectionListener.
In the MembersInjector or InjectionListener you write, instantiate the provider and set the field to the instance the provider provides.
This is a very powerful feature, which would futher allow you to--for instance--automatically provide the configuration based on which instance you're injecting into or based on the name of the field. However, use it carefully and document it heavily, because it may be counter-intuitive to your coworkers that Guice is providing for an annotation other than #Inject. Also bear in mind that this won't work for constructor injection, so refactoring from field injection to constructor injection will cause Guice to complain that it's missing a required binding to instantiate the class.
I had a similar problem. I wanted to use a custom annotation that receives a enum param to choose the implementation. After a lot of research, debug and testing, I came to the following solution:
//enum to define authentication types
public enum AuthType {
Ldap, Saml
}
//custom annotation to be used in injection
#Target({ ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD })
#BindingAnnotation
public #interface Auth {
AuthType value();
}
//defintion of authenticator
public interface Authenticator {
public void doSomehting();
}
//Authenticator implementations
public class LdapAuthenticator implements Authenticator {
#Override
public void doSomehting() {
// doing ldap stuff
}
}
public class SamlAuthenticator implements Authenticator {
#Override
public void doSomehting() {
// doing saml stuff
}
}
public class MyModule extends AbstractModule {
// annotate fields to bind to implementations
private #Auth(AuthType.Ldap) Authenticator ldap;
private #Auth(AuthType.Saml) Authenticator saml;
#Override
protected void configure() {
//bind the implementation to the annotation from field
bindAnnotated("ldap", LdapAuthenticator.class);
bindAnnotated("saml", SamlAuthenticator.class);
}
private void bindAnnotated(String fieldName, Class<? extends Authenticator> implementation) {
try {
//get the annotation from fields, then bind it to implementation
Annotation ann = MyModule.class.getDeclaredField(fieldName).getAnnotation(Auth.class);
bind(Authenticator.class).annotatedWith(ann).to(implementation);
} catch (NoSuchFieldException | SecurityException e) {
throw new RuntimeException(e);
}
}
}
//usage: add #Auth(<AuthType>) to the dependency
public class ClientClass {
private Authenticator authenticator;
#Inject
public ClientClass(#Auth(AuthType.Ldap) Authenticator authenticator) {
this.authenticator = authenticator;
}
}
Check the documentation of Binder
I tested the Jeff Bowman solution, but it apparently works only binding to providers
As a BindingAnnotations#binding-annotations-with-attributes states equals() and hashCode() should be properly implemented. So given that there is MyAnnotation
#Qualifier
#Retention(RetentionPolicy.RUNTIME)
#Target({ElementType.FIELD, ElementType.PARAMETER})
public #interface MyAnnotation {
SomeEnum value() default SomeEnum.A;
}
which is used to specify SomeInterface implementation(SomeDefault and SomeOther), SomeModule class could look like
public class SomeModule extends AbstractModule {
#Override
protected void configure() {
bind(Key.get(SomeInterface.class, createAnnotationClass(A))).to(SomeDefault.class);
// more common binding expresion
bind(SomeInterface.class).annotatedWith(createAnnotationClass(B)).to(SomeDefault.class);
}
private Annotation createAnnotationClass(SomeEnum someEnum) {
return new MyAnnotation() {
#Override
public SomeEnum value() {
return someEnum;
}
#Override
public Class<? extends Annotation> annotationType() {
return MyAnnotation.class;
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
MyAnnotationCl myAnnoCl = (MyAnnotationCl) o;
return A == myAnnoCl.getValue();
}
#Override
public int hashCode() {
// from java annotation documentation
return (127 * "value".hashCode()) ^ value().hashCode();
}
};
}
}
Then annotation could be used as follows:
public class DoSomethingWithSomething {
private final SomeInterface someImplementation;
#Inject
public DoSomethingWithSomething(
#MyAnnotation SomeInterface someDefault
// #MyAnnotation(A) SomeInterface someDefault
// #MyAnnotation(B) SomeInterface someOther
) {
this.someImplementation = someDefault;
}
}
We have an annotation #Accepts:
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.FIELD)
public #interface Accepts {
Class[] value();
}
It takes a list of Classes. These are later used to validate in a DSL that a field was passed an instance of the classes listed as acceptable.
Some examples of this annotation in use:
public enum PropertyName {
#Accepts({Integer.class})
xCoordinate,
#Accepts({Integer.class})
yCoordinate,
#Accepts({Boolean.class})
showPermission
#Accepts({String.class, FieldScript.class, List.class})
onClick
/* And So On*/
}
I am adding a new item to this enum called 'value' and it can accept a String or a PropertyResolver. PropertyResolver is an interface defined as below:
public interface PropertyResolver<T> {
public T getValue(TagContext tagContext);
}
I don't know how to do a .class on PropertyResolver to pass on to #Accepts. Is it possible to do so?
Thanks.
You will have to do PropertyResolver.class. There will only one Class instance that represents the the class (raw-version).
No such things as PropertyResolver<T>.class or PropertyResolver<Integer>.class exist.
Always, keep in mind that in Java, generics is compile time only feature.
I want to create a custom annotation (using Java) which would accept other annotations as parameter, something like:
public #interface ExclusiveOr {
Annotation[] value();
}
But this causes compiler error "invalid type for annotation member".
Object[] also doesn't work.
Is there a way to do what I want?
The error is produced because you can't use interfaces as annotation values (change it to Comparable and you'll get the same error). From the JLS:
It is a compile-time error if the return type of a method declared in an annotation type is any type other than one of the following: one of the primitive types, String, Class and any invocation of Class, an enum type, an annotation type, or an array of one of the preceding types. It is also a compile-time error if any method declared in an annotation type has a signature that is override-equivalent to that of any public or protected method declared in class Object or in the interface annotation.Annotation.
I'm afraid I don't know of a good workaround, but now at least you know why you get the error.
Depending on the reason why you would want to specify other annotations there are multiple solutions:
An array of instances of a single annotation type
Probably not what you meant in your question, but if you want to specify multiple instances of a single annotation type it's certainly possible:
public #interface Test {
SomeAnnotation[] value();
}
An array of annotation types instead of instances
If you do not need to specify any parameters on the individual annotations you can just user their class objects instead of instances.
public #interface Test {
Class<? extends Annotation>[] value();
}
But an enum would of course also do the trick in most situations.
Use multiple arrays
If the set of possible annotation types you want to use is limited, you can create a separate parameter for each one.
public #interface Test {
SomeAnnotation[] somes() default { };
ThisAnnotation[] thiss() default { };
ThatAnnotation[] thats() default { };
}
Giving a default value to each member makes it possible to only specify arrays for the types you need.
You can do:
Class<? extends Annotation>[] value();
Not sure if that helps, but . . .
I myself hereby propose a workaround for the given problem:
Well, what I wanted to make possible was something like that:
#Contract({
#ExclusiveOr({
#IsType(IAtomicType.class),
#Or({
#IsType(IListType.class),
#IsType(ISetType.class)
})
})
})
Proposed workaround:
Define a class with parameter-less constructor (which will be called by your own annotation processor later) in following way:
final class MyContract extends Contract{
// parameter-less ctor will be handeled by annotation processor
public MyContract(){
super(
new ExclusiveOr(
new IsType(IAtomicType.class),
new Or(
new IsType(IListType.class),
new IsType(ISetType.class)
)
)
);
}
}
usage:
#Contract(MyContract.class)
class MyClass{
// ...
}
I just ran into this exact problem, but (inspired by #ivan_ivanovich_ivanoff) I have discovered a way to specify a bundle of any combination of Annotations as an annotation member: use a prototype / template class.
In this example I define a WhereOr (i.e. a "where clause" for my model annotation) which I need to contain arbitrary Spring meta-annotations (like #Qualifier meta-annotations).
The minor (?) defect in this is the forced dereferencing that separates the implementation of the where clause with the concrete type that it describes.
#Target({})
#Retention(RetentionPolicy.RUNTIME)
public #interface WhereOr {
Class<?>[] value() default {};
}
#Target({ElementType.TYPE, ElementType.ANNOTATION_TYPE})
#Retention(RetentionPolicy.RUNTIME)
public #interface JsonModel {
Class<?> value();
WhereOr where() default #WhereOr;
}
public class Prototypes {
#Qualifier("myContext")
#PreAuthorize("hasRole('ROLE_ADMINISTRATOR')")
public static class ExampleAnd {
}
}
#JsonModel(
value = MusicLibrary.class,
where = #WhereOr(Prototypes.ExampleAnd.class)
)
public interface JsonMusicLibrary {
#JsonIgnore
int getMajorVersion();
// ...
}
I will programmatically extract the possible valid configurations from the "where clause" annotation. In this case I also use the prototypes class as a logical AND grouping and the array of classes as the logical OR.