[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<? extends ConstraintValidator>>
- * 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<Class, Class<? extends ConstraintValidator>> 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