Skip to content

Commit

Permalink
HV-747 Making sure the correct validator classes are used depending o…
Browse files Browse the repository at this point in the history
…n the constraint target
  • Loading branch information
gunnarmorling committed Mar 11, 2013
1 parent 270e7dc commit d745fc7
Show file tree
Hide file tree
Showing 5 changed files with 75 additions and 26 deletions.
Expand Up @@ -161,7 +161,7 @@ private <T, V> void validateConstraints(ValidationContext<T> executionContext,

private <T> boolean mainConstraintNeedsEvaluation(ValidationContext<T> executionContext, Set<ConstraintViolation<T>> constraintViolations) {
// there is no validator for the main constraints
if ( descriptor.getConstraintValidatorClasses().isEmpty() ) {
if ( descriptor.getApplyingConstraintValidatorClasses().isEmpty() ) {
return false;
}

Expand Down
Expand Up @@ -26,6 +26,7 @@
import javax.validation.ConstraintValidatorFactory;
import javax.validation.metadata.ConstraintDescriptor;

import org.hibernate.validator.internal.metadata.descriptor.ConstraintDescriptorImpl;
import org.hibernate.validator.internal.util.Contracts;
import org.hibernate.validator.internal.util.TypeHelper;
import org.hibernate.validator.internal.util.logging.Log;
Expand Down Expand Up @@ -76,7 +77,7 @@ public ConstraintValidatorManager(ConstraintValidatorFactory constraintValidator
* @return A initialized constraint validator for the given type and annotation of the value to be validated.
*/
public <V, A extends Annotation> ConstraintValidator<A, V> getInitializedValidator(Type validatedValueType,
ConstraintDescriptor<A> descriptor,
ConstraintDescriptorImpl<A> descriptor,
ConstraintValidatorFactory constraintFactory) {
Contracts.assertNotNull( validatedValueType );
Contracts.assertNotNull( descriptor );
Expand Down Expand Up @@ -180,9 +181,9 @@ public int numberOfCachedConstraintValidatorInstances() {
*
* @return The class of a matching validator.
*/
private <A extends Annotation> Class<? extends ConstraintValidator<?, ?>> findMatchingValidatorClass(ConstraintDescriptor<A> descriptor, Type validatedValueType) {
private <A extends Annotation> Class<? extends ConstraintValidator<?, ?>> findMatchingValidatorClass(ConstraintDescriptorImpl<A> descriptor, Type validatedValueType) {
Map<Type, Class<? extends ConstraintValidator<?, ?>>> availableValidatorTypes = TypeHelper.getValidatorsTypes(
descriptor.getConstraintValidatorClasses()
descriptor.getApplyingConstraintValidatorClasses()
);

List<Type> discoveredSuitableTypes = findSuitableValidatorTypes( validatedValueType, availableValidatorTypes );
Expand Down
Expand Up @@ -55,6 +55,7 @@
import org.hibernate.validator.internal.util.logging.LoggerFactory;

import static org.hibernate.validator.constraints.CompositionType.AND;
import static org.hibernate.validator.internal.util.CollectionHelper.newArrayList;
import static org.hibernate.validator.internal.util.CollectionHelper.newHashMap;
import static org.hibernate.validator.internal.util.CollectionHelper.newHashSet;

Expand Down Expand Up @@ -108,6 +109,8 @@ public class ConstraintDescriptorImpl<T extends Annotation> implements Constrain
*/
private final Class<? extends ConstraintValidator<T, ?>> crossParameterConstraintValidatorClass;

private final List<Class<? extends ConstraintValidator<T, ?>>> applyingConstraintValidatorClasses;

/**
* The groups for which to apply this constraint.
*/
Expand Down Expand Up @@ -176,13 +179,27 @@ public ConstraintDescriptorImpl(T annotation,
this.attributes = buildAnnotationParameterMap( annotation );
this.groups = buildGroupSet( implicitGroup );
this.payloads = buildPayloadSet( annotation );

this.constraintValidatorDefinitionClasses = constraintHelper.getValidatorClasses( annotationType );
this.crossParameterConstraintValidatorClass = findCrossParameterValidatorClass(
constraintValidatorDefinitionClasses
);
List<Class<? extends ConstraintValidator<T, ?>>> genericValidatorClasses = findGenericValidatorClasses(
constraintValidatorDefinitionClasses
);


this.constraintType = determineConstraintType( member );
this.composingConstraints = parseComposingConstraints( member, constraintHelper );

if ( constraintType == ConstraintType.GENERIC ) {
this.applyingConstraintValidatorClasses = Collections.unmodifiableList( genericValidatorClasses );
}
else {
List<Class<? extends ConstraintValidator<T, ?>>> validatorClasses = newArrayList();
validatorClasses.add( crossParameterConstraintValidatorClass );
this.applyingConstraintValidatorClasses = Collections.unmodifiableList( validatorClasses );
}
}

public ConstraintDescriptorImpl(Member member,
Expand Down Expand Up @@ -223,6 +240,16 @@ public ConstraintTarget getValidationAppliesTo() {
return constraintValidatorDefinitionClasses;
}

/**
* Returns those validators registered with this constraint which apply to
* the given constraint type (either generic or cross-parameter).
*
* @return The validators applying to type of this constraint.
*/
public List<Class<? extends ConstraintValidator<T, ?>>> getApplyingConstraintValidatorClasses() {
return applyingConstraintValidatorClasses;
}

@Override
public Map<String, Object> getAttributes() {
return attributes;
Expand Down Expand Up @@ -445,12 +472,26 @@ private Set<Class<?>> buildGroupSet(Class<?> implicitGroup) {
return crossParameterValidatorClass;
}

private List<Class<? extends ConstraintValidator<T, ?>>> findGenericValidatorClasses(List<Class<? extends ConstraintValidator<T, ?>>> constraintValidatorDefinitionClasses) {
List<Class<? extends ConstraintValidator<T, ?>>> genericValidatorClasses = newArrayList();

for ( Class<? extends ConstraintValidator<T, ?>> validatorClass : constraintValidatorDefinitionClasses ) {
if ( supportsValidationTarget( validatorClass, ValidationTarget.ANNOTATED_ELEMENT ) ) {
genericValidatorClasses.add( validatorClass );
}
}

return genericValidatorClasses;
}

private boolean supportsValidationTarget(Class<?> validatorClass, ValidationTarget target) {
SupportedValidationTarget supportedTargetAnnotation = validatorClass.getAnnotation(
SupportedValidationTarget.class
);

//by default constraints target the annotated element
if ( supportedTargetAnnotation == null ) {
return false;
return target == ValidationTarget.ANNOTATED_ELEMENT;
}
ValidationTarget[] targets = supportedTargetAnnotation.value();
for ( ValidationTarget configuredTarget : targets ) {
Expand Down
Expand Up @@ -27,8 +27,6 @@
import java.lang.reflect.TypeVariable;
import java.lang.reflect.WildcardType;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
Expand All @@ -39,6 +37,9 @@
import org.hibernate.validator.internal.util.logging.Log;
import org.hibernate.validator.internal.util.logging.LoggerFactory;

import static org.hibernate.validator.internal.util.CollectionHelper.newHashMap;
import static org.hibernate.validator.internal.util.CollectionHelper.newHashSet;

/**
* Provides utility methods for working with types.
*
Expand All @@ -51,7 +52,7 @@ public final class TypeHelper {
private static final Log log = LoggerFactory.make();

static {
Map<Class<?>, Set<Class<?>>> subtypesByPrimitive = new HashMap<Class<?>, Set<Class<?>>>();
Map<Class<?>, Set<Class<?>>> subtypesByPrimitive = newHashMap();

putPrimitiveSubtypes( subtypesByPrimitive, Void.TYPE );
putPrimitiveSubtypes( subtypesByPrimitive, Boolean.TYPE );
Expand Down Expand Up @@ -219,6 +220,7 @@ public static Type getArrayType(Type componentType) {
public static GenericArrayType genericArrayType(final Type componentType) {
return new GenericArrayType() {

@Override
public Type getGenericComponentType() {
return componentType;
}
Expand All @@ -241,14 +243,17 @@ public static boolean isInstance(Type type, Object object) {
*/
public static ParameterizedType parameterizedType(final Class<?> rawType, final Type... actualTypeArguments) {
return new ParameterizedType() {
@Override
public Type[] getActualTypeArguments() {
return actualTypeArguments;
}

@Override
public Type getRawType() {
return rawType;
}

@Override
public Type getOwnerType() {
return null;
}
Expand Down Expand Up @@ -291,7 +296,7 @@ public static Type[] getResolvedInterfaces(Type type) {
public static <T extends Annotation> Map<Type, Class<? extends ConstraintValidator<?, ?>>> getValidatorsTypes(
List<Class<? extends ConstraintValidator<T, ?>>> validators) {
Map<Type, Class<? extends ConstraintValidator<?, ?>>> validatorsTypes =
new HashMap<Type, Class<? extends ConstraintValidator<?, ?>>>();
newHashMap();
for ( Class<? extends ConstraintValidator<?, ?>> validator : validators ) {
validatorsTypes.put( extractType( validator ), validator );
}
Expand All @@ -300,7 +305,7 @@ public static Type[] getResolvedInterfaces(Type type) {
}

private static Type extractType(Class<? extends ConstraintValidator<?, ?>> validator) {
Map<Type, Type> resolvedTypes = new HashMap<Type, Type>();
Map<Type, Type> resolvedTypes = newHashMap();
Type constraintValidatorType = resolveTypes( resolvedTypes, validator );

//we now have all bind from a type to its resolution at one level
Expand Down Expand Up @@ -375,7 +380,7 @@ private static Type resolveTypeForClassAndHierarchy(Map<Type, Type> resolvedType

private static void putPrimitiveSubtypes(Map<Class<?>, Set<Class<?>>> subtypesByPrimitive, Class<?> primitiveType,
Class<?>... directSubtypes) {
Set<Class<?>> subtypes = new HashSet<Class<?>>();
Set<Class<?>> subtypes = newHashSet();

for ( Class<?> directSubtype : directSubtypes ) {
subtypes.add( directSubtype );
Expand Down
Expand Up @@ -33,6 +33,7 @@
import org.hibernate.validator.internal.constraintvalidators.NotNullValidator;
import org.hibernate.validator.internal.engine.constraintvalidation.ConstraintValidatorFactoryImpl;
import org.hibernate.validator.internal.engine.constraintvalidation.ConstraintValidatorManager;
import org.hibernate.validator.internal.metadata.descriptor.ConstraintDescriptorImpl;
import org.hibernate.validator.testutil.TestForIssue;

import static org.fest.assertions.Assertions.assertThat;
Expand Down Expand Up @@ -68,7 +69,7 @@ public void testGetDefaultConstraintValidatorFactory() {

@Test
public void testGetInitializedValidator() {
ConstraintDescriptor<?> constraintDescriptor = getConstraintDescriptorForProperty( "s1" );
ConstraintDescriptorImpl<?> constraintDescriptor = getConstraintDescriptorForProperty( "s1" );

ConstraintValidator<?, ?> constraintValidator = constraintValidatorManager.getInitializedValidator(
String.class,
Expand All @@ -81,7 +82,7 @@ public void testGetInitializedValidator() {

@Test(expectedExceptions = IllegalArgumentException.class)
public void testNullValidatedValueThrowsIllegalArgumentException() {
ConstraintDescriptor<?> constraintDescriptor = getConstraintDescriptorForProperty( "s1" );
ConstraintDescriptorImpl<?> constraintDescriptor = getConstraintDescriptorForProperty( "s1" );

constraintValidatorManager.getInitializedValidator(
null,
Expand All @@ -102,7 +103,7 @@ public void testNullDescriptorThrowsIllegalArgumentException() {

@Test(expectedExceptions = IllegalArgumentException.class)
public void testNullFactoryThrowsIllegalArgumentException() {
ConstraintDescriptor<?> constraintDescriptor = getConstraintDescriptorForProperty( "s1" );
ConstraintDescriptorImpl<?> constraintDescriptor = getConstraintDescriptorForProperty( "s1" );

constraintValidatorManager.getInitializedValidator(
String.class,
Expand All @@ -114,7 +115,7 @@ public void testNullFactoryThrowsIllegalArgumentException() {
@Test(expectedExceptions = UnexpectedTypeException.class,
expectedExceptionsMessageRegExp = "HV000030.*")
public void testUnexpectedTypeException() {
ConstraintDescriptor<?> constraintDescriptor = getConstraintDescriptorForProperty( "s2" );
ConstraintDescriptorImpl<?> constraintDescriptor = getConstraintDescriptorForProperty( "s2" );

constraintValidatorManager.getInitializedValidator(
Object.class,
Expand All @@ -125,7 +126,7 @@ public void testUnexpectedTypeException() {

@Test
public void testConstraintValidatorInstancesAreCachedPerFactory() {
ConstraintDescriptor<?> constraintDescriptor = getConstraintDescriptorForProperty( "s1" );
ConstraintDescriptorImpl<?> constraintDescriptor = getConstraintDescriptorForProperty( "s1" );

ConstraintValidator<?, ?> constraintValidator1 = constraintValidatorManager.getInitializedValidator(
String.class,
Expand Down Expand Up @@ -158,7 +159,7 @@ public void testConstraintValidatorInstancesAreCachedPerFactory() {

@Test
public void testOnlyTheInstancesForTheLeastRecentlyUsedCustomFactoryAreCached() {
ConstraintDescriptor<?> constraintDescriptor = getConstraintDescriptorForProperty( "s1" );
ConstraintDescriptorImpl<?> constraintDescriptor = getConstraintDescriptorForProperty( "s1" );

for ( int i = 0; i < 10; i++ ) {
constraintValidatorManager.getInitializedValidator(
Expand Down Expand Up @@ -193,10 +194,10 @@ public void testValidatorsAreCachedPerConstraint() {
.buildValidatorFactory()
.getValidator();

ConstraintDescriptor<?> notNullOnFirstNameDescriptor = getSingleConstraintDescriptorForProperty(
ConstraintDescriptorImpl<?> notNullOnFirstNameDescriptor = getSingleConstraintDescriptorForProperty(
validator, User.class, "firstName"
);
ConstraintDescriptor<?> notNullOnLastNameDescriptor = getSingleConstraintDescriptorForProperty(
ConstraintDescriptorImpl<?> notNullOnLastNameDescriptor = getSingleConstraintDescriptorForProperty(
validator, User.class, "lastName"
);

Expand Down Expand Up @@ -226,13 +227,13 @@ public void testValidatorsAreCachedPerConstraintAndAnnotationMembers() {
.buildValidatorFactory()
.getValidator();

ConstraintDescriptor<?> sizeOnMiddleNameDescriptor = getSingleConstraintDescriptorForProperty(
ConstraintDescriptorImpl<?> sizeOnMiddleNameDescriptor = getSingleConstraintDescriptorForProperty(
validator, User.class, "middleName"
);
ConstraintDescriptor<?> sizeOnAddress1Descriptor = getSingleConstraintDescriptorForProperty(
ConstraintDescriptorImpl<?> sizeOnAddress1Descriptor = getSingleConstraintDescriptorForProperty(
validator, User.class, "address1"
);
ConstraintDescriptor<?> sizeOnAddress2Descriptor = getSingleConstraintDescriptorForProperty(
ConstraintDescriptorImpl<?> sizeOnAddress2Descriptor = getSingleConstraintDescriptorForProperty(
validator, User.class, "address2"
);

Expand All @@ -250,11 +251,11 @@ public void testValidatorsAreCachedPerConstraintAndAnnotationMembers() {
assertThat( sizeValidatorForAddress1 ).isSameAs( sizeValidatorForAddress2 );
}

private ConstraintDescriptor<?> getConstraintDescriptorForProperty(String propertyName) {
private ConstraintDescriptorImpl<?> getConstraintDescriptorForProperty(String propertyName) {
return getSingleConstraintDescriptorForProperty( validator, Foo.class, propertyName );
}

private ConstraintDescriptor<?> getSingleConstraintDescriptorForProperty(Validator validator, Class<?> clazz, String propertyName) {
private ConstraintDescriptorImpl<?> getSingleConstraintDescriptorForProperty(Validator validator, Class<?> clazz, String propertyName) {
BeanDescriptor beanDescriptor = validator.getConstraintsForClass( clazz );
PropertyDescriptor propertyDescriptor = beanDescriptor.getConstraintsForProperty(
propertyName
Expand All @@ -265,7 +266,7 @@ private ConstraintDescriptor<?> getSingleConstraintDescriptorForProperty(Validat
1,
"There should be only one constraint descriptor"
);
return constraintDescriptorSet.iterator().next();
return (ConstraintDescriptorImpl<?>) constraintDescriptorSet.iterator().next();
}

public class Foo {
Expand All @@ -290,6 +291,7 @@ public MyCustomValidatorFactory() {

@Override
public void releaseInstance(ConstraintValidator<?, ?> instance) {
delegate.releaseInstance( instance ); }
delegate.releaseInstance( instance );
}
}
}

0 comments on commit d745fc7

Please sign in to comment.