Hi,
There's something I've been trying to do and I don't know if it complies with the JSR-303 specifications.
Let's assume the following model:
Code:
@Entity
public class Person {
// ... some other attributes...
@Embedded
private Phone phone;
}
@Entity
public class Company {
// ... some other attributes...
@Embedded
private Phone phone;
}
@Embeddable
public class Phone {
private String number;
private String extension;
}
So we have a Person that has a Phone and a Company that also has a Phone. Let's say I want to validate the format of the phone number one way for the Person and another way for the Company, I thought the following would be valid:
Code:
@Target({ METHOD, FIELD, TYPE })
@Retention(RUNTIME)
@Documented
@Constraint(validatedBy = PhoneConstraintValidator.class)
public @interface PhoneConstraint {
String message() default "{PhoneConstraint.message}";
Class<?>[] groups() default { };
Class<? extends Payload>[] payload() default {};
PhoneConstraintMode value();
}
public enum PhoneConstraintMode {
MODE_A, MODE_B
}
public class PhoneConstraintValidator implements ContraintValidator<PhoneConstraint, Phone> {
private PhoneConstraintMode mode;
public void initialize(PhoneConstraint phoneConstraint) {
mode = phoneConstraint.value();
}
public boolean isValid(Phone value, ConstraintValidatorContext context) {
boolean valid = true;
if (value != null) {
if (mode == PhoneConstraintMode.MODE_A) {
if (/*test goes here*/) {
valid = false;
context.buildConstraintViolationWithTemplate("{PhoneConstraint.MODE_A.message}").addNode("number").addConstraintViolation().disableDefaultConstraintViolation();
}
} else if (/*test goes here*/) {
valid = false;
context.buildConstraintViolationWithTemplate("{PhoneConstraint.MODE_B.message}").addNode("number").addConstraintViolation().disableDefaultConstraintViolation();
}
}
}
return valid;
}
}
Then, I would simply place the constraint on the field and use the right mode in each owning entity:
Code:
@Entity
public class Person {
// ... some other attributes...
@Embedded
@PhoneConstraint(PhoneConstraintMode.MODE_A)
private Phone phone;
}
@Entity
public class Company {
// ... some other attributes...
@Embedded
@PhoneConstraint(PhoneConstraintMode.MODE_B)
private Phone phone;
}
This works. The property path in the ConstraintViolation is "phone.number" when I validate the Person or the Company, but the invalid value is the whole Phone object and not the invalid number. Wouldn't it make sense that the invalid value reflects the property path of a given ConstraintViolation based on the root bean being validated? I understand that the value validated by the constraint is the Phone as a whole, but if I explicitly disable the default constraint and add a node to the constraint that I'm creating, I thought that the invalid value would match that node path. Or is what I'm trying to do a misuse of the validation API?
Thank you.