[hibernate-commits] Hibernate SVN: r15889 - in validator/trunk/hibernate-validator/src: main/java/org/hibernate/validation/util and 3 other directories.

hibernate-commits at lists.jboss.org hibernate-commits at lists.jboss.org
Wed Feb 4 07:41:32 EST 2009


Author: hardy.ferentschik
Date: 2009-02-04 07:41:32 -0500 (Wed, 04 Feb 2009)
New Revision: 15889

Added:
   validator/trunk/hibernate-validator/src/test/java/org/hibernate/validation/eg/MultipleMinMax.java
   validator/trunk/hibernate-validator/src/test/java/org/hibernate/validation/engine/ValidatorResolutionTest.java
Modified:
   validator/trunk/hibernate-validator/src/main/java/org/hibernate/validation/engine/ConstraintTree.java
   validator/trunk/hibernate-validator/src/main/java/org/hibernate/validation/util/ValidatorTypeHelper.java
   validator/trunk/hibernate-validator/src/test/java/org/hibernate/validation/util/ValidatorTypeTest.java
Log:
BVAL-99 first cut for validator resolution. needs more tests.

Modified: validator/trunk/hibernate-validator/src/main/java/org/hibernate/validation/engine/ConstraintTree.java
===================================================================
--- validator/trunk/hibernate-validator/src/main/java/org/hibernate/validation/engine/ConstraintTree.java	2009-02-04 08:49:24 UTC (rev 15888)
+++ validator/trunk/hibernate-validator/src/main/java/org/hibernate/validation/engine/ConstraintTree.java	2009-02-04 12:41:32 UTC (rev 15889)
@@ -17,15 +17,19 @@
 */
 package org.hibernate.validation.engine;
 
+import java.lang.annotation.Annotation;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Map;
 import javax.validation.ConstraintDescriptor;
 import javax.validation.ConstraintValidator;
+import javax.validation.ConstraintValidatorFactory;
 import javax.validation.ValidationException;
 
 import org.slf4j.Logger;
 
 import org.hibernate.validation.util.LoggerFactory;
+import org.hibernate.validation.util.ValidatorTypeHelper;
 
 /**
  * Due to constraint conposition a single constraint annotation can lead to a whole constraint tree beeing validated.
@@ -76,7 +80,7 @@
 		return descriptor;
 	}
 
-	public <T> void validateConstraints(Object value, Class<T> beanClass, ValidationContext<T> validationContext) {
+	public void validateConstraints(Object value, Class beanClass, ValidationContext validationContext) {
 		for ( ConstraintTree tree : getChildren() ) {
 			tree.validateConstraints( value, beanClass, validationContext );
 		}
@@ -86,7 +90,10 @@
 		if ( log.isTraceEnabled() ) {
 			log.trace( "Validating value {} against constraint defined by {}", value, descriptor );
 		}
-		if ( !getConstraintValidator( descriptor, validationContext ).isValid( value, constraintContext ) ) {
+		ConstraintValidator validator = getInitalizedValidator(
+				value, validationContext.getConstraintValidatorFactory()
+		);
+		if ( !validator.isValid( value, constraintContext ) ) {
 			for ( ConstraintValidatorContextImpl.ErrorMessage error : constraintContext.getErrorMessages() ) {
 				final String message = error.getMessage();
 				createConstraintViolation( value, beanClass, validationContext, leafBeanInstance, message, descriptor );
@@ -127,25 +134,100 @@
 		validationContext.addConstraintFailure( failingConstraintViolation );
 	}
 
-	private ConstraintValidator getConstraintValidator(ConstraintDescriptor descriptor, ValidationContext validationContext) {
+	/**
+	 * @param value The value to be validated.
+	 * @param constraintFactory constraint factory used to instantiate the constraint validator.
+	 *
+	 * @return A initalized constraint validator matching the type of the value to be validated.
+	 */
+	private ConstraintValidator getInitalizedValidator(Object value, ConstraintValidatorFactory constraintFactory) {
 		ConstraintValidator constraintValidator;
-		try {
-			//FIXME do choose the right validator depending on the object validated
-			constraintValidator = validationContext.getConstraintValidatorFactory().getInstance( descriptor.getConstraintValidatorClasses().get(0) );
+		Class validatorClass;
+		if ( value == null ) {
+			validatorClass = descriptor.getConstraintValidatorClasses().get( 0 );
 		}
-		catch ( RuntimeException e ) {
-			//FIXME do choose the right validator depending on the object validated
-			throw new ValidationException(
-					"Unable to instantiate " + descriptor.getConstraintValidatorClasses().get(0), e
-			);
+		else {
+			validatorClass = findMatchingValidatorClass( value );
 		}
+		constraintValidator = constraintFactory.getInstance( validatorClass );
+		initializeConstraint( descriptor, constraintValidator );
+		return constraintValidator;
+	}
 
+	/**
+	 * Runs the validator resolution algorithm.
+	 *
+	 * @param value the object to validate
+	 *
+	 * @return The class of a matching validator.
+	 */
+	private Class findMatchingValidatorClass(Object value) {
+		Class valueClass = value.getClass();
+
+		Map<Class<?>, Class<? extends ConstraintValidator<? extends Annotation, ?>>> validatorsTypes = ValidatorTypeHelper
+				.getValidatorsTypes( descriptor.getConstraintValidatorClasses() );
+
+		List<Class> assignableClasses = new ArrayList<Class>();
+		for ( Class clazz : validatorsTypes.keySet() ) {
+			if ( clazz.isAssignableFrom( valueClass ) ) {
+				assignableClasses.add( clazz );
+			}
+		}
+
+		resolveAssignableClasses( assignableClasses );
+		if ( assignableClasses.size() != 1 ) {
+			StringBuilder builder = new StringBuilder();
+			builder.append( "There are multiple validators which could validate the type " );
+			builder.append( valueClass );
+			builder.append( ". The validator classes are: " );
+			for ( Class clazz : assignableClasses ) {
+				builder.append( clazz.getName() );
+				builder.append( ", " );
+			}
+			builder.delete( builder.length() - 2, builder.length() );
+			throw new ValidationException( builder.toString() );
+		}
+
+		return validatorsTypes.get( assignableClasses.get( 0 ) );
+	}
+
+	/**
+	 * Tries to reduce all assignable classes down to a single class.
+	 *
+	 * @param assignableClasses The set of all classes which are assignable to the class of the value to be validated and
+	 * which are handled by at least one of the  validators for the specified constraint.
+	 */
+	private void resolveAssignableClasses(List<Class> assignableClasses) {
+		if ( assignableClasses.size() == 1 ) {
+			return;
+		}
+
+		List<Class> classesToRemove = new ArrayList<Class>();
+		do {
+			classesToRemove.clear();
+			Class clazz = assignableClasses.get( 0 );
+			for ( int i = 1; i <= assignableClasses.size(); i++ ) {
+				if ( clazz.isAssignableFrom( assignableClasses.get( i ) ) ) {
+					classesToRemove.add( clazz );
+				}
+				else if ( assignableClasses.get( i ).isAssignableFrom( clazz ) ) {
+					classesToRemove.add( assignableClasses.get( i ) );
+				}
+			}
+			assignableClasses.removeAll( classesToRemove );
+		} while ( classesToRemove.size() > 0 );
+	}
+
+	@SuppressWarnings("unchecked")
+	private void initializeConstraint
+			(ConstraintDescriptor
+					descriptor, ConstraintValidator
+					constraintValidator) {
 		try {
 			constraintValidator.initialize( descriptor.getAnnotation() );
 		}
 		catch ( RuntimeException e ) {
 			throw new ValidationException( "Unable to intialize " + constraintValidator.getClass().getName(), e );
 		}
-		return constraintValidator;
 	}
 }

Modified: validator/trunk/hibernate-validator/src/main/java/org/hibernate/validation/util/ValidatorTypeHelper.java
===================================================================
--- validator/trunk/hibernate-validator/src/main/java/org/hibernate/validation/util/ValidatorTypeHelper.java	2009-02-04 08:49:24 UTC (rev 15888)
+++ validator/trunk/hibernate-validator/src/main/java/org/hibernate/validation/util/ValidatorTypeHelper.java	2009-02-04 12:41:32 UTC (rev 15889)
@@ -1,3 +1,20 @@
+// $Id$
+/*
+* JBoss, Home of Professional Open Source
+* Copyright 2008, Red Hat Middleware LLC, and individual contributors
+* by the @authors tag. See the copyright.txt in the distribution for a
+* full listing of individual contributors.
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+* http://www.apache.org/licenses/LICENSE-2.0
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
 package org.hibernate.validation.util;
 
 import java.lang.annotation.Annotation;
@@ -5,64 +22,68 @@
 import java.lang.reflect.Type;
 import java.lang.reflect.TypeVariable;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 import javax.validation.ConstraintValidator;
 import javax.validation.ValidationException;
 
 
 /**
- * Helper methods around ConstraintValidator types
+ * Helper methods around <code>ConstraintValidator</code> types.
+ *
+ * @author Emmanuel Bernanrd
+ * @author Hardy Ferentschik
  */
 public class ValidatorTypeHelper {
 	private static final int VALIDATOR_TYPE_INDEX = 1;
 
 	/**
-	 * for a lsit of validators, return a Map<Class, Class&lt;? extends ConstraintValidator&gt;&gt;
-	 * The key is the type the validator accepts, the value is the validator class itself
-
+	 * @param validators List of constraint validator classes (for a given constraint).
+	 *
+	 * @return Return a Map&lt;Class, Class&lt;? extends ConstraintValidator&gt;&gt; where the map
+	 *         key is the type the validator accepts and value the validator class itself.
 	 */
 	public static Map<Class<?>, Class<? extends ConstraintValidator<? extends Annotation, ?>>>
-	    getValidatorsTypes(Class<? extends ConstraintValidator<? extends Annotation,?>>[] validators) {
-		if (validators == null || validators.length == 0) {
-			//TODObe more contextually specific
-			throw new ValidationException("No ConstraintValidators associated to @Constraint");
+	getValidatorsTypes(List<Class<? extends ConstraintValidator<?, ?>>> validators) {
+		if ( validators == null || validators.size() == 0 ) {
+			throw new ValidationException( "No ConstraintValidators associated to @Constraint" );
 		}
 		else {
 			Map<Class<?>, Class<? extends ConstraintValidator<? extends Annotation, ?>>> validatorsTypes =
 					new HashMap<Class<?>, Class<? extends ConstraintValidator<? extends Annotation, ?>>>();
-			for (Class<? extends ConstraintValidator<? extends Annotation,?>> validator : validators) {
-				validatorsTypes.put( extractType(validator), validator );
+			for ( Class<? extends ConstraintValidator<? extends Annotation, ?>> validator : validators ) {
+				validatorsTypes.put( extractType( validator ), validator );
 			}
 			return validatorsTypes;
 		}
 	}
 
-	private static Class<?> extractType(Class<? extends ConstraintValidator<?,?>> validator) {
+	private static Class<?> extractType(Class<? extends ConstraintValidator<?, ?>> validator) {
 
 		Map<Type, Type> resolvedTypes = new HashMap<Type, Type>();
-		Type constraintValidatorType = resolveTypes(resolvedTypes, validator);
+		Type constraintValidatorType = resolveTypes( resolvedTypes, validator );
 
 		//we now have all bind from a type to its resolution at one level
 
 		//FIXME throw assertion exception if constraintValidatorType == null
-		Type validatorType = ( (ParameterizedType) constraintValidatorType ).getActualTypeArguments()[VALIDATOR_TYPE_INDEX];
+		Type validatorType = ( ( ParameterizedType ) constraintValidatorType ).getActualTypeArguments()[VALIDATOR_TYPE_INDEX];
 		while ( resolvedTypes.containsKey( validatorType ) ) {
 			validatorType = resolvedTypes.get( validatorType );
 		}
 		//FIXME raise an exception if validatorType is not a class
-		return (Class<?>) validatorType;
+		return ( Class<?> ) validatorType;
 	}
 
 	//TEst method, remove
-	public static Type extractTypeLoose(Class<? extends ConstraintValidator<?,?>> validator) {
+	public static Type extractTypeLoose(Class<? extends ConstraintValidator<?, ?>> validator) {
 
 		Map<Type, Type> resolvedTypes = new HashMap<Type, Type>();
-		Type constraintValidatorType = resolveTypes(resolvedTypes, validator);
+		Type constraintValidatorType = resolveTypes( resolvedTypes, validator );
 
 		//we now have all bind from a type to its resolution at one level
 
 		//FIXME throw assertion exception if constraintValidatorType == null
-		Type validatorType = ( (ParameterizedType) constraintValidatorType ).getActualTypeArguments()[VALIDATOR_TYPE_INDEX];
+		Type validatorType = ( ( ParameterizedType ) constraintValidatorType ).getActualTypeArguments()[VALIDATOR_TYPE_INDEX];
 		while ( resolvedTypes.containsKey( validatorType ) ) {
 			validatorType = resolvedTypes.get( validatorType );
 		}
@@ -71,27 +92,33 @@
 	}
 
 	private static Type resolveTypes(Map<Type, Type> resolvedTypes, Type type) {
-		if (type == null) {
+		if ( type == null ) {
 			return null;
 		}
 		else if ( type instanceof Class ) {
-			Class<?> clazz = ( Class<?> ) type;
-			Type returnedType = resolveTypes(resolvedTypes, clazz.getGenericSuperclass() );
-			if (returnedType != null) return returnedType;
-			for (Type genericInterface : clazz.getGenericInterfaces() ) {
-				returnedType = resolveTypes(resolvedTypes, genericInterface );
-				if (returnedType != null) return returnedType;
+			Class clazz = ( Class ) type;
+			Type returnedType = resolveTypes( resolvedTypes, clazz.getGenericSuperclass() );
+			if ( returnedType != null ) {
+				return returnedType;
 			}
+			for ( Type genericInterface : clazz.getGenericInterfaces() ) {
+				returnedType = resolveTypes( resolvedTypes, genericInterface );
+				if ( returnedType != null ) {
+					return returnedType;
+				}
+			}
 		}
 		else if ( type instanceof ParameterizedType ) {
-			ParameterizedType paramType = (ParameterizedType) type;
-			if ( ! ( paramType.getRawType() instanceof Class ) ) return null; //don't know what to do here
-			Class<?> rawType = (Class<?>) paramType.getRawType();
+			ParameterizedType paramType = ( ParameterizedType ) type;
+			if ( !( paramType.getRawType() instanceof Class ) ) {
+				return null; //don't know what to do here
+			}
+			Class<?> rawType = ( Class<?> ) paramType.getRawType();
 
 			TypeVariable<?>[] originalTypes = rawType.getTypeParameters();
 			Type[] partiallyResolvedTypes = paramType.getActualTypeArguments();
 			int nbrOfParams = originalTypes.length;
-			for (int i = 0 ; i < nbrOfParams; i++) {
+			for ( int i = 0; i < nbrOfParams; i++ ) {
 				resolvedTypes.put( originalTypes[i], partiallyResolvedTypes[i] );
 			}
 
@@ -100,9 +127,9 @@
 				return type;
 			}
 			else {
-				resolveTypes(resolvedTypes, rawType.getGenericSuperclass() );
-				for (Type genericInterface : rawType.getGenericInterfaces() ) {
-					resolveTypes(resolvedTypes, genericInterface );
+				resolveTypes( resolvedTypes, rawType.getGenericSuperclass() );
+				for ( Type genericInterface : rawType.getGenericInterfaces() ) {
+					resolveTypes( resolvedTypes, genericInterface );
 				}
 			}
 		}


Property changes on: validator/trunk/hibernate-validator/src/main/java/org/hibernate/validation/util/ValidatorTypeHelper.java
___________________________________________________________________
Name: svn:keywords
   + Id

Added: validator/trunk/hibernate-validator/src/test/java/org/hibernate/validation/eg/MultipleMinMax.java
===================================================================
--- validator/trunk/hibernate-validator/src/test/java/org/hibernate/validation/eg/MultipleMinMax.java	                        (rev 0)
+++ validator/trunk/hibernate-validator/src/test/java/org/hibernate/validation/eg/MultipleMinMax.java	2009-02-04 12:41:32 UTC (rev 15889)
@@ -0,0 +1,39 @@
+// $Id:$
+/*
+* JBoss, Home of Professional Open Source
+* Copyright 2008, Red Hat Middleware LLC, and individual contributors
+* by the @authors tag. See the copyright.txt in the distribution for a
+* full listing of individual contributors.
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+* http://www.apache.org/licenses/LICENSE-2.0
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package org.hibernate.validation.eg;
+
+import javax.validation.constraints.Max;
+import javax.validation.constraints.Min;
+
+/**
+ * @author Hardy Ferentschik
+ */
+public class MultipleMinMax {
+	@Min(10l)
+	@Max(20l)
+	Number number;
+
+	@Min(10l)
+	@Max(20l)
+	String stringNumber;
+
+	public MultipleMinMax(String stringNumber, Number number) {
+		this.stringNumber = stringNumber;
+		this.number = number;
+	}
+}

Copied: validator/trunk/hibernate-validator/src/test/java/org/hibernate/validation/engine/ValidatorResolutionTest.java (from rev 15854, validator/trunk/hibernate-validator/src/test/java/org/hibernate/validation/engine/ConstraintCompositionTest.java)
===================================================================
--- validator/trunk/hibernate-validator/src/test/java/org/hibernate/validation/engine/ValidatorResolutionTest.java	                        (rev 0)
+++ validator/trunk/hibernate-validator/src/test/java/org/hibernate/validation/engine/ValidatorResolutionTest.java	2009-02-04 12:41:32 UTC (rev 15889)
@@ -0,0 +1,45 @@
+// $Id$
+/*
+* JBoss, Home of Professional Open Source
+* Copyright 2008, Red Hat Middleware LLC, and individual contributors
+* by the @authors tag. See the copyright.txt in the distribution for a
+* full listing of individual contributors.
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+* http://www.apache.org/licenses/LICENSE-2.0
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,  
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package org.hibernate.validation.engine;
+
+import java.util.Set;
+import javax.validation.ConstraintViolation;
+import javax.validation.Validator;
+
+import org.junit.Test;
+
+import org.hibernate.validation.eg.MultipleMinMax;
+import static org.hibernate.validation.util.TestUtil.assertNumberOfViolations;
+import static org.hibernate.validation.util.TestUtil.getValidator;
+
+/**
+ * Tests for constraint validator resolution.
+ *
+ * @author Hardy Ferentschik
+ */
+public class ValidatorResolutionTest {
+
+	@Test
+	public void testValidatorResolutionForMinMax() {
+		Validator validator = getValidator();
+
+		MultipleMinMax minMax = new MultipleMinMax( "5", 5 );
+		Set<ConstraintViolation<MultipleMinMax>> constraintViolations = validator.validate( minMax );
+		assertNumberOfViolations( constraintViolations, 2 );
+	}
+}
\ No newline at end of file

Modified: validator/trunk/hibernate-validator/src/test/java/org/hibernate/validation/util/ValidatorTypeTest.java
===================================================================
--- validator/trunk/hibernate-validator/src/test/java/org/hibernate/validation/util/ValidatorTypeTest.java	2009-02-04 08:49:24 UTC (rev 15888)
+++ validator/trunk/hibernate-validator/src/test/java/org/hibernate/validation/util/ValidatorTypeTest.java	2009-02-04 12:41:32 UTC (rev 15889)
@@ -19,6 +19,8 @@
 
 import java.lang.annotation.Annotation;
 import java.lang.reflect.Type;
+import java.util.ArrayList;
+import java.util.List;
 import java.util.Map;
 import java.util.Set;
 import javax.validation.ConstraintValidator;
@@ -29,7 +31,6 @@
 import org.junit.Test;
 
 import org.hibernate.validation.constraints.composition.FrenchZipcodeConstraintValidator;
-import org.hibernate.validation.util.ValidatorTypeHelper;
 
 /**
  * Tests for message resolution.
@@ -40,8 +41,10 @@
 
 	@Test
 	public void testTypeDiscovery() {
+		List<Class<? extends ConstraintValidator<?, ?>>> validators = new ArrayList<Class<? extends ConstraintValidator<?, ?>>>();
+		validators.add( FrenchZipcodeConstraintValidator.class );
 		Map<Class<?>, Class<? extends ConstraintValidator<? extends Annotation, ?>>> validatorsTypes = ValidatorTypeHelper
-				.getValidatorsTypes( new Class[] { FrenchZipcodeConstraintValidator.class } );
+				.getValidatorsTypes( validators );
 		assertEquals( FrenchZipcodeConstraintValidator.class, validatorsTypes.get( String.class ) );
 
 		Type type = ValidatorTypeHelper
@@ -49,20 +52,19 @@
 
 
 		Type type2 = ValidatorTypeHelper
-				.extractTypeLoose( TestValidator2.class  );
+				.extractTypeLoose( TestValidator2.class );
 		assertEquals( type, type2 );
 
 	}
 
 	public static class TestValidator implements ConstraintValidator<NotNull, Set<String>> {
 		public void initialize(NotNull constraintAnnotation) {
-			//To change body of implemented methods use File | Settings | File Templates.
 		}
 
 		public boolean isValid(Set<String> object, ConstraintValidatorContext constraintValidatorContext) {
-			return false;  //To change body of implemented methods use File | Settings | File Templates.
+			return false;
 		}
-}
+	}
 
 	public static class TestValidator2 implements ConstraintValidator<NotNull, Set<String>> {
 		public void initialize(NotNull constraintAnnotation) {




More information about the hibernate-commits mailing list