Author: hardy.ferentschik
Date: 2009-10-14 13:05:10 -0400 (Wed, 14 Oct 2009)
New Revision: 17746
Modified:
validator/trunk/hibernate-validator/src/main/java/org/hibernate/validator/engine/ConstraintValidatorContextImpl.java
validator/trunk/hibernate-validator/src/test/java/org/hibernate/validator/constraints/ConstraintValidatorContextTest.java
Log:
HV-253 Correctly implemented the usage of inIterable, atKey and atIndex
Modified:
validator/trunk/hibernate-validator/src/main/java/org/hibernate/validator/engine/ConstraintValidatorContextImpl.java
===================================================================
---
validator/trunk/hibernate-validator/src/main/java/org/hibernate/validator/engine/ConstraintValidatorContextImpl.java 2009-10-14
15:12:59 UTC (rev 17745)
+++
validator/trunk/hibernate-validator/src/main/java/org/hibernate/validator/engine/ConstraintValidatorContextImpl.java 2009-10-14
17:05:10 UTC (rev 17746)
@@ -20,7 +20,6 @@
import java.util.ArrayList;
import java.util.List;
import javax.validation.ConstraintValidatorContext;
-import javax.validation.Path;
import javax.validation.ValidationException;
import javax.validation.metadata.ConstraintDescriptor;
@@ -57,8 +56,10 @@
}
public List<MessageAndPath> getMessageAndPathList() {
- if(defaultDisabled && messageAndPaths.size() == 0) {
- throw new ValidationException("At least one custom message must be created if the
default error message gets disabled.");
+ if ( defaultDisabled && messageAndPaths.size() == 0 ) {
+ throw new ValidationException(
+ "At least one custom message must be created if the default error message gets
disabled."
+ );
}
List<MessageAndPath> returnedMessageAndPaths = new
ArrayList<MessageAndPath>( messageAndPaths );
@@ -107,9 +108,8 @@
}
public ConstraintViolationBuilder.NodeBuilderCustomizableContext addNode(String name)
{
- NodeImpl node = new NodeImpl( name );
- propertyPath.addNode( node );
- return new InIterableNodeBuilderImpl( messageTemplate, propertyPath );
+ // we need to defer the adding of the new node, since we don't know yet whether
the new node will be iterable
+ return new InIterableNodeBuilderImpl( messageTemplate, propertyPath, name );
}
public ConstraintValidatorContext addConstraintViolation() {
@@ -121,57 +121,78 @@
class InIterableNodeBuilderImpl implements
ConstraintViolationBuilder.NodeBuilderCustomizableContext {
String messageTemplate;
PathImpl propertyPath;
+ String deferredNodeName;
- InIterableNodeBuilderImpl(String template, PathImpl path) {
- messageTemplate = template;
- propertyPath = path;
+ InIterableNodeBuilderImpl(String template, PathImpl path, String deferredNodeName) {
+ this.messageTemplate = template;
+ this.propertyPath = path;
+ this.deferredNodeName = deferredNodeName;
}
public ConstraintViolationBuilder.NodeContextBuilder inIterable() {
- return new InIterablePropertiesBuilderImpl( messageTemplate, propertyPath );
+ this.propertyPath.getLeafNode().setInIterable( true );
+ return new InIterablePropertiesBuilderImpl( messageTemplate, propertyPath,
deferredNodeName );
}
public ConstraintViolationBuilder.NodeBuilderCustomizableContext addNode(String name)
{
- Path.Node node = new NodeImpl( name );
- propertyPath.addNode( node );
+ addDeferredNode(); // now that we add another node we can add the deferred parent
node
+ deferredNodeName = name;
return this;
}
public ConstraintValidatorContext addConstraintViolation() {
+ addDeferredNode();
messageAndPaths.add( new MessageAndPath( messageTemplate, propertyPath ) );
return ConstraintValidatorContextImpl.this;
}
+
+ private void addDeferredNode() {
+ if ( deferredNodeName != null ) {
+ NodeImpl node = new NodeImpl( deferredNodeName );
+ propertyPath.addNode( node );
+ }
+ }
}
class InIterablePropertiesBuilderImpl implements
ConstraintViolationBuilder.NodeContextBuilder {
String messageTemplate;
PathImpl propertyPath;
+ String deferredNodeName;
- InIterablePropertiesBuilderImpl(String template, PathImpl path) {
- messageTemplate = template;
- propertyPath = path;
- propertyPath.getLeafNode().setInIterable( true );
+ InIterablePropertiesBuilderImpl(String template, PathImpl path, String
deferredNodeName) {
+ this.messageTemplate = template;
+ this.propertyPath = path;
+ this.deferredNodeName = deferredNodeName;
}
public ConstraintViolationBuilder.NodeBuilderDefinedContext atKey(Object key) {
propertyPath.getLeafNode().setKey( key );
+ addDeferredNode();
return new NodeBuilderImpl( messageTemplate, propertyPath );
}
public ConstraintViolationBuilder.NodeBuilderDefinedContext atIndex(Integer index) {
propertyPath.getLeafNode().setIndex( index );
+ addDeferredNode();
return new NodeBuilderImpl( messageTemplate, propertyPath );
}
public ConstraintViolationBuilder.NodeBuilderCustomizableContext addNode(String name)
{
- Path.Node node = new NodeImpl( name );
- propertyPath.addNode( node );
- return new InIterableNodeBuilderImpl( messageTemplate, propertyPath );
+ addDeferredNode();
+ return new InIterableNodeBuilderImpl( messageTemplate, propertyPath, name );
}
public ConstraintValidatorContext addConstraintViolation() {
+ addDeferredNode();
messageAndPaths.add( new MessageAndPath( messageTemplate, propertyPath ) );
return ConstraintValidatorContextImpl.this;
}
+
+ private void addDeferredNode() {
+ if ( deferredNodeName != null ) {
+ NodeImpl node = new NodeImpl( deferredNodeName );
+ propertyPath.addNode( node );
+ }
+ }
}
}
Modified:
validator/trunk/hibernate-validator/src/test/java/org/hibernate/validator/constraints/ConstraintValidatorContextTest.java
===================================================================
---
validator/trunk/hibernate-validator/src/test/java/org/hibernate/validator/constraints/ConstraintValidatorContextTest.java 2009-10-14
15:12:59 UTC (rev 17745)
+++
validator/trunk/hibernate-validator/src/test/java/org/hibernate/validator/constraints/ConstraintValidatorContextTest.java 2009-10-14
17:05:10 UTC (rev 17746)
@@ -17,13 +17,30 @@
*/
package org.hibernate.validator.constraints;
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
import java.util.Set;
+import javax.validation.Constraint;
+import javax.validation.ConstraintValidator;
+import javax.validation.ConstraintValidatorContext;
import javax.validation.ConstraintViolation;
+import javax.validation.Payload;
import javax.validation.Validator;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertTrue;
import org.testng.annotations.Test;
+import org.hibernate.validator.engine.ConstraintValidatorContextImpl;
+import org.hibernate.validator.engine.MessageAndPath;
+import org.hibernate.validator.engine.PathImpl;
import org.hibernate.validator.util.TestUtil;
+import static org.hibernate.validator.util.TestUtil.assertCorrectConstraintTypes;
import static org.hibernate.validator.util.TestUtil.assertCorrectPropertyPaths;
import static org.hibernate.validator.util.TestUtil.assertNumberOfViolations;
@@ -66,4 +83,163 @@
assertNumberOfViolations( constraintViolations, 1 );
assertCorrectPropertyPaths( constraintViolations, "start" );
}
+
+ @Test
+ public void testDifferentPaths() {
+ String message = "message";
+ ConstraintValidatorContextImpl context = createEmptyConstraintValidatorContextImpl();
+ context.buildConstraintViolationWithTemplate( message )
+ .addNode( "foo" )
+ .addNode( "bar" ).inIterable().atIndex( 3 )
+ .addConstraintViolation();
+
+ List<MessageAndPath> messageAndPathList = context.getMessageAndPathList();
+ assertMessageAndPath( messageAndPathList.get( 0 ), message, "foo[3].bar" );
+
+
+ context = createEmptyConstraintValidatorContextImpl();
+ context.buildConstraintViolationWithTemplate( message )
+ .addNode( "foo" )
+ .addNode( null ).inIterable().atKey( "test" )
+ .addConstraintViolation();
+
+ messageAndPathList = context.getMessageAndPathList();
+ assertMessageAndPath( messageAndPathList.get( 0 ), message, "foo[test]" );
+
+ context = createEmptyConstraintValidatorContextImpl();
+ context.buildConstraintViolationWithTemplate( message )
+ .addNode( "foo" )
+ .addNode( "bar" ).inIterable().atKey( "test" )
+ .addNode( "fubar" )
+ .addConstraintViolation();
+
+ messageAndPathList = context.getMessageAndPathList();
+ assertMessageAndPath( messageAndPathList.get( 0 ), message,
"foo[test].bar.fubar" );
+
+ context = createEmptyConstraintValidatorContextImpl();
+ context.buildConstraintViolationWithTemplate( message )
+ .addNode( "foo" )
+ .addNode( "bar" ).inIterable().atKey( "test" )
+ .addNode( "fubar" ).inIterable().atIndex( 10 )
+ .addConstraintViolation();
+
+ messageAndPathList = context.getMessageAndPathList();
+ assertMessageAndPath( messageAndPathList.get( 0 ), message,
"foo[test].bar[10].fubar" );
+
+ context = createEmptyConstraintValidatorContextImpl();
+ context.buildConstraintViolationWithTemplate( message )
+ .addNode( "foo" )
+ .addNode( "bar" ).inIterable().atKey( "test" )
+ .addNode( "fubar" ).inIterable()
+ .addConstraintViolation();
+
+ messageAndPathList = context.getMessageAndPathList();
+ assertMessageAndPath( messageAndPathList.get( 0 ), message,
"foo[test].bar[].fubar" );
+ }
+
+ @Test
+ public void testMultipleMessages() {
+ String message1 = "message1";
+ String message2 = "message2";
+ ConstraintValidatorContextImpl context = createEmptyConstraintValidatorContextImpl();
+ context.buildConstraintViolationWithTemplate( message1 )
+ .addNode( "foo" )
+ .addNode( "bar" ).inIterable().atKey( "key" )
+ .addConstraintViolation();
+ context.buildConstraintViolationWithTemplate( message2 )
+ .addConstraintViolation();
+
+ List<MessageAndPath> messageAndPathList = context.getMessageAndPathList();
+ assertTrue( messageAndPathList.size() == 2 );
+ assertMessageAndPath( messageAndPathList.get( 0 ), message1, "foo[key].bar"
);
+ assertMessageAndPath( messageAndPathList.get( 1 ), message2, "" );
+ }
+
+ /**
+ * HV-253
+ */
+ @Test
+ public void propertyPathInIterable() {
+ Validator validator = TestUtil.getValidator();
+ Group group = new Group( Gender.MALE, new Person( Gender.FEMALE ) );
+
+ Set<ConstraintViolation<Group>> constraintViolations = validator.validate(
group );
+ assertNumberOfViolations( constraintViolations, 1 );
+ assertCorrectPropertyPaths( constraintViolations, "persons[0]" );
+ assertCorrectConstraintTypes( constraintViolations, CompatiblePersons.class );
+ }
+
+ private ConstraintValidatorContextImpl createEmptyConstraintValidatorContextImpl() {
+ ConstraintValidatorContextImpl context = new ConstraintValidatorContextImpl(
+ PathImpl.createNewPath( null ), null
+ );
+ context.disableDefaultConstraintViolation();
+ return context;
+ }
+
+ private void assertMessageAndPath(MessageAndPath messageAndPath, String expectedMessage,
String expectedPath) {
+ assertEquals( messageAndPath.getPath(), PathImpl.createPathFromString( expectedPath ),
"Wrong path" );
+ assertEquals( messageAndPath.getMessage(), expectedMessage, "Wrong message"
);
+ }
+
+ private enum Gender {
+ MALE, FEMALE
+ }
+
+ @CompatiblePersons
+ private class Group {
+ Gender gender;
+ List<Person> persons = new ArrayList<Person>();
+
+ public Group(Gender gender, Person... persons) {
+ this.gender = gender;
+ this.persons.addAll( Arrays.asList( persons ) );
+ }
+ }
+
+ private class Person {
+ Gender gender;
+
+ public Person(Gender gender) {
+ this.gender = gender;
+ }
+ }
+
+ @Target({ java.lang.annotation.ElementType.TYPE,
java.lang.annotation.ElementType.ANNOTATION_TYPE })
+ @Retention(RetentionPolicy.RUNTIME)
+ @Constraint(validatedBy = { CompatiblePersonsValidator.class })
+ @Documented
+ public @interface CompatiblePersons {
+ String message() default "";
+
+ Class<?>[] groups() default { };
+
+ Class<? extends Payload>[] payload() default { };
+ }
+
+ public static class CompatiblePersonsValidator implements
ConstraintValidator<CompatiblePersons, Group> {
+ public void initialize(CompatiblePersons constraintAnnotation) {
+ }
+
+ public boolean isValid(Group group, ConstraintValidatorContext
constraintValidatorContext) {
+ if ( group == null ) {
+ return true;
+ }
+
+ constraintValidatorContext.disableDefaultConstraintViolation();
+
+ for ( int index = 0; index < group.persons.size(); index++ ) {
+ Person person = group.persons.get( index );
+ if ( !group.gender.equals( person.gender ) ) {
+ constraintValidatorContext
+ .buildConstraintViolationWithTemplate(
"constraints.CompatiblePersons.gender.message" )
+ .addNode( "persons" )
+ .addNode( null ).inIterable().atIndex( index )
+ .addConstraintViolation();
+ return false;
+ }
+ }
+ return true;
+ }
+ }
}