Thank you Hardy, that worked beautifully...this is a great API.
Just a few related questions I would like to ask here - let me know if I need to make new topics instead.
1) This is minor, but for some reason with this solution I saw double messages for each error, but it could be related to Spring.
Is there something in the setup that could cause JSR303 to validate twice? For example, a constraint belonging to more than one group
from those marking the current constraints?
2) I have another requirement which I wonder is doable using separate constraints/validators:
The Hobbyist also has a bean-level constraint, HasNoDuplicateHobby, which raises a violation for each duplicate in the list.
This works well on its own, but I would like to make it part of the syntaxCheck-duplicateCheck-semanticCheck group sequence.
Is this possible, using the existing code? I would think not, and that a custom single top-level constraint is necessary which performs all the checks
within isValid(), navigating the tree up and down as needed.
3) Regarding navigation within the object graph using ConstraintValidatorContext, in the below code
from the HasNoDuplicateHobby constraint validator's isValid(), does the context get reset after each .addConstraintViolation()?
I think the answer is yes (otherwise the path would become corrupted at the next iteration)? Relatedly, is the invocation to
disableDefaultConstraintViolation() necessary after each addConstraintViolation()? I am just trying to grasp better how navigation
works:
Code:
//...HasNoDuplicateHobby's constraint validator's isValid() invoked on a hobbyist target
boolean valid = true;
List<HobbyString> list = ((Hobbyist )target ).getHobbies();
outer: for ( int i = 0; i < list.size(); i++)
for ( int j = 0; j < i; j++) {
HobbyString current = list.get( i);
if ( list.get(j).equals( current)) {
//mark current as a violator
valid = false;
ctx.disableDefaultConstraintViolation();
ctx.buildConstraintViolationWithTemplate( "duplicates not allowed").addNode("hobbies")
.addNode( "hobby").inIterable().atIndex( i)
.addConstraintViolation();
continue outer;
}
}
return valid;