[hibernate-commits] Hibernate SVN: r17746 - in validator/trunk/hibernate-validator/src: test/java/org/hibernate/validator/constraints and 1 other directory.

hibernate-commits at lists.jboss.org hibernate-commits at lists.jboss.org
Wed Oct 14 13:05:10 EDT 2009


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;
+		}
+	}
 }



More information about the hibernate-commits mailing list