/*
* Hibernate Validator, declare and validate application constraints
*
* License: Apache License, Version 2.0
* See the license.txt file in the root directory or <http://www.apache.org/licenses/LICENSE-2.0>.
*/
package org.hibernate.validator.test.internal.engine.constraintvalidation;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
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 org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
import org.hibernate.validator.HibernateValidatorConfiguration;
import org.hibernate.validator.cfg.ConstraintMapping;
import org.hibernate.validator.cfg.GenericConstraintDef;
import org.hibernate.validator.testutil.TestForIssue;
import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
import static java.lang.annotation.ElementType.TYPE;
import static org.hibernate.validator.testutil.ConstraintViolationAssert.assertCorrectConstraintViolationMessages;
import static org.hibernate.validator.testutil.ConstraintViolationAssert.assertNumberOfViolations;
import static org.hibernate.validator.testutils.ValidatorUtil.getConfiguration;
/**
* @author Hardy Ferentschik
* @author Gunnar Morling
*/
public class ConstraintValidatorResolutionTest {
private HibernateValidatorConfiguration configuration;
private ConstraintMapping constraintMapping;
@BeforeMethod
public void setupMapping() {
configuration = getConfiguration();
constraintMapping = configuration.createConstraintMapping();
configuration.addMapping( constraintMapping );
}
@Test
@TestForIssue(jiraKey = "HV-623")
public void validatorForParametrizedTypeIsCorrectlyResolved() {
//given
constraintMapping.type( Value.class )
.constraint(
new GenericConstraintDef<ConstraintWithParametrizedValidator>(
ConstraintWithParametrizedValidator.class
)
);
Validator validator = configuration.buildValidatorFactory().getValidator();
//when
Set<ConstraintViolation<Value<Integer>>> constraintViolations = validator.validate( new Value<Integer>() );
//then
assertNumberOfViolations( constraintViolations, 1 );
}
@Test
@TestForIssue(jiraKey = "HV-623")
public void validatorForRawTypeIsCorrectlyResolved() {
//given
constraintMapping.type( Value.class )
.constraint( new GenericConstraintDef<ConstraintWithRawValidator>( ConstraintWithRawValidator.class ) );
Validator validator = configuration.buildValidatorFactory().getValidator();
//when
Set<ConstraintViolation<Value<Integer>>> constraintViolations = validator.validate( new Value<Integer>() );
//then
assertNumberOfViolations( constraintViolations, 1 );
}
/**
* As per the JLS, {@code Value<T>} is a sub-type of of the raw type
* {@code Value}. Therefore
* {@link ParametrizedValidatorForConstraintWithRawAndParametrizedValidator}
* is more specific than
* {@link RawValidatorForConstraintWithRawAndParametrizedValidator}.
*
* @see <a href="https://docs.oracle.com/javase/specs/jls/se8/html/jls-4.html#jls-4.10.2">JLS</a> (subtyping)
* @see <a href="http://beanvalidation.org/2.0/spec/#typevalidatorresolution">BV spec</a> (constraint validator resolution)
*/
@Test
@TestForIssue(jiraKey = "HV-623")
public void parametrizedValidatorHasPrecedenceOverRawValidator() {
//given
constraintMapping.type( Value.class )
.constraint(
new GenericConstraintDef<ConstraintWithRawAndParametrizedValidator>(
ConstraintWithRawAndParametrizedValidator.class
)
);
Validator validator = configuration.buildValidatorFactory().getValidator();
//when
Set<ConstraintViolation<Value<Integer>>> constraintViolations = validator.validate( new Value<Integer>() );
//then
assertNumberOfViolations( constraintViolations, 1 );
assertCorrectConstraintViolationMessages(
constraintViolations,
"ParametrizedValidatorForConstraintWithRawAndParametrizedValidator"
);
}
public class Value<T> {
}
@Target({ TYPE, ANNOTATION_TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = ParametrizedValidator.class)
@Documented
public @interface ConstraintWithParametrizedValidator {
String message() default "foo";
Class<?>[] groups() default { };
Class<? extends Payload>[] payload() default { };
}
public static class ParametrizedValidator
implements ConstraintValidator<ConstraintWithParametrizedValidator, Value<?>> {
@Override
public boolean isValid(Value<?> settingValue, ConstraintValidatorContext context) {
return false;
}
}
@Target({ TYPE, ANNOTATION_TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = RawValidator.class)
@Documented
public @interface ConstraintWithRawValidator {
String message() default "foo";
Class<?>[] groups() default { };
Class<? extends Payload>[] payload() default { };
}
@SuppressWarnings("rawtypes")
public static class RawValidator implements ConstraintValidator<ConstraintWithRawValidator, Value> {
@Override
public boolean isValid(Value value, ConstraintValidatorContext context) {
return false;
}
}
@Target({ TYPE, ANNOTATION_TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = {
RawValidatorForConstraintWithRawAndParametrizedValidator.class,
ParametrizedValidatorForConstraintWithRawAndParametrizedValidator.class
})
@Documented
public @interface ConstraintWithRawAndParametrizedValidator {
String message() default "foo";
Class<?>[] groups() default { };
Class<? extends Payload>[] payload() default { };
}
public static class ParametrizedValidatorForConstraintWithRawAndParametrizedValidator
implements ConstraintValidator<ConstraintWithRawAndParametrizedValidator, Value<?>> {
@Override
public boolean isValid(Value<?> value, ConstraintValidatorContext context) {
context.disableDefaultConstraintViolation();
context.buildConstraintViolationWithTemplate(
"ParametrizedValidatorForConstraintWithRawAndParametrizedValidator"
).addConstraintViolation();
return false;
}
}
@SuppressWarnings("rawtypes")
public static class RawValidatorForConstraintWithRawAndParametrizedValidator
implements ConstraintValidator<ConstraintWithRawAndParametrizedValidator, Value> {
@Override
public boolean isValid(Value value, ConstraintValidatorContext context) {
context.disableDefaultConstraintViolation();
context.buildConstraintViolationWithTemplate( "RawValidatorForConstraintWithRawAndParametrizedValidator" )
.addConstraintViolation();
return false;
}
}
}