views:

250

answers:

3

Hello,

I need to create a custom constraint annotation which can access the value of another field of my bean. I'll use this annotation to validate the field because it depends on the value of the other but the way I define it the compiler says "The value for annotation attribute" of my field "must be a constant expression".

I've defined it in this way:

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy=EqualsFieldValidator.class)
@Documented
public @interface EqualsField {
    public String field();

    String message() default "{com.myCom.annotations.EqualsField.message}";

    Class<?>[] groups() default {};

    Class<? extends Payload>[] payload() default {};
}

public class EqualsFieldValidator implements ConstraintValidator<EqualsField, String>{

    private EqualsField equalsField;

    @Override
    public void initialize(EqualsField equalsField) {
        this.equalsField = equalsField;     
    }

    @Override
    public boolean isValid(String thisField, ConstraintValidatorContext arg1) {
        //my validation
    }

}

and in my bean I want something like this:

public class MyBean{

     private String field1;

     @EqualsField(field=field1)
     private String field2;
}

Is there any way to define the annotation so the field value can be a variable?

Thanks

A: 

As the compiler said annotations must be constant (i.e. you can determine the value at compile time.) Now If I'm guessing correctly it looks like you are using this annotation to denote that the values of those fields should be equal when run through the equals field validator. One approach you could take is using reflection. Instead of trying to annotate with the value, annotate with the field name instead

public class MyBean{

     private String field1;

     @EqualsField("field1")
     private String field2;
}

Then in your validator you can read the name of the field and use reflection to access it

Object o = object.getClass().getDeclaredField(annotationValue).get(object);
o == object.(field with annotation) OR
o.equals(object.(field with annotation));

Depending on what you are trying to do you may need to add in logic based on the field type, but still the same general principle.

M. Jessup
OK, but how can I access to the object where the annotation is in the EqualsFieldValidator?
Javi
You can't, unfortunately.
GaryF
+2  A: 

The easiest thing to do is take one step back: the constraint/validator you have written works on a field-level, but what you want to enforce is a cross-field dependency i.e. a class-level constraint.

Rewrite your constraint and validator to work at the class level (i.e. the annotation will go on the class, not on the field). That way you'll get access to the entire class. In your isValid(..) method, simply do a get on both the fields, compare, and return appropriately.

GaryF
It works in this way though I need to associate the validation error with one of the fields and if I validate the whole object I'll get an error for the whole object instead of an error for the field, but thanks anyway.
Javi
No, that's not the case. The second argument to the isValid method, ConstraintValidatorContext, allows you to override that behaviour. You can use the addError() method on that to say the error should appear on a specific field, as well as removing the default class-level error.
GaryF
A: 

Check out this previous question, has multiple solutions for cross-field validation: http://stackoverflow.com/questions/1972933/cross-field-validation-with-hibernate-validator-jsr-303

larsrc