1

I wonder if there is a possiblity to pass dynamically values to an annotation attribute.

I know that annotation are not designed to be modified but I'm using Hibernate Filters and condition to be put are not static in my case.

I think that the only solution is to use librairies whose aim is to read and modify byte code such as Javassist or ASM but it would be too much better if there is another solution.

ps: The difficulty in my case is that I should modify annotations (attribute's value) but librairies I mentioned above allow to create not to edit that's why I'm wondering for another solution

Thanks in advance

javaxiss
  • 680
  • 3
  • 13
  • 34
  • You cannot. I don't see why you would need to. If you know what value you would've passed to it is, just use it to act on the annotated field. – Sotirios Delimanolis May 07 '13 at 16:03
  • What about passing a class argument and creating an instance of it? – Will May 07 '13 at 16:05
  • @SotiriosDelimanolis: the value to put depends on what user checks, initially it's unknown ;-) – javaxiss May 07 '13 at 16:10
  • @WillP: What do you mean by that ? – javaxiss May 07 '13 at 16:11
  • @Issam, my idea was to use a class parameter instead of a value, but i doubt it will integrate with Filters. Something like `@Valid(MyValidator.class)`. Are you on JavaEE or Spring? You may try something like http://stackoverflow.com/questions/12568385/passing-dynamic-parameters-to-an-annotation – Will May 07 '13 at 16:19
  • @WillP I'm using JEE, and I suppose that what I need does not depend on what I have as Frameworks. In fact, I wonder if I could change the filter's condition dynamically (the condition is the annotation parameter value). The condition will be fixed by users' choices from UI... ps: sorry for my late reply – javaxiss May 17 '13 at 15:44

2 Answers2

2

I don't know if it integrates nicely with your frameworks, but i would like to suggest the following:

  • Create an annotation which receives a Class that implements the validation rule
  • Create an interface which the annotation can receive
  • Create an implementation for the interface which has the logic for your rule
  • Add the annotations to your model class
  • Create an annotation processor which applies the validation for each annotated field

I wrote the following example in Groovy, but using standard Java libs and idiomatic Java. Warn me if anything is unreadable:

import java.lang.annotation.*

// Our Rule interface
interface Rule<T> { boolean isValid(T t) }

// Here is the annotation which can receive a Rule class
@Retention(RetentionPolicy.RUNTIME)
@interface Validation { Class<? extends Rule> value() }

// An implementation of our Rule, in this case, for a Person's name
class NameRule implements Rule<Person> {
  PersonDAO dao = new PersonDAO()
  boolean isValid(Person person) {
    Integer mode = dao.getNameValidationMode()
    if (mode == 1) { // Don't hardcode numbers; use enums
      return person.name ==~ "[A-Z]{1}[a-z ]{2,25}" // regex matching
    } else if (mode == 2) {
      return person.name ==~ "[a-zA-Z]{1,25}"
    }
  }
}

After these declarations, the usage:

// Our model with an annotated field
class Person {
  @Validation(NameRule.class)
  String name
}

// Here we are mocking a database select to get the rule save in the database
// Don't use hardcoded numbers, stick to a enum or anything else
class PersonDAO { Integer getNameValidationMode() { return 1 } }

The processing of the annotations:

// Here we get each annotation and process it against the object
class AnnotationProcessor {
  String validate(Person person) {
    def annotatedFields = person.class.declaredFields.findAll { it.annotations.size() > 0 }
    for (field in annotatedFields) {
      for (annotation in field.annotations) {
        Rule rule = annotation.value().newInstance()
        if (! rule.isValid(person)) {
          return "Error: name is not valid"
        }
        else {
          return "Valid"
        }
      }
    }
  }
}

And tests:

// These two must pass
assert new AnnotationProcessor().validate( 
  new Person(name: "spongebob squarepants") ) == "Error: name is not valid"

assert new AnnotationProcessor().validate( 
  new Person(name: "John doe") ) == "Valid"

Also, take a look at GContracts, it provides some interesting validation-through-annotations model.

Will
  • 14,348
  • 1
  • 42
  • 44
  • How do you do this in a compile-time annotation processor where `annotation.value().newInstance()` may not work? Referring to http://stackoverflow.com/questions/39222612/how-to-create-an-instance-out-of-a-typemirror – Erik Hofer Aug 30 '16 at 14:48
0

Annotation parameters are hard coded constants in the classfile. So the only way to change them is to generate a new classfile.

Unfortunately, I'm not familiar with Hibernate, so I can't suggest the best option in your specific case.

Antimony
  • 37,781
  • 10
  • 100
  • 107
  • You said that annotation are hard coded in the classfile. So, is there a possibility to load classfiles and modify some values ? I do not know if you see what I mean ? – javaxiss May 17 '13 at 15:46
  • Once loaded, classfiles are immutable. You can change them all you want before they are loaded though. – Antimony May 18 '13 at 01:39