From 32f6e73e5a6bfed92ea0c4022a0ea9fdce55cc65 Mon Sep 17 00:00:00 2001 From: Hilmerc Date: Wed, 10 Jan 2018 10:51:39 -0500 Subject: [PATCH 1/8] HV-1552 Adding new MinAge Constraint --- .../validator/constraints/MinAge.java | 66 ++++++++++++++++++ .../hv/MinAgeValidator.java | 45 ++++++++++++ .../metadata/core/ConstraintHelper.java | 4 ++ .../validator/ValidationMessages.properties | 3 +- .../hv/AgeValidatorTest.java | 69 +++++++++++++++++++ 5 files changed, 186 insertions(+), 1 deletion(-) create mode 100644 engine/src/main/java/org/hibernate/validator/constraints/MinAge.java create mode 100644 engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/hv/MinAgeValidator.java create mode 100644 engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/hv/AgeValidatorTest.java diff --git a/engine/src/main/java/org/hibernate/validator/constraints/MinAge.java b/engine/src/main/java/org/hibernate/validator/constraints/MinAge.java new file mode 100644 index 0000000000..e7430bc088 --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/constraints/MinAge.java @@ -0,0 +1,66 @@ +/* + * Hibernate Validator, declare and validate application constraints + * + * License: Apache License, Version 2.0 + * See the license.txt file in the root directory or . + */ +package org.hibernate.validator.constraints; + +import javax.validation.Constraint; +import javax.validation.Payload; +import java.lang.annotation.Documented; +import java.lang.annotation.Repeatable; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + + +import static java.lang.annotation.ElementType.ANNOTATION_TYPE; +import static java.lang.annotation.ElementType.CONSTRUCTOR; +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.PARAMETER; +import static java.lang.annotation.ElementType.TYPE_USE; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +/** + * The annotated element must be a date where the number of years go by to today must be + * greater or equal to the specified value + *

+ *

+ * The supported type is {@code LocalDate}. {@code null} is considered valid. + *

+ * + * @author Hillmer Chona + * @since 6.0.x + */ + +@Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE}) +@Retention(RUNTIME) +@Repeatable(MinAge.List.class) +@Documented +@Constraint(validatedBy = {}) +public @interface MinAge { + + String message() default "{org.hibernate.validator.constraints.MinAge.message}"; + + Class[] groups() default {}; + + Class[] payload() default {}; + + /** + * @return value the age must be greater or equal to + */ + int value(); + + /** + * Defines several {@link MinAge} annotations on the same element. + * + * @see MinAge + */ + @Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE}) + @Retention(RUNTIME) + @Documented + @interface List { + MinAge[] value(); + } +} diff --git a/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/hv/MinAgeValidator.java b/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/hv/MinAgeValidator.java new file mode 100644 index 0000000000..4e00cbf14f --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/hv/MinAgeValidator.java @@ -0,0 +1,45 @@ +/* + * Hibernate Validator, declare and validate application constraints + * + * License: Apache License, Version 2.0 + * See the license.txt file in the root directory or . + */ +package org.hibernate.validator.internal.constraintvalidators.hv; + +import org.hibernate.validator.constraints.MinAge; + +import javax.validation.ConstraintValidator; +import javax.validation.ConstraintValidatorContext; + +import java.time.LocalDate; +import java.time.temporal.ChronoUnit; + +/** + * + * Checks that the number of years from a given date to today is greater or equal to + * a specified value + * + * @author Hillmer Chona + * @since 6.0.x + */ + + +public class MinAgeValidator implements ConstraintValidator { + + private long minAge; + + @Override + public void initialize(MinAge minAgeValue) { + this.minAge = minAgeValue.value(); + } + + @Override + public boolean isValid(LocalDate date, ConstraintValidatorContext constraintValidatorContext) { + // null values are valid + if ( date == null ) { + return true; + } + + return ChronoUnit.YEARS.between( date, LocalDate.now() ) >= minAge; + } +} diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/core/ConstraintHelper.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/core/ConstraintHelper.java index 9d0ca63446..b61e730392 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/metadata/core/ConstraintHelper.java +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/core/ConstraintHelper.java @@ -60,6 +60,7 @@ import org.hibernate.validator.constraints.ISBN; import org.hibernate.validator.constraints.Length; import org.hibernate.validator.constraints.LuhnCheck; +import org.hibernate.validator.constraints.MinAge; import org.hibernate.validator.constraints.Mod10Check; import org.hibernate.validator.constraints.Mod11Check; import org.hibernate.validator.constraints.ModCheck; @@ -258,6 +259,7 @@ import org.hibernate.validator.internal.constraintvalidators.hv.ISBNValidator; import org.hibernate.validator.internal.constraintvalidators.hv.LengthValidator; import org.hibernate.validator.internal.constraintvalidators.hv.LuhnCheckValidator; +import org.hibernate.validator.internal.constraintvalidators.hv.MinAgeValidator; import org.hibernate.validator.internal.constraintvalidators.hv.Mod10CheckValidator; import org.hibernate.validator.internal.constraintvalidators.hv.Mod11CheckValidator; import org.hibernate.validator.internal.constraintvalidators.hv.ModCheckValidator; @@ -462,6 +464,8 @@ public ConstraintHelper() { ) ); } + putConstraint( tmpConstraints, MinAge.class, MinAgeValidator.class ); + if ( isJavaMoneyInClasspath() ) { putConstraints( tmpConstraints, Negative.class, Arrays.asList( NegativeValidatorForBigDecimal.class, diff --git a/engine/src/main/resources/org/hibernate/validator/ValidationMessages.properties b/engine/src/main/resources/org/hibernate/validator/ValidationMessages.properties index c9d3ac8536..62e81c5700 100644 --- a/engine/src/main/resources/org/hibernate/validator/ValidationMessages.properties +++ b/engine/src/main/resources/org/hibernate/validator/ValidationMessages.properties @@ -29,6 +29,7 @@ org.hibernate.validator.constraints.ISBN.message = invalid IS org.hibernate.validator.constraints.Length.message = length must be between {min} and {max} org.hibernate.validator.constraints.CodePointLength.message = length must be between {min} and {max} org.hibernate.validator.constraints.LuhnCheck.message = The check digit for ${validatedValue} is invalid, Luhn Modulo 10 checksum failed +org.hibernate.validator.constraints.MinAge.message = must be older than {value} org.hibernate.validator.constraints.Mod10Check.message = The check digit for ${validatedValue} is invalid, Modulo 10 checksum failed org.hibernate.validator.constraints.Mod11Check.message = The check digit for ${validatedValue} is invalid, Modulo 11 checksum failed org.hibernate.validator.constraints.ModCheck.message = The check digit for ${validatedValue} is invalid, ${modType} checksum failed @@ -50,4 +51,4 @@ org.hibernate.validator.constraints.pl.NIP.message = Invalid VA org.hibernate.validator.constraints.pl.PESEL.message = Invalid Polish National Identification Number (PESEL) org.hibernate.validator.constraints.time.DurationMax.message = must be shorter than${inclusive == true ? ' or equal to' : ''}${days == 0 ? '' : days == 1 ? ' 1 day' : ' ' += days += ' days'}${hours == 0 ? '' : hours == 1 ? ' 1 hour' : ' ' += hours += ' hours'}${minutes == 0 ? '' : minutes == 1 ? ' 1 minute' : ' ' += minutes += ' minutes'}${seconds == 0 ? '' : seconds == 1 ? ' 1 second' : ' ' += seconds += ' seconds'}${millis == 0 ? '' : millis == 1 ? ' 1 milli' : ' ' += millis += ' millis'}${nanos == 0 ? '' : nanos == 1 ? ' 1 nano' : ' ' += nanos += ' nanos'} -org.hibernate.validator.constraints.time.DurationMin.message = must be longer than${inclusive == true ? ' or equal to' : ''}${days == 0 ? '' : days == 1 ? ' 1 day' : ' ' += days += ' days'}${hours == 0 ? '' : hours == 1 ? ' 1 hour' : ' ' += hours += ' hours'}${minutes == 0 ? '' : minutes == 1 ? ' 1 minute' : ' ' += minutes += ' minutes'}${seconds == 0 ? '' : seconds == 1 ? ' 1 second' : ' ' += seconds += ' seconds'}${millis == 0 ? '' : millis == 1 ? ' 1 milli' : ' ' += millis += ' millis'}${nanos == 0 ? '' : nanos == 1 ? ' 1 nano' : ' ' += nanos += ' nanos'} +org.hibernate.validator.constraints.time.DurationMin.message = must be longer than${inclusive == true ? ' or equal to' : ''}${days == 0 ? '' : days == 1 ? ' 1 day' : ' ' += days += ' days'}${hours == 0 ? '' : hours == 1 ? ' 1 hour' : ' ' += hours += ' hours'}${minutes == 0 ? '' : minutes == 1 ? ' 1 minute' : ' ' += minutes += ' minutes'}${seconds == 0 ? '' : seconds == 1 ? ' 1 second' : ' ' += seconds += ' seconds'}${millis == 0 ? '' : millis == 1 ? ' 1 milli' : ' ' += millis += ' millis'}${nanos == 0 ? '' : nanos == 1 ? ' 1 nano' : ' ' += nanos += ' nanos'} \ No newline at end of file diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/hv/AgeValidatorTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/hv/AgeValidatorTest.java new file mode 100644 index 0000000000..19f8068635 --- /dev/null +++ b/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/hv/AgeValidatorTest.java @@ -0,0 +1,69 @@ +/* + * Hibernate Validator, declare and validate application constraints + * + * License: Apache License, Version 2.0 + * See the license.txt file in the root directory or . + */ +package org.hibernate.validator.test.internal.constraintvalidators.hv; + +import org.hibernate.validator.constraints.MinAge; +import org.hibernate.validator.internal.constraintvalidators.hv.MinAgeValidator; +import org.hibernate.validator.internal.util.annotation.ConstraintAnnotationDescriptor; +import org.hibernate.validator.testutil.TestForIssue; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; + +import java.time.LocalDate; + +import static org.testng.Assert.assertFalse; +import static org.testng.Assert.assertTrue; + + +/** + * A set of tests for {@link MinAge} constraint validator ({@link MinAgeValidator}), which + * make sure that validation is performed correctly. + * + * @author Hillmer Chona + * @since 6.0.x + */ +@TestForIssue(jiraKey = "HV-1552" ) +public class AgeValidatorTest { + private MinAgeValidator ageValidator; + + private int value = 18; + + @BeforeMethod + public void setUp() throws Exception { + + ConstraintAnnotationDescriptor.Builder descriptorBuilder = new ConstraintAnnotationDescriptor.Builder<>( MinAge.class ); + descriptorBuilder.setAttribute( "value" , value ); + descriptorBuilder.setMessage( "must be older" ); + + ageValidator = new MinAgeValidator(); + ageValidator.initialize( descriptorBuilder.build().getAnnotation() ); + } + + + @Test + public void validDate() throws Exception { + + assertValidAge( null ); + + LocalDate todayMinus18Years = LocalDate.now().minusYears( 18 ); + LocalDate todayMinus2MonthAnd18Years = LocalDate.now().minusMonths( 2 ).minusYears( 18 ); + LocalDate tomorrowMinus18Years = LocalDate.now().plusDays( 1 ).minusYears( 18 ); + + assertValidAge( todayMinus18Years ); + assertValidAge( todayMinus2MonthAnd18Years ); + assertInvalidAge( tomorrowMinus18Years ); + + } + + private void assertValidAge(LocalDate birthDate) { + assertTrue( ageValidator.isValid( birthDate, null ), birthDate + " should be a date equal or more than " + value + " years before today" ); + } + + private void assertInvalidAge(LocalDate birthDate) { + assertFalse( ageValidator.isValid( birthDate, null ), birthDate + " should be a date less than " + value + " years before today" ); + } +} From 0c450ec3153bab0d34fdb70db99fef2180220e4c Mon Sep 17 00:00:00 2001 From: Hilmerc Date: Mon, 15 Jan 2018 17:03:52 -0500 Subject: [PATCH 2/8] HV-1552 Adding new MinAge Constraint --- .../constraints/{MinAge.java => AgeMin.java} | 26 ++-- .../hv/AgeMinValidator.java | 73 ++++++++++++ .../hv/MinAgeValidator.java | 45 ------- .../metadata/core/ConstraintHelper.java | 6 +- .../validator/ValidationMessages.properties | 4 +- .../hv/AgeValidatorTest.java | 111 +++++++++++++----- 6 files changed, 179 insertions(+), 86 deletions(-) rename engine/src/main/java/org/hibernate/validator/constraints/{MinAge.java => AgeMin.java} (72%) create mode 100644 engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/hv/AgeMinValidator.java delete mode 100644 engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/hv/MinAgeValidator.java diff --git a/engine/src/main/java/org/hibernate/validator/constraints/MinAge.java b/engine/src/main/java/org/hibernate/validator/constraints/AgeMin.java similarity index 72% rename from engine/src/main/java/org/hibernate/validator/constraints/MinAge.java rename to engine/src/main/java/org/hibernate/validator/constraints/AgeMin.java index e7430bc088..6ec99055fd 100644 --- a/engine/src/main/java/org/hibernate/validator/constraints/MinAge.java +++ b/engine/src/main/java/org/hibernate/validator/constraints/AgeMin.java @@ -31,36 +31,44 @@ *

* * @author Hillmer Chona - * @since 6.0.x + * @since 6.0.8 */ - @Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE}) @Retention(RUNTIME) -@Repeatable(MinAge.List.class) +@Repeatable(AgeMin.List.class) @Documented @Constraint(validatedBy = {}) -public @interface MinAge { +public @interface AgeMin { - String message() default "{org.hibernate.validator.constraints.MinAge.message}"; + String message() default "{org.hibernate.validator.constraints.AgeMin.message}"; Class[] groups() default {}; Class[] payload() default {}; /** - * @return value the age must be greater or equal to + * @return value the age in years from a given date must be greater or equal to */ int value(); /** - * Defines several {@link MinAge} annotations on the same element. + * Specifies whether the specified value is inclusive or exclusive. + * By default, it is inclusive. + * + * @return {@code true} if the number of years from a given date must be higher or equal to the specified value, + * {@code false} if the number of years from a given date must be higher + */ + boolean inclusive() default true; + + /** + * Defines several {@link AgeMin} annotations on the same element. * - * @see MinAge + * @see AgeMin */ @Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE}) @Retention(RUNTIME) @Documented @interface List { - MinAge[] value(); + AgeMin[] value(); } } diff --git a/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/hv/AgeMinValidator.java b/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/hv/AgeMinValidator.java new file mode 100644 index 0000000000..fd9568d765 --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/hv/AgeMinValidator.java @@ -0,0 +1,73 @@ +/* + * Hibernate Validator, declare and validate application constraints + * + * License: Apache License, Version 2.0 + * See the license.txt file in the root directory or . + */ +package org.hibernate.validator.internal.constraintvalidators.hv; + +import org.hibernate.validator.constraints.AgeMin; +import org.hibernate.validator.constraintvalidation.HibernateConstraintValidator; +import org.hibernate.validator.constraintvalidation.HibernateConstraintValidatorInitializationContext; +import org.hibernate.validator.internal.util.logging.Log; +import org.hibernate.validator.internal.util.logging.LoggerFactory; + +import javax.validation.ConstraintValidatorContext; +import javax.validation.metadata.ConstraintDescriptor; + +import java.lang.invoke.MethodHandles; +import java.time.Clock; +import java.time.Duration; +import java.time.LocalDate; +import java.time.temporal.ChronoUnit; + +/** + * + * Checks that the number of years from a given date to today is greater or equal to + * a specified value + * + * @author Hillmer Chona + */ +public class AgeMinValidator implements HibernateConstraintValidator { + + private static final Log LOG = LoggerFactory.make( MethodHandles.lookup() ); + + private int minAge; + + private boolean inclusive; + + protected Clock referenceClock; + + @Override + public void initialize(ConstraintDescriptor constraintDescriptor, HibernateConstraintValidatorInitializationContext initializationContext) { + try { + this.referenceClock = Clock.offset( + initializationContext.getClockProvider().getClock(), + getEffectiveTemporalValidationTolerance( initializationContext.getTemporalValidationTolerance() ) + ); + this.minAge = constraintDescriptor.getAnnotation().value(); + this.inclusive = constraintDescriptor.getAnnotation().inclusive(); + + } + catch (Exception e) { + throw LOG.getUnableToGetCurrentTimeFromClockProvider( e ); + } + } + + @Override + public boolean isValid(LocalDate date, ConstraintValidatorContext constraintValidatorContext) { + // null values are valid + if ( date == null ) { + return true; + } + return inclusive ? ChronoUnit.YEARS.between( date, getReferenceValue( referenceClock ) ) >= minAge : ChronoUnit.YEARS.between( date, getReferenceValue( referenceClock ) ) > minAge; + } + + private LocalDate getReferenceValue(Clock reference) { + return LocalDate.now( reference ); + } + + protected Duration getEffectiveTemporalValidationTolerance(Duration absoluteTemporalValidationTolerance) { + return absoluteTemporalValidationTolerance.negated(); + } +} diff --git a/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/hv/MinAgeValidator.java b/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/hv/MinAgeValidator.java deleted file mode 100644 index 4e00cbf14f..0000000000 --- a/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/hv/MinAgeValidator.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Hibernate Validator, declare and validate application constraints - * - * License: Apache License, Version 2.0 - * See the license.txt file in the root directory or . - */ -package org.hibernate.validator.internal.constraintvalidators.hv; - -import org.hibernate.validator.constraints.MinAge; - -import javax.validation.ConstraintValidator; -import javax.validation.ConstraintValidatorContext; - -import java.time.LocalDate; -import java.time.temporal.ChronoUnit; - -/** - * - * Checks that the number of years from a given date to today is greater or equal to - * a specified value - * - * @author Hillmer Chona - * @since 6.0.x - */ - - -public class MinAgeValidator implements ConstraintValidator { - - private long minAge; - - @Override - public void initialize(MinAge minAgeValue) { - this.minAge = minAgeValue.value(); - } - - @Override - public boolean isValid(LocalDate date, ConstraintValidatorContext constraintValidatorContext) { - // null values are valid - if ( date == null ) { - return true; - } - - return ChronoUnit.YEARS.between( date, LocalDate.now() ) >= minAge; - } -} diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/core/ConstraintHelper.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/core/ConstraintHelper.java index b61e730392..7e99a90e40 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/metadata/core/ConstraintHelper.java +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/core/ConstraintHelper.java @@ -60,7 +60,7 @@ import org.hibernate.validator.constraints.ISBN; import org.hibernate.validator.constraints.Length; import org.hibernate.validator.constraints.LuhnCheck; -import org.hibernate.validator.constraints.MinAge; +import org.hibernate.validator.constraints.AgeMin; import org.hibernate.validator.constraints.Mod10Check; import org.hibernate.validator.constraints.Mod11Check; import org.hibernate.validator.constraints.ModCheck; @@ -259,7 +259,7 @@ import org.hibernate.validator.internal.constraintvalidators.hv.ISBNValidator; import org.hibernate.validator.internal.constraintvalidators.hv.LengthValidator; import org.hibernate.validator.internal.constraintvalidators.hv.LuhnCheckValidator; -import org.hibernate.validator.internal.constraintvalidators.hv.MinAgeValidator; +import org.hibernate.validator.internal.constraintvalidators.hv.AgeMinValidator; import org.hibernate.validator.internal.constraintvalidators.hv.Mod10CheckValidator; import org.hibernate.validator.internal.constraintvalidators.hv.Mod11CheckValidator; import org.hibernate.validator.internal.constraintvalidators.hv.ModCheckValidator; @@ -464,7 +464,7 @@ public ConstraintHelper() { ) ); } - putConstraint( tmpConstraints, MinAge.class, MinAgeValidator.class ); + putConstraint( tmpConstraints, AgeMin.class, AgeMinValidator.class ); if ( isJavaMoneyInClasspath() ) { putConstraints( tmpConstraints, Negative.class, Arrays.asList( diff --git a/engine/src/main/resources/org/hibernate/validator/ValidationMessages.properties b/engine/src/main/resources/org/hibernate/validator/ValidationMessages.properties index 62e81c5700..1a062109e4 100644 --- a/engine/src/main/resources/org/hibernate/validator/ValidationMessages.properties +++ b/engine/src/main/resources/org/hibernate/validator/ValidationMessages.properties @@ -29,7 +29,7 @@ org.hibernate.validator.constraints.ISBN.message = invalid IS org.hibernate.validator.constraints.Length.message = length must be between {min} and {max} org.hibernate.validator.constraints.CodePointLength.message = length must be between {min} and {max} org.hibernate.validator.constraints.LuhnCheck.message = The check digit for ${validatedValue} is invalid, Luhn Modulo 10 checksum failed -org.hibernate.validator.constraints.MinAge.message = must be older than {value} +org.hibernate.validator.constraints.AgeMin.message = must be older than {value} org.hibernate.validator.constraints.Mod10Check.message = The check digit for ${validatedValue} is invalid, Modulo 10 checksum failed org.hibernate.validator.constraints.Mod11Check.message = The check digit for ${validatedValue} is invalid, Modulo 11 checksum failed org.hibernate.validator.constraints.ModCheck.message = The check digit for ${validatedValue} is invalid, ${modType} checksum failed @@ -51,4 +51,4 @@ org.hibernate.validator.constraints.pl.NIP.message = Invalid VA org.hibernate.validator.constraints.pl.PESEL.message = Invalid Polish National Identification Number (PESEL) org.hibernate.validator.constraints.time.DurationMax.message = must be shorter than${inclusive == true ? ' or equal to' : ''}${days == 0 ? '' : days == 1 ? ' 1 day' : ' ' += days += ' days'}${hours == 0 ? '' : hours == 1 ? ' 1 hour' : ' ' += hours += ' hours'}${minutes == 0 ? '' : minutes == 1 ? ' 1 minute' : ' ' += minutes += ' minutes'}${seconds == 0 ? '' : seconds == 1 ? ' 1 second' : ' ' += seconds += ' seconds'}${millis == 0 ? '' : millis == 1 ? ' 1 milli' : ' ' += millis += ' millis'}${nanos == 0 ? '' : nanos == 1 ? ' 1 nano' : ' ' += nanos += ' nanos'} -org.hibernate.validator.constraints.time.DurationMin.message = must be longer than${inclusive == true ? ' or equal to' : ''}${days == 0 ? '' : days == 1 ? ' 1 day' : ' ' += days += ' days'}${hours == 0 ? '' : hours == 1 ? ' 1 hour' : ' ' += hours += ' hours'}${minutes == 0 ? '' : minutes == 1 ? ' 1 minute' : ' ' += minutes += ' minutes'}${seconds == 0 ? '' : seconds == 1 ? ' 1 second' : ' ' += seconds += ' seconds'}${millis == 0 ? '' : millis == 1 ? ' 1 milli' : ' ' += millis += ' millis'}${nanos == 0 ? '' : nanos == 1 ? ' 1 nano' : ' ' += nanos += ' nanos'} \ No newline at end of file +org.hibernate.validator.constraints.time.DurationMin.message = must be longer than${inclusive == true ? ' or equal to' : ''}${days == 0 ? '' : days == 1 ? ' 1 day' : ' ' += days += ' days'}${hours == 0 ? '' : hours == 1 ? ' 1 hour' : ' ' += hours += ' hours'}${minutes == 0 ? '' : minutes == 1 ? ' 1 minute' : ' ' += minutes += ' minutes'}${seconds == 0 ? '' : seconds == 1 ? ' 1 second' : ' ' += seconds += ' seconds'}${millis == 0 ? '' : millis == 1 ? ' 1 milli' : ' ' += millis += ' millis'}${nanos == 0 ? '' : nanos == 1 ? ' 1 nano' : ' ' += nanos += ' nanos'} diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/hv/AgeValidatorTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/hv/AgeValidatorTest.java index 19f8068635..c848df7253 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/hv/AgeValidatorTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/hv/AgeValidatorTest.java @@ -6,64 +6,121 @@ */ package org.hibernate.validator.test.internal.constraintvalidators.hv; -import org.hibernate.validator.constraints.MinAge; -import org.hibernate.validator.internal.constraintvalidators.hv.MinAgeValidator; +import static java.lang.annotation.ElementType.FIELD; +import static org.hibernate.validator.testutil.ConstraintViolationAssert.assertNoViolations; +import static org.hibernate.validator.testutil.ConstraintViolationAssert.assertThat; +import static org.hibernate.validator.testutil.ConstraintViolationAssert.violationOf; +import static org.hibernate.validator.testutils.ValidatorUtil.getConfiguration; +import static org.testng.Assert.assertFalse; +import static org.testng.Assert.assertTrue; + +import java.util.Set; + +import javax.validation.ConstraintValidator; +import javax.validation.ConstraintViolation; +import javax.validation.Validator; + +import org.hibernate.validator.HibernateValidator; +import org.hibernate.validator.HibernateValidatorConfiguration; +import org.hibernate.validator.cfg.ConstraintMapping; +import org.hibernate.validator.cfg.defs.AgeMinDef; +import org.hibernate.validator.constraints.AgeMin; +import org.hibernate.validator.constraintvalidation.HibernateConstraintValidator; +import org.hibernate.validator.internal.constraintvalidators.hv.AgeMinValidator; import org.hibernate.validator.internal.util.annotation.ConstraintAnnotationDescriptor; import org.hibernate.validator.testutil.TestForIssue; + +import org.hibernate.validator.testutils.ConstraintValidatorInitializationHelper; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import java.time.LocalDate; -import static org.testng.Assert.assertFalse; -import static org.testng.Assert.assertTrue; - - /** - * A set of tests for {@link MinAge} constraint validator ({@link MinAgeValidator}), which + * A set of tests for {@link AgeMin} constraint validator ({@link AgeMinValidator}), which * make sure that validation is performed correctly. * * @author Hillmer Chona - * @since 6.0.x + * @since 6.0.8 */ @TestForIssue(jiraKey = "HV-1552" ) public class AgeValidatorTest { - private MinAgeValidator ageValidator; private int value = 18; - @BeforeMethod - public void setUp() throws Exception { - - ConstraintAnnotationDescriptor.Builder descriptorBuilder = new ConstraintAnnotationDescriptor.Builder<>( MinAge.class ); - descriptorBuilder.setAttribute( "value" , value ); - descriptorBuilder.setMessage( "must be older" ); + /** + * @return an initialized {@link ConstraintValidator} using {@code DUMMY_CONSTRAINT_VALIDATOR_INITIALIZATION_CONTEXT} + */ + private ConstraintValidator getInitializedValidator(int value, boolean inclusive) { + HibernateConstraintValidator validator = new AgeMinValidator(); + ConstraintAnnotationDescriptor.Builder descriptorBuilder = new ConstraintAnnotationDescriptor.Builder<>( AgeMin.class ); + descriptorBuilder.setAttribute( "value", value ); + descriptorBuilder.setAttribute( "inclusive", inclusive ); + ConstraintAnnotationDescriptor descriptor = descriptorBuilder.build(); + ConstraintValidatorInitializationHelper.initialize( validator, descriptor ); + return validator; + } - ageValidator = new MinAgeValidator(); - ageValidator.initialize( descriptorBuilder.build().getAnnotation() ); + private void assertValidAge(LocalDate birthDate, ConstraintValidator constraintValidator) { + assertTrue( constraintValidator.isValid( birthDate, null ), birthDate + " should be a date equal or more than " + value + " years before today" ); } + private void assertInvalidAge(LocalDate birthDate, ConstraintValidator constraintValidator) { + assertFalse( constraintValidator.isValid( birthDate, null ), birthDate + " should be a date less than " + value + " years before today" ); + } @Test - public void validDate() throws Exception { - - assertValidAge( null ); + public void testLocalDate() throws Exception { + ConstraintValidator constraintValidator = getInitializedValidator( value, true ); LocalDate todayMinus18Years = LocalDate.now().minusYears( 18 ); LocalDate todayMinus2MonthAnd18Years = LocalDate.now().minusMonths( 2 ).minusYears( 18 ); LocalDate tomorrowMinus18Years = LocalDate.now().plusDays( 1 ).minusYears( 18 ); - assertValidAge( todayMinus18Years ); - assertValidAge( todayMinus2MonthAnd18Years ); - assertInvalidAge( tomorrowMinus18Years ); + assertValidAge( null , constraintValidator ); + assertValidAge( todayMinus18Years, constraintValidator ); + assertValidAge( todayMinus2MonthAnd18Years, constraintValidator ); + assertInvalidAge( tomorrowMinus18Years, constraintValidator ); + } + + @Test + public void testInclusiveLocalDate() throws Exception { + ConstraintValidator constraintValidatorInclusiveTrue = getInitializedValidator( value, true ); + ConstraintValidator constraintValidatorInclusiveFalse = getInitializedValidator( value, false ); + LocalDate todayMinus18Years = LocalDate.now().minusYears( 18 ); + + assertValidAge( todayMinus18Years, constraintValidatorInclusiveTrue ); + assertInvalidAge( todayMinus18Years, constraintValidatorInclusiveFalse ); } - private void assertValidAge(LocalDate birthDate) { - assertTrue( ageValidator.isValid( birthDate, null ), birthDate + " should be a date equal or more than " + value + " years before today" ); + @Test + public void testProgrammaticDefinition() throws Exception { + HibernateValidatorConfiguration config = getConfiguration( HibernateValidator.class ); + ConstraintMapping mapping = config.createConstraintMapping(); + mapping.type( User.class ) + .property( "birthDate" , FIELD ) + .constraint( new AgeMinDef().value( this.value ) ); + config.addMapping( mapping ); + Validator validator = config.buildValidatorFactory().getValidator(); + + LocalDate todayMinus18Years = LocalDate.now().minusYears( 18 ); + LocalDate tomorrowMinus18Years = LocalDate.now().plusDays( 1 ).minusYears( 18 ); + + Set> constraintViolations = validator.validate( new User( todayMinus18Years ) ); + assertNoViolations( constraintViolations ); + + constraintViolations = validator.validate( new User( tomorrowMinus18Years ) ); + assertThat( constraintViolations ).containsOnlyViolations( + violationOf( AgeMin.class ) + ); } - private void assertInvalidAge(LocalDate birthDate) { - assertFalse( ageValidator.isValid( birthDate, null ), birthDate + " should be a date less than " + value + " years before today" ); + private static class User { + private final LocalDate birthDate; + + public User(LocalDate birthDate) { + this.birthDate = birthDate; + } } } From 1d34b291787a71b10c2d20eaf0df3bae80006378 Mon Sep 17 00:00:00 2001 From: Hilmerc Date: Mon, 15 Jan 2018 18:06:50 -0500 Subject: [PATCH 3/8] HV-1552 Adding new MinAge Constraint --- .../test/internal/constraintvalidators/hv/AgeValidatorTest.java | 1 - 1 file changed, 1 deletion(-) diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/hv/AgeValidatorTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/hv/AgeValidatorTest.java index c848df7253..face7a7236 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/hv/AgeValidatorTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/hv/AgeValidatorTest.java @@ -31,7 +31,6 @@ import org.hibernate.validator.testutil.TestForIssue; import org.hibernate.validator.testutils.ConstraintValidatorInitializationHelper; -import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import java.time.LocalDate; From a93a9e553f6ca939d677f333d2cff5b584e51e94 Mon Sep 17 00:00:00 2001 From: Hilmerc Date: Tue, 23 Jan 2018 16:19:18 -0500 Subject: [PATCH 4/8] HV-1552 Adding new MinAge Constraint --- .../validator/cfg/defs/AgeMinDef.java | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 engine/src/main/java/org/hibernate/validator/cfg/defs/AgeMinDef.java diff --git a/engine/src/main/java/org/hibernate/validator/cfg/defs/AgeMinDef.java b/engine/src/main/java/org/hibernate/validator/cfg/defs/AgeMinDef.java new file mode 100644 index 0000000000..72fa2da671 --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/cfg/defs/AgeMinDef.java @@ -0,0 +1,21 @@ +package org.hibernate.validator.cfg.defs; + +import org.hibernate.validator.cfg.ConstraintDef; +import org.hibernate.validator.constraints.AgeMin; + +public class AgeMinDef extends ConstraintDef { + + public AgeMinDef() { + super( AgeMin.class ); + } + + public AgeMinDef value(int value) { + addParameter( "value", value ); + return this; + } + + public AgeMinDef inclusive(boolean inclusive) { + addParameter( "inclusive", inclusive ); + return this; + } +} From 4e74cd4dfd367484c8b28c8f77383fdce1f7e0d6 Mon Sep 17 00:00:00 2001 From: Hilmerc Date: Thu, 8 Feb 2018 12:34:00 -0500 Subject: [PATCH 5/8] HV-1552 Adding new MinAge Constraint --- .../validator/cfg/defs/AgeMinDef.java | 17 ++ .../validator/constraints/AgeMin.java | 22 +- .../age/AbstractAgeInstantBasedValidator.java | 63 +++++ .../hv/age/AbstractAgeTimeBasedValidator.java | 64 +++++ .../AbstractAgeMinInstantBasedValidator.java | 58 ++++ .../AbstractAgeMinTimeBasedValidator.java} | 58 ++-- .../age/min/AgeMinValidatorForCalendar.java | 32 +++ .../age/min/AgeMinValidatorForLocalDate.java | 25 ++ .../metadata/core/ConstraintHelper.java | 9 +- .../hv/AgeValidatorTest.java | 125 --------- .../hv/age/AgeValidatorTest.java | 261 ++++++++++++++++++ 11 files changed, 569 insertions(+), 165 deletions(-) create mode 100644 engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/hv/age/AbstractAgeInstantBasedValidator.java create mode 100644 engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/hv/age/AbstractAgeTimeBasedValidator.java create mode 100644 engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/hv/age/min/AbstractAgeMinInstantBasedValidator.java rename engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/hv/{AgeMinValidator.java => age/min/AbstractAgeMinTimeBasedValidator.java} (56%) create mode 100644 engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/hv/age/min/AgeMinValidatorForCalendar.java create mode 100644 engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/hv/age/min/AgeMinValidatorForLocalDate.java delete mode 100644 engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/hv/AgeValidatorTest.java create mode 100644 engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/hv/age/AgeValidatorTest.java diff --git a/engine/src/main/java/org/hibernate/validator/cfg/defs/AgeMinDef.java b/engine/src/main/java/org/hibernate/validator/cfg/defs/AgeMinDef.java index 72fa2da671..a8d404a545 100644 --- a/engine/src/main/java/org/hibernate/validator/cfg/defs/AgeMinDef.java +++ b/engine/src/main/java/org/hibernate/validator/cfg/defs/AgeMinDef.java @@ -1,8 +1,20 @@ +/* + * Hibernate Validator, declare and validate application constraints + * + * License: Apache License, Version 2.0 + * See the license.txt file in the root directory or . + */ package org.hibernate.validator.cfg.defs; import org.hibernate.validator.cfg.ConstraintDef; import org.hibernate.validator.constraints.AgeMin; +import java.time.temporal.ChronoUnit; + +/** + * @author Hillmer Chona + * @since 6.0.8 + */ public class AgeMinDef extends ConstraintDef { public AgeMinDef() { @@ -18,4 +30,9 @@ public AgeMinDef inclusive(boolean inclusive) { addParameter( "inclusive", inclusive ); return this; } + + public AgeMinDef unit(ChronoUnit unit) { + addParameter( "unit", unit ); + return this; + } } diff --git a/engine/src/main/java/org/hibernate/validator/constraints/AgeMin.java b/engine/src/main/java/org/hibernate/validator/constraints/AgeMin.java index 6ec99055fd..8171ecbd75 100644 --- a/engine/src/main/java/org/hibernate/validator/constraints/AgeMin.java +++ b/engine/src/main/java/org/hibernate/validator/constraints/AgeMin.java @@ -12,6 +12,7 @@ import java.lang.annotation.Repeatable; import java.lang.annotation.Retention; import java.lang.annotation.Target; +import java.time.temporal.ChronoUnit; import static java.lang.annotation.ElementType.ANNOTATION_TYPE; @@ -23,11 +24,14 @@ import static java.lang.annotation.RetentionPolicy.RUNTIME; /** - * The annotated element must be a date where the number of years go by to today must be - * greater or equal to the specified value + * The annotated element must be a date where the number of Years, Days, Months, etc. according + * to an unit {@code java.time.temporal.ChronoUnit} go by to today must be + * greater or equal to the specified value if inclusive is true + * or is greater when inclusive is false. *

*

* The supported type is {@code LocalDate}. {@code null} is considered valid. + * The supported type is {@code Calendar}. {@code null} is considered valid. *

* * @author Hillmer Chona @@ -47,7 +51,7 @@ Class[] payload() default {}; /** - * @return value the age in years from a given date must be greater or equal to + * @return value the referenceAge according to unit from a given date must be greater or equal to */ int value(); @@ -60,6 +64,16 @@ */ boolean inclusive() default true; + + /** + * Specifies the date period unit ( years, months, days, etc.) that will be used to compare the given date + * with the reference value. + * By default, it is YEARS. + * + * @return unit the date period unit + */ + ChronoUnit unit() default ChronoUnit.YEARS; + /** * Defines several {@link AgeMin} annotations on the same element. * @@ -71,4 +85,6 @@ @interface List { AgeMin[] value(); } + + } diff --git a/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/hv/age/AbstractAgeInstantBasedValidator.java b/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/hv/age/AbstractAgeInstantBasedValidator.java new file mode 100644 index 0000000000..e74726d4d5 --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/hv/age/AbstractAgeInstantBasedValidator.java @@ -0,0 +1,63 @@ +/* + * Hibernate Validator, declare and validate application constraints + * + * License: Apache License, Version 2.0 + * See the license.txt file in the root directory or . + */ +package org.hibernate.validator.internal.constraintvalidators.hv.age; + +import java.lang.annotation.Annotation; +import java.time.Clock; +import java.time.Duration; +import java.time.Instant; +import java.time.temporal.ChronoUnit; + +import javax.validation.ConstraintValidatorContext; + +import org.hibernate.validator.constraintvalidation.HibernateConstraintValidator; + +/** + * Base class for all age validators that use an {@link Instant} to be compared to the age reference. + * + * @author Hillmer Chona + * @since 6.0.8 + */ +public abstract class AbstractAgeInstantBasedValidator implements HibernateConstraintValidator { + + protected Clock referenceClock; + + protected int referenceAge; + + protected boolean inclusive; + + protected ChronoUnit unit; + + @Override + public boolean isValid(T value, ConstraintValidatorContext context) { + // null values are valid + if ( value == null ) { + return true; + } + + long result = this.getCurrentAge( value ) - this.referenceAge; + + return isValid( result ); + } + + /** + * Returns the temporal validation tolerance to apply. + */ + protected abstract Duration getEffectiveTemporalValidationTolerance(Duration absoluteTemporalValidationTolerance); + + /** + * Returns the number of Years, Days, Months, etc. according to an unit {@code java.time.temporal.ChronoUnit} + * from a given {@code java.util.Calendar} to current day + */ + protected abstract long getCurrentAge(T value); + + /** + * Returns whether the result of the comparison between the validated value and the age reference is considered + * valid. + */ + protected abstract boolean isValid(long result); +} diff --git a/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/hv/age/AbstractAgeTimeBasedValidator.java b/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/hv/age/AbstractAgeTimeBasedValidator.java new file mode 100644 index 0000000000..c81896e9ad --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/hv/age/AbstractAgeTimeBasedValidator.java @@ -0,0 +1,64 @@ +/* + * Hibernate Validator, declare and validate application constraints + * + * License: Apache License, Version 2.0 + * See the license.txt file in the root directory or . + */ +package org.hibernate.validator.internal.constraintvalidators.hv.age; + +import java.lang.annotation.Annotation; +import java.time.Clock; +import java.time.Duration; +import java.time.temporal.ChronoUnit; +import java.time.temporal.TemporalAccessor; + +import javax.validation.ConstraintValidatorContext; + +import org.hibernate.validator.constraintvalidation.HibernateConstraintValidator; + +/** + * Base class for all age validators that are based on the {@code java.time} package. + * + * @author Hillmer Chona + * @since 6.0.8 + */ +public abstract class AbstractAgeTimeBasedValidator> + implements HibernateConstraintValidator { + + protected Clock referenceClock; + + protected int referenceAge; + + protected boolean inclusive; + + protected ChronoUnit unit; + + @Override + public boolean isValid(T value, ConstraintValidatorContext context) { + // null values are valid + if ( value == null ) { + return true; + } + + long result = this.getCurrentAge( value ) - this.referenceAge; + + return isValid( result ); + } + + /** + * Returns the temporal validation tolerance to apply. + */ + protected abstract Duration getEffectiveTemporalValidationTolerance(Duration absoluteTemporalValidationTolerance); + + /** + * Returns the number of Years, Days, Months, etc. according to an unit {@code java.time.temporal.ChronoUnit} + * from a given {@code java.util.Calendar} to current day + */ + protected abstract long getCurrentAge(T value); + + /** + * Returns whether the result of the comparison between the validated value and the age reference is considered + * valid. + */ + protected abstract boolean isValid(long result); +} diff --git a/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/hv/age/min/AbstractAgeMinInstantBasedValidator.java b/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/hv/age/min/AbstractAgeMinInstantBasedValidator.java new file mode 100644 index 0000000000..af4f9085e2 --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/hv/age/min/AbstractAgeMinInstantBasedValidator.java @@ -0,0 +1,58 @@ +/* + * Hibernate Validator, declare and validate application constraints + * + * License: Apache License, Version 2.0 + * See the license.txt file in the root directory or . + */ +package org.hibernate.validator.internal.constraintvalidators.hv.age.min; + +import java.lang.invoke.MethodHandles; +import java.time.Clock; +import java.time.Duration; +import java.time.Instant; + +import javax.validation.metadata.ConstraintDescriptor; + +import org.hibernate.validator.constraints.AgeMin; +import org.hibernate.validator.constraintvalidation.HibernateConstraintValidatorInitializationContext; +import org.hibernate.validator.internal.constraintvalidators.hv.age.AbstractAgeInstantBasedValidator; +import org.hibernate.validator.internal.util.logging.Log; +import org.hibernate.validator.internal.util.logging.LoggerFactory; + +/** + * Base class for all {@code @AgeMin} validators that use an {@link Instant} to be compared to the age reference. + * + * @author Hillmer Chona + * @since 6.0.8 + */ +public abstract class AbstractAgeMinInstantBasedValidator extends AbstractAgeInstantBasedValidator { + + private static final Log LOG = LoggerFactory.make( MethodHandles.lookup() ); + + @Override + public void initialize(ConstraintDescriptor constraintDescriptor, HibernateConstraintValidatorInitializationContext initializationContext) { + try { + super.referenceClock = Clock.offset( + initializationContext.getClockProvider().getClock(), + getEffectiveTemporalValidationTolerance( initializationContext.getTemporalValidationTolerance() ) + ); + super.referenceAge = constraintDescriptor.getAnnotation().value(); + super.inclusive = constraintDescriptor.getAnnotation().inclusive(); + super.unit = constraintDescriptor.getAnnotation().unit(); + + } + catch (Exception e) { + throw LOG.getUnableToGetCurrentTimeFromClockProvider( e ); + } + } + + @Override + protected Duration getEffectiveTemporalValidationTolerance(Duration absoluteTemporalValidationTolerance) { + return absoluteTemporalValidationTolerance; + } + + @Override + protected boolean isValid(long result) { + return super.inclusive ? result >= 0 : result > 0; + } +} diff --git a/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/hv/AgeMinValidator.java b/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/hv/age/min/AbstractAgeMinTimeBasedValidator.java similarity index 56% rename from engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/hv/AgeMinValidator.java rename to engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/hv/age/min/AbstractAgeMinTimeBasedValidator.java index fd9568d765..57a2a9f086 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/hv/AgeMinValidator.java +++ b/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/hv/age/min/AbstractAgeMinTimeBasedValidator.java @@ -4,49 +4,44 @@ * License: Apache License, Version 2.0 * See the license.txt file in the root directory or . */ -package org.hibernate.validator.internal.constraintvalidators.hv; +package org.hibernate.validator.internal.constraintvalidators.hv.age.min; + +import java.lang.invoke.MethodHandles; +import java.time.Clock; +import java.time.Duration; +import java.time.temporal.Temporal; +import java.time.temporal.TemporalAccessor; + +import javax.validation.metadata.ConstraintDescriptor; import org.hibernate.validator.constraints.AgeMin; -import org.hibernate.validator.constraintvalidation.HibernateConstraintValidator; import org.hibernate.validator.constraintvalidation.HibernateConstraintValidatorInitializationContext; +import org.hibernate.validator.internal.constraintvalidators.hv.age.AbstractAgeTimeBasedValidator; import org.hibernate.validator.internal.util.logging.Log; import org.hibernate.validator.internal.util.logging.LoggerFactory; -import javax.validation.ConstraintValidatorContext; -import javax.validation.metadata.ConstraintDescriptor; - -import java.lang.invoke.MethodHandles; -import java.time.Clock; -import java.time.Duration; -import java.time.LocalDate; -import java.time.temporal.ChronoUnit; - /** * - * Checks that the number of years from a given date to today is greater or equal to - * a specified value + * Base class for all {@code @AgeMin} validators that are based on the {@code java.time} package. * * @author Hillmer Chona + * @since 6.0.8 */ -public class AgeMinValidator implements HibernateConstraintValidator { +public abstract class AbstractAgeMinTimeBasedValidator> + extends AbstractAgeTimeBasedValidator { private static final Log LOG = LoggerFactory.make( MethodHandles.lookup() ); - private int minAge; - - private boolean inclusive; - - protected Clock referenceClock; - @Override public void initialize(ConstraintDescriptor constraintDescriptor, HibernateConstraintValidatorInitializationContext initializationContext) { try { - this.referenceClock = Clock.offset( + super.referenceClock = Clock.offset( initializationContext.getClockProvider().getClock(), getEffectiveTemporalValidationTolerance( initializationContext.getTemporalValidationTolerance() ) ); - this.minAge = constraintDescriptor.getAnnotation().value(); - this.inclusive = constraintDescriptor.getAnnotation().inclusive(); + super.referenceAge = constraintDescriptor.getAnnotation().value(); + super.inclusive = constraintDescriptor.getAnnotation().inclusive(); + super.unit = constraintDescriptor.getAnnotation().unit(); } catch (Exception e) { @@ -55,19 +50,12 @@ public void initialize(ConstraintDescriptor constraintDescriptor, Hibern } @Override - public boolean isValid(LocalDate date, ConstraintValidatorContext constraintValidatorContext) { - // null values are valid - if ( date == null ) { - return true; - } - return inclusive ? ChronoUnit.YEARS.between( date, getReferenceValue( referenceClock ) ) >= minAge : ChronoUnit.YEARS.between( date, getReferenceValue( referenceClock ) ) > minAge; - } - - private LocalDate getReferenceValue(Clock reference) { - return LocalDate.now( reference ); - } - protected Duration getEffectiveTemporalValidationTolerance(Duration absoluteTemporalValidationTolerance) { return absoluteTemporalValidationTolerance.negated(); } + + @Override + protected boolean isValid(long result) { + return super.inclusive ? result >= 0 : result > 0; + } } diff --git a/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/hv/age/min/AgeMinValidatorForCalendar.java b/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/hv/age/min/AgeMinValidatorForCalendar.java new file mode 100644 index 0000000000..910e7d8731 --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/hv/age/min/AgeMinValidatorForCalendar.java @@ -0,0 +1,32 @@ +package org.hibernate.validator.internal.constraintvalidators.hv.age.min; + +import java.time.Instant; +import java.time.LocalDate; +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.util.Calendar; + +/** + * Checks that the number of Years, Days, Months, etc. according to an unit {@code java.time.temporal.ChronoUnit} + * from a given {@code java.util.Calendar} to current day is greater than or equal to the specified value if inclusive is true + * or is greater when inclusive is false. + * + * @author Hillmer Chona + * @since 6.0.8 + */ +public class AgeMinValidatorForCalendar extends AbstractAgeMinInstantBasedValidator { + @Override + protected long getCurrentAge(Calendar value) { + ZonedDateTime zdt1 = getInstant( value ).atZone( ZoneId.systemDefault() ); + LocalDate date1 = zdt1.toLocalDate(); + + ZonedDateTime zdt2 = super.referenceClock.instant().atZone( ZoneId.systemDefault() ); + LocalDate date2 = zdt2.toLocalDate(); + + return super.unit.between( date1, date2 ); + } + + private Instant getInstant(Calendar value) { + return value.toInstant(); + } +} diff --git a/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/hv/age/min/AgeMinValidatorForLocalDate.java b/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/hv/age/min/AgeMinValidatorForLocalDate.java new file mode 100644 index 0000000000..cfa4b6edcb --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/hv/age/min/AgeMinValidatorForLocalDate.java @@ -0,0 +1,25 @@ +package org.hibernate.validator.internal.constraintvalidators.hv.age.min; + +import java.time.Clock; +import java.time.LocalDate; + +/** + * Checks that the number of Years, Days, Months, etc. according to an unit {@code java.time.temporal.ChronoUnit} + * from a given {@code java.time.LocalDate} to current day is greater than or equal to the specified value if inclusive is true + * or is greater when inclusive is false. + * + * @author Hillmer Chona + * @since 6.0.8 + */ +public class AgeMinValidatorForLocalDate extends AbstractAgeMinTimeBasedValidator { + + @Override + protected long getCurrentAge(LocalDate value) { + return super.unit.between( value, getReferenceValue( super.referenceClock ) ); + } + + private LocalDate getReferenceValue(Clock reference) { + return LocalDate.now( reference ); + } + +} diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/core/ConstraintHelper.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/core/ConstraintHelper.java index 7e99a90e40..c9a983847c 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/metadata/core/ConstraintHelper.java +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/core/ConstraintHelper.java @@ -259,7 +259,6 @@ import org.hibernate.validator.internal.constraintvalidators.hv.ISBNValidator; import org.hibernate.validator.internal.constraintvalidators.hv.LengthValidator; import org.hibernate.validator.internal.constraintvalidators.hv.LuhnCheckValidator; -import org.hibernate.validator.internal.constraintvalidators.hv.AgeMinValidator; import org.hibernate.validator.internal.constraintvalidators.hv.Mod10CheckValidator; import org.hibernate.validator.internal.constraintvalidators.hv.Mod11CheckValidator; import org.hibernate.validator.internal.constraintvalidators.hv.ModCheckValidator; @@ -268,6 +267,8 @@ import org.hibernate.validator.internal.constraintvalidators.hv.ScriptAssertValidator; import org.hibernate.validator.internal.constraintvalidators.hv.URLValidator; import org.hibernate.validator.internal.constraintvalidators.hv.UniqueElementsValidator; +import org.hibernate.validator.internal.constraintvalidators.hv.age.min.AgeMinValidatorForCalendar; +import org.hibernate.validator.internal.constraintvalidators.hv.age.min.AgeMinValidatorForLocalDate; import org.hibernate.validator.internal.constraintvalidators.hv.br.CNPJValidator; import org.hibernate.validator.internal.constraintvalidators.hv.br.CPFValidator; import org.hibernate.validator.internal.constraintvalidators.hv.pl.NIPValidator; @@ -464,7 +465,11 @@ public ConstraintHelper() { ) ); } - putConstraint( tmpConstraints, AgeMin.class, AgeMinValidator.class ); + List>> ageMinValidators = new ArrayList<>( 2 ); + ageMinValidators.add( AgeMinValidatorForLocalDate.class ); + ageMinValidators.add( AgeMinValidatorForCalendar.class ); + putConstraints( tmpConstraints, AgeMin.class, ageMinValidators ); + if ( isJavaMoneyInClasspath() ) { putConstraints( tmpConstraints, Negative.class, Arrays.asList( diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/hv/AgeValidatorTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/hv/AgeValidatorTest.java deleted file mode 100644 index face7a7236..0000000000 --- a/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/hv/AgeValidatorTest.java +++ /dev/null @@ -1,125 +0,0 @@ -/* - * Hibernate Validator, declare and validate application constraints - * - * License: Apache License, Version 2.0 - * See the license.txt file in the root directory or . - */ -package org.hibernate.validator.test.internal.constraintvalidators.hv; - -import static java.lang.annotation.ElementType.FIELD; -import static org.hibernate.validator.testutil.ConstraintViolationAssert.assertNoViolations; -import static org.hibernate.validator.testutil.ConstraintViolationAssert.assertThat; -import static org.hibernate.validator.testutil.ConstraintViolationAssert.violationOf; -import static org.hibernate.validator.testutils.ValidatorUtil.getConfiguration; -import static org.testng.Assert.assertFalse; -import static org.testng.Assert.assertTrue; - -import java.util.Set; - -import javax.validation.ConstraintValidator; -import javax.validation.ConstraintViolation; -import javax.validation.Validator; - -import org.hibernate.validator.HibernateValidator; -import org.hibernate.validator.HibernateValidatorConfiguration; -import org.hibernate.validator.cfg.ConstraintMapping; -import org.hibernate.validator.cfg.defs.AgeMinDef; -import org.hibernate.validator.constraints.AgeMin; -import org.hibernate.validator.constraintvalidation.HibernateConstraintValidator; -import org.hibernate.validator.internal.constraintvalidators.hv.AgeMinValidator; -import org.hibernate.validator.internal.util.annotation.ConstraintAnnotationDescriptor; -import org.hibernate.validator.testutil.TestForIssue; - -import org.hibernate.validator.testutils.ConstraintValidatorInitializationHelper; -import org.testng.annotations.Test; - -import java.time.LocalDate; - -/** - * A set of tests for {@link AgeMin} constraint validator ({@link AgeMinValidator}), which - * make sure that validation is performed correctly. - * - * @author Hillmer Chona - * @since 6.0.8 - */ -@TestForIssue(jiraKey = "HV-1552" ) -public class AgeValidatorTest { - - private int value = 18; - - /** - * @return an initialized {@link ConstraintValidator} using {@code DUMMY_CONSTRAINT_VALIDATOR_INITIALIZATION_CONTEXT} - */ - private ConstraintValidator getInitializedValidator(int value, boolean inclusive) { - HibernateConstraintValidator validator = new AgeMinValidator(); - ConstraintAnnotationDescriptor.Builder descriptorBuilder = new ConstraintAnnotationDescriptor.Builder<>( AgeMin.class ); - descriptorBuilder.setAttribute( "value", value ); - descriptorBuilder.setAttribute( "inclusive", inclusive ); - ConstraintAnnotationDescriptor descriptor = descriptorBuilder.build(); - ConstraintValidatorInitializationHelper.initialize( validator, descriptor ); - return validator; - } - - private void assertValidAge(LocalDate birthDate, ConstraintValidator constraintValidator) { - assertTrue( constraintValidator.isValid( birthDate, null ), birthDate + " should be a date equal or more than " + value + " years before today" ); - } - - private void assertInvalidAge(LocalDate birthDate, ConstraintValidator constraintValidator) { - assertFalse( constraintValidator.isValid( birthDate, null ), birthDate + " should be a date less than " + value + " years before today" ); - } - - @Test - public void testLocalDate() throws Exception { - ConstraintValidator constraintValidator = getInitializedValidator( value, true ); - - LocalDate todayMinus18Years = LocalDate.now().minusYears( 18 ); - LocalDate todayMinus2MonthAnd18Years = LocalDate.now().minusMonths( 2 ).minusYears( 18 ); - LocalDate tomorrowMinus18Years = LocalDate.now().plusDays( 1 ).minusYears( 18 ); - - assertValidAge( null , constraintValidator ); - assertValidAge( todayMinus18Years, constraintValidator ); - assertValidAge( todayMinus2MonthAnd18Years, constraintValidator ); - assertInvalidAge( tomorrowMinus18Years, constraintValidator ); - } - - @Test - public void testInclusiveLocalDate() throws Exception { - ConstraintValidator constraintValidatorInclusiveTrue = getInitializedValidator( value, true ); - ConstraintValidator constraintValidatorInclusiveFalse = getInitializedValidator( value, false ); - - LocalDate todayMinus18Years = LocalDate.now().minusYears( 18 ); - - assertValidAge( todayMinus18Years, constraintValidatorInclusiveTrue ); - assertInvalidAge( todayMinus18Years, constraintValidatorInclusiveFalse ); - } - - @Test - public void testProgrammaticDefinition() throws Exception { - HibernateValidatorConfiguration config = getConfiguration( HibernateValidator.class ); - ConstraintMapping mapping = config.createConstraintMapping(); - mapping.type( User.class ) - .property( "birthDate" , FIELD ) - .constraint( new AgeMinDef().value( this.value ) ); - config.addMapping( mapping ); - Validator validator = config.buildValidatorFactory().getValidator(); - - LocalDate todayMinus18Years = LocalDate.now().minusYears( 18 ); - LocalDate tomorrowMinus18Years = LocalDate.now().plusDays( 1 ).minusYears( 18 ); - - Set> constraintViolations = validator.validate( new User( todayMinus18Years ) ); - assertNoViolations( constraintViolations ); - - constraintViolations = validator.validate( new User( tomorrowMinus18Years ) ); - assertThat( constraintViolations ).containsOnlyViolations( - violationOf( AgeMin.class ) - ); - } - - private static class User { - private final LocalDate birthDate; - - public User(LocalDate birthDate) { - this.birthDate = birthDate; - } - } -} diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/hv/age/AgeValidatorTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/hv/age/AgeValidatorTest.java new file mode 100644 index 0000000000..129e8df061 --- /dev/null +++ b/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/hv/age/AgeValidatorTest.java @@ -0,0 +1,261 @@ +/* + * Hibernate Validator, declare and validate application constraints + * + * License: Apache License, Version 2.0 + * See the license.txt file in the root directory or . + */ +package org.hibernate.validator.test.internal.constraintvalidators.hv.age; + +import static java.lang.annotation.ElementType.FIELD; +import static org.hibernate.validator.testutil.ConstraintViolationAssert.assertNoViolations; +import static org.hibernate.validator.testutil.ConstraintViolationAssert.assertThat; +import static org.hibernate.validator.testutil.ConstraintViolationAssert.violationOf; +import static org.hibernate.validator.testutils.ValidatorUtil.getConfiguration; +import static org.testng.Assert.assertFalse; +import static org.testng.Assert.assertTrue; + +import java.time.LocalDate; +import java.time.temporal.ChronoUnit; +import java.util.Calendar; +import java.util.Set; + +import javax.validation.ConstraintValidator; +import javax.validation.ConstraintViolation; +import javax.validation.Validator; + +import org.hibernate.validator.HibernateValidator; +import org.hibernate.validator.HibernateValidatorConfiguration; +import org.hibernate.validator.cfg.ConstraintMapping; +import org.hibernate.validator.cfg.defs.AgeMinDef; +import org.hibernate.validator.constraints.AgeMin; +import org.hibernate.validator.constraintvalidation.HibernateConstraintValidator; +import org.hibernate.validator.internal.constraintvalidators.hv.age.min.AgeMinValidatorForCalendar; +import org.hibernate.validator.internal.constraintvalidators.hv.age.min.AgeMinValidatorForLocalDate; +import org.hibernate.validator.internal.util.annotation.ConstraintAnnotationDescriptor; +import org.hibernate.validator.testutil.TestForIssue; +import org.hibernate.validator.testutils.ConstraintValidatorInitializationHelper; + +import org.testng.annotations.Test; + +/** + * A set of tests for {@link AgeMin} constraint validator ({@link AgeMinValidatorForLocalDate}, {@link AgeMinValidatorForCalendar}), which + * make sure that validation is performed correctly. + * + * @author Hillmer Chona + * @since 6.0.8 + */ +@TestForIssue(jiraKey = "HV-1552") +public class AgeValidatorTest { + + private static final int MINIMUM_AGE_YEARS = 18; + + private static final int MINIMUM_AGE_MONTHS = 240; + + @Test + public void testLocalDate() throws Exception { + ConstraintValidator constraintValidator = getInitializedValidator( MINIMUM_AGE_YEARS, true ); + + LocalDate todayMinus18Years = LocalDate.now().minusYears( MINIMUM_AGE_YEARS ); + LocalDate todayMinus2MonthAnd18Years = LocalDate.now().minusMonths( 2 ).minusYears( MINIMUM_AGE_YEARS ); + LocalDate tomorrowMinus18Years = LocalDate.now().plusDays( 1 ).minusYears( MINIMUM_AGE_YEARS ); + + assertValidAge( null, constraintValidator ); + assertValidAge( todayMinus18Years, constraintValidator ); + assertValidAge( todayMinus2MonthAnd18Years, constraintValidator ); + assertInvalidAge( tomorrowMinus18Years, constraintValidator ); + } + + @Test + public void testInclusiveLocalDate() throws Exception { + ConstraintValidator constraintValidatorInclusiveTrue = getInitializedValidator( + MINIMUM_AGE_YEARS, + true + ); + ConstraintValidator constraintValidatorInclusiveFalse = getInitializedValidator( + MINIMUM_AGE_YEARS, + false + ); + + LocalDate todayMinus18Years = LocalDate.now().minusYears( MINIMUM_AGE_YEARS ); + + assertValidAge( todayMinus18Years, constraintValidatorInclusiveTrue ); + assertInvalidAge( todayMinus18Years, constraintValidatorInclusiveFalse ); + } + + @Test + public void testProgrammaticDefinition() throws Exception { + HibernateValidatorConfiguration config = getConfiguration( HibernateValidator.class ); + ConstraintMapping mapping = config.createConstraintMapping(); + mapping.type( User.class ) + .property( "birthDate", FIELD ) + .constraint( new AgeMinDef().value( MINIMUM_AGE_YEARS ) ); + config.addMapping( mapping ); + Validator validator = config.buildValidatorFactory().getValidator(); + + LocalDate todayMinus18Years = LocalDate.now().minusYears( MINIMUM_AGE_YEARS ); + LocalDate tomorrowMinus18Years = LocalDate.now().plusDays( 1 ).minusYears( MINIMUM_AGE_YEARS ); + + Set> constraintViolations = validator.validate( new User( todayMinus18Years ) ); + assertNoViolations( constraintViolations ); + + constraintViolations = validator.validate( new User( tomorrowMinus18Years ) ); + assertThat( constraintViolations ).containsOnlyViolations( + violationOf( AgeMin.class ) + ); + } + + @Test + public void testLocalDateChronoUnits() throws Exception { + ConstraintValidator constraintValidator = getInitializedValidator( + MINIMUM_AGE_MONTHS, + true, + ChronoUnit.MONTHS + ); + + LocalDate todayMinus18Years = LocalDate.now().minusMonths( MINIMUM_AGE_MONTHS ); + LocalDate todayMinus2MonthAnd18Years = LocalDate.now().minusMonths( 2 ).minusMonths( MINIMUM_AGE_MONTHS ); + LocalDate tomorrowMinus18Years = LocalDate.now().plusDays( 1 ).minusMonths( MINIMUM_AGE_MONTHS ); + + assertValidAge( null, constraintValidator ); + assertValidAge( todayMinus18Years, constraintValidator ); + assertValidAge( todayMinus2MonthAnd18Years, constraintValidator ); + assertInvalidAge( tomorrowMinus18Years, constraintValidator ); + } + + + @Test + public void testCalendar() throws Exception { + ConstraintValidator constraintValidator = getInitializedValidatorForCalendar( + MINIMUM_AGE_YEARS, + true + ); + + Calendar todayMinus18Years = getCalendarTodayMinus18Years(); + + Calendar todayMinus2MonthAnd18Years = getCalendarTodayMinus18Years(); + todayMinus2MonthAnd18Years.add( Calendar.MONTH, 2 * -1 ); + + Calendar tomorrowMinus18Years = getCalendarTodayMinus18Years(); + tomorrowMinus18Years.add( Calendar.DATE, 1 ); + + assertValidAge( null, constraintValidator ); + assertValidAge( todayMinus18Years, constraintValidator ); + assertValidAge( todayMinus2MonthAnd18Years, constraintValidator ); + assertInvalidAge( tomorrowMinus18Years, constraintValidator ); + } + + //@Test + public void testInclusiveCalendar() throws Exception { + ConstraintValidator constraintValidatorInclusiveTrue = getInitializedValidatorForCalendar( + MINIMUM_AGE_YEARS, + true + ); + ConstraintValidator constraintValidatorInclusiveFalse = getInitializedValidatorForCalendar( + MINIMUM_AGE_YEARS, + false + ); + + Calendar todayMinus18Years = getCalendarTodayMinus18Years(); + + assertValidAge( todayMinus18Years, constraintValidatorInclusiveTrue ); + assertInvalidAge( todayMinus18Years, constraintValidatorInclusiveFalse ); + } + + private Calendar getCalendarTodayMinus18Years() { + + Calendar calendar = Calendar.getInstance(); + + calendar.clear( Calendar.HOUR_OF_DAY ); + calendar.clear( Calendar.MINUTE ); + calendar.clear( Calendar.SECOND ); + calendar.clear( Calendar.MILLISECOND ); + + calendar.add( Calendar.YEAR, MINIMUM_AGE_YEARS * -1 ); + + return calendar; + + } + + /** + * @return an initialized {@link ConstraintValidator} using {@code DUMMY_CONSTRAINT_VALIDATOR_INITIALIZATION_CONTEXT} + */ + private ConstraintValidator getInitializedValidator(int value, boolean inclusive) { + HibernateConstraintValidator validator = new AgeMinValidatorForLocalDate(); + ConstraintAnnotationDescriptor.Builder descriptorBuilder = new ConstraintAnnotationDescriptor.Builder<>( + AgeMin.class ); + descriptorBuilder.setAttribute( "value", value ); + descriptorBuilder.setAttribute( "inclusive", inclusive ); + ConstraintAnnotationDescriptor descriptor = descriptorBuilder.build(); + ConstraintValidatorInitializationHelper.initialize( validator, descriptor ); + return validator; + } + + /** + * @return an initialized {@link ConstraintValidator} using {@code DUMMY_CONSTRAINT_VALIDATOR_INITIALIZATION_CONTEXT} + */ + private ConstraintValidator getInitializedValidatorForCalendar(int value, boolean inclusive) { + HibernateConstraintValidator validator = new AgeMinValidatorForCalendar(); + ConstraintAnnotationDescriptor.Builder descriptorBuilder = new ConstraintAnnotationDescriptor.Builder<>( + AgeMin.class ); + descriptorBuilder.setAttribute( "value", value ); + descriptorBuilder.setAttribute( "inclusive", inclusive ); + ConstraintAnnotationDescriptor descriptor = descriptorBuilder.build(); + ConstraintValidatorInitializationHelper.initialize( validator, descriptor ); + return validator; + } + + /** + * @return an initialized {@link ConstraintValidator} using {@code DUMMY_CONSTRAINT_VALIDATOR_INITIALIZATION_CONTEXT} + */ + private ConstraintValidator getInitializedValidator( + int value, + boolean inclusive, + ChronoUnit unit) { + HibernateConstraintValidator validator = new AgeMinValidatorForLocalDate(); + ConstraintAnnotationDescriptor.Builder descriptorBuilder = new ConstraintAnnotationDescriptor.Builder<>( + AgeMin.class ); + descriptorBuilder.setAttribute( "value", value ); + descriptorBuilder.setAttribute( "inclusive", inclusive ); + descriptorBuilder.setAttribute( "unit", unit ); + + ConstraintAnnotationDescriptor descriptor = descriptorBuilder.build(); + ConstraintValidatorInitializationHelper.initialize( validator, descriptor ); + return validator; + } + + private void assertValidAge(LocalDate birthDate, ConstraintValidator constraintValidator) { + assertTrue( + constraintValidator.isValid( birthDate, null ), + birthDate + " should be a date equal or more than " + MINIMUM_AGE_YEARS + " years before today" + ); + } + + private void assertInvalidAge(LocalDate birthDate, ConstraintValidator constraintValidator) { + assertFalse( + constraintValidator.isValid( birthDate, null ), + birthDate + " should be a date less than " + MINIMUM_AGE_YEARS + " years before today" + ); + } + + private void assertValidAge(Calendar birthDate, ConstraintValidator constraintValidator) { + assertTrue( + constraintValidator.isValid( birthDate, null ), + birthDate + " should be a date equal or more than " + MINIMUM_AGE_YEARS + " years before today" + ); + } + + private void assertInvalidAge(Calendar birthDate, ConstraintValidator constraintValidator) { + assertFalse( + constraintValidator.isValid( birthDate, null ), + birthDate + " should be a date less than " + MINIMUM_AGE_YEARS + " years before today" + ); + } + + private static class User { + private final LocalDate birthDate; + + public User(LocalDate birthDate) { + this.birthDate = birthDate; + } + } +} From edd9954e1cb4358469dac5880055a6062c87e788 Mon Sep 17 00:00:00 2001 From: Hilmerc Date: Sun, 11 Feb 2018 10:14:51 -0500 Subject: [PATCH 6/8] HV-1552 Adding new MinAge Constraint --- .../validator/cfg/defs/AgeMinDef.java | 8 +- .../validator/constraints/AgeMin.java | 47 ++-- .../age/AbstractAgeInstantBasedValidator.java | 48 +++- .../hv/age/AbstractAgeTimeBasedValidator.java | 34 ++- .../AbstractAgeMinInstantBasedValidator.java | 24 +- .../min/AbstractAgeMinTimeBasedValidator.java | 30 +-- .../age/min/AgeMinValidatorForCalendar.java | 24 +- .../hv/age/min/AgeMinValidatorForDate.java | 27 +++ .../age/min/AgeMinValidatorForHijrahDate.java | 26 +++ .../min/AgeMinValidatorForJapaneseDate.java | 27 +++ .../age/min/AgeMinValidatorForLocalDate.java | 15 +- .../age/min/AgeMinValidatorForMinguoDate.java | 27 +++ .../AgeMinValidatorForThaiBuddhistDate.java | 27 +++ .../hv/age/min/AgeMinValidatorForYear.java | 27 +++ .../age/min/AgeMinValidatorForYearMonth.java | 27 +++ .../metadata/core/ConstraintHelper.java | 18 +- .../hv/age/AgeValidatorTest.java | 205 ++++++++++++------ 17 files changed, 477 insertions(+), 164 deletions(-) create mode 100644 engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/hv/age/min/AgeMinValidatorForDate.java create mode 100644 engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/hv/age/min/AgeMinValidatorForHijrahDate.java create mode 100644 engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/hv/age/min/AgeMinValidatorForJapaneseDate.java create mode 100644 engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/hv/age/min/AgeMinValidatorForMinguoDate.java create mode 100644 engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/hv/age/min/AgeMinValidatorForThaiBuddhistDate.java create mode 100644 engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/hv/age/min/AgeMinValidatorForYear.java create mode 100644 engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/hv/age/min/AgeMinValidatorForYearMonth.java diff --git a/engine/src/main/java/org/hibernate/validator/cfg/defs/AgeMinDef.java b/engine/src/main/java/org/hibernate/validator/cfg/defs/AgeMinDef.java index a8d404a545..bd0300ce3c 100644 --- a/engine/src/main/java/org/hibernate/validator/cfg/defs/AgeMinDef.java +++ b/engine/src/main/java/org/hibernate/validator/cfg/defs/AgeMinDef.java @@ -26,13 +26,13 @@ public AgeMinDef value(int value) { return this; } - public AgeMinDef inclusive(boolean inclusive) { - addParameter( "inclusive", inclusive ); + public AgeMinDef unit(ChronoUnit unit) { + addParameter( "unit", unit ); return this; } - public AgeMinDef unit(ChronoUnit unit) { - addParameter( "unit", unit ); + public AgeMinDef inclusive(boolean inclusive) { + addParameter( "inclusive", inclusive ); return this; } } diff --git a/engine/src/main/java/org/hibernate/validator/constraints/AgeMin.java b/engine/src/main/java/org/hibernate/validator/constraints/AgeMin.java index 8171ecbd75..de01a5fc72 100644 --- a/engine/src/main/java/org/hibernate/validator/constraints/AgeMin.java +++ b/engine/src/main/java/org/hibernate/validator/constraints/AgeMin.java @@ -24,15 +24,25 @@ import static java.lang.annotation.RetentionPolicy.RUNTIME; /** - * The annotated element must be a date where the number of Years, Days, Months, etc. according - * to an unit {@code java.time.temporal.ChronoUnit} go by to today must be - * greater or equal to the specified value if inclusive is true - * or is greater when inclusive is false. + * The annotated element must be an instant, date or time for which at least + * the specified amount ({@link AgeMin#value()}) of Years/Days/Months/etc. defined + * by {@link AgeMin#unit()} have passed till now. *

+ * Supported types are: + *

*

- * The supported type is {@code LocalDate}. {@code null} is considered valid. - * The supported type is {@code Calendar}. {@code null} is considered valid. - *

+ * {@code null} elements are considered valid. + * * * @author Hillmer Chona * @since 6.0.8 @@ -51,28 +61,27 @@ Class[] payload() default {}; /** - * @return value the referenceAge according to unit from a given date must be greater or equal to + * @return the age according to unit from a given instant, date or time must be greater or equal to */ int value(); /** - * Specifies whether the specified value is inclusive or exclusive. - * By default, it is inclusive. + * Specifies the date period unit ( Years/Days/Months/etc. ) that will be used to compare the given instant, + * date or time with the reference value. + * By default, it is ({@link ChronoUnit#YEARS}). * - * @return {@code true} if the number of years from a given date must be higher or equal to the specified value, - * {@code false} if the number of years from a given date must be higher + * @return the date period unit */ - boolean inclusive() default true; - + ChronoUnit unit() default ChronoUnit.YEARS; /** - * Specifies the date period unit ( years, months, days, etc.) that will be used to compare the given date - * with the reference value. - * By default, it is YEARS. + * Specifies whether the specified value is inclusive or exclusive. + * By default, it is inclusive. * - * @return unit the date period unit + * @return {@code true} if the date period units from a given instant, date or time must be higher or equal to the specified value, + * {@code false} if date period units from a given instant, date or time must be higher */ - ChronoUnit unit() default ChronoUnit.YEARS; + boolean inclusive() default true; /** * Defines several {@link AgeMin} annotations on the same element. diff --git a/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/hv/age/AbstractAgeInstantBasedValidator.java b/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/hv/age/AbstractAgeInstantBasedValidator.java index e74726d4d5..f6486b6af5 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/hv/age/AbstractAgeInstantBasedValidator.java +++ b/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/hv/age/AbstractAgeInstantBasedValidator.java @@ -7,14 +7,20 @@ package org.hibernate.validator.internal.constraintvalidators.hv.age; import java.lang.annotation.Annotation; +import java.lang.invoke.MethodHandles; import java.time.Clock; import java.time.Duration; import java.time.Instant; +import java.time.LocalDate; +import java.time.ZoneOffset; import java.time.temporal.ChronoUnit; import javax.validation.ConstraintValidatorContext; import org.hibernate.validator.constraintvalidation.HibernateConstraintValidator; +import org.hibernate.validator.constraintvalidation.HibernateConstraintValidatorInitializationContext; +import org.hibernate.validator.internal.util.logging.Log; +import org.hibernate.validator.internal.util.logging.LoggerFactory; /** * Base class for all age validators that use an {@link Instant} to be compared to the age reference. @@ -22,9 +28,12 @@ * @author Hillmer Chona * @since 6.0.8 */ -public abstract class AbstractAgeInstantBasedValidator implements HibernateConstraintValidator { +public abstract class AbstractAgeInstantBasedValidator + implements HibernateConstraintValidator { - protected Clock referenceClock; + private static final Log LOG = LoggerFactory.make( MethodHandles.lookup() ); + + private Clock referenceClock; protected int referenceAge; @@ -32,14 +41,39 @@ public abstract class AbstractAgeInstantBasedValidator protected ChronoUnit unit; + public void initialize( + int referenceAge, + ChronoUnit unit, + boolean inclusive, + HibernateConstraintValidatorInitializationContext initializationContext) { + try { + this.referenceClock = Clock.offset( + initializationContext.getClockProvider().getClock(), + getEffectiveTemporalValidationTolerance( initializationContext.getTemporalValidationTolerance() ) + ); + } + catch (Exception e) { + throw LOG.getUnableToGetCurrentTimeFromClockProvider( e ); + } + this.referenceAge = referenceAge; + this.unit = unit; + this.inclusive = inclusive; + } + @Override public boolean isValid(T value, ConstraintValidatorContext context) { // null values are valid if ( value == null ) { return true; } + // As Instant does not support plus operation on ChronoUnits greater than DAYS we need to convert it to LocalDate + // first, which supports such operations. - long result = this.getCurrentAge( value ) - this.referenceAge; + long result = getInstant( value ).atZone( ZoneOffset.ofHours( 0 ) ) + .toLocalDate() + .plus( referenceAge, unit ) + .compareTo( + LocalDate.now( referenceClock ) ); return isValid( result ); } @@ -50,14 +84,14 @@ public boolean isValid(T value, ConstraintValidatorContext context) { protected abstract Duration getEffectiveTemporalValidationTolerance(Duration absoluteTemporalValidationTolerance); /** - * Returns the number of Years, Days, Months, etc. according to an unit {@code java.time.temporal.ChronoUnit} - * from a given {@code java.util.Calendar} to current day + * Returns the {@link Instant} measured from Epoch. */ - protected abstract long getCurrentAge(T value); + protected abstract Instant getInstant(T value); /** - * Returns whether the result of the comparison between the validated value and the age reference is considered + * Returns whether the result of the comparison between the validated value and the reference age is considered * valid. */ protected abstract boolean isValid(long result); + } diff --git a/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/hv/age/AbstractAgeTimeBasedValidator.java b/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/hv/age/AbstractAgeTimeBasedValidator.java index c81896e9ad..de2421fefa 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/hv/age/AbstractAgeTimeBasedValidator.java +++ b/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/hv/age/AbstractAgeTimeBasedValidator.java @@ -7,14 +7,19 @@ package org.hibernate.validator.internal.constraintvalidators.hv.age; import java.lang.annotation.Annotation; +import java.lang.invoke.MethodHandles; import java.time.Clock; import java.time.Duration; import java.time.temporal.ChronoUnit; import java.time.temporal.TemporalAccessor; +import javax.validation.ClockProvider; import javax.validation.ConstraintValidatorContext; import org.hibernate.validator.constraintvalidation.HibernateConstraintValidator; +import org.hibernate.validator.constraintvalidation.HibernateConstraintValidatorInitializationContext; +import org.hibernate.validator.internal.util.logging.Log; +import org.hibernate.validator.internal.util.logging.LoggerFactory; /** * Base class for all age validators that are based on the {@code java.time} package. @@ -25,7 +30,9 @@ public abstract class AbstractAgeTimeBasedValidator> implements HibernateConstraintValidator { - protected Clock referenceClock; + private static final Log LOG = LoggerFactory.make( MethodHandles.lookup() ); + + private Clock referenceClock; protected int referenceAge; @@ -33,6 +40,21 @@ public abstract class AbstractAgeTimeBasedValidator extends AbstractAgeInstantBasedValidator { - private static final Log LOG = LoggerFactory.make( MethodHandles.lookup() ); - @Override public void initialize(ConstraintDescriptor constraintDescriptor, HibernateConstraintValidatorInitializationContext initializationContext) { - try { - super.referenceClock = Clock.offset( - initializationContext.getClockProvider().getClock(), - getEffectiveTemporalValidationTolerance( initializationContext.getTemporalValidationTolerance() ) - ); - super.referenceAge = constraintDescriptor.getAnnotation().value(); - super.inclusive = constraintDescriptor.getAnnotation().inclusive(); - super.unit = constraintDescriptor.getAnnotation().unit(); - - } - catch (Exception e) { - throw LOG.getUnableToGetCurrentTimeFromClockProvider( e ); - } + super.initialize( constraintDescriptor.getAnnotation().value(), constraintDescriptor.getAnnotation().unit(), + constraintDescriptor.getAnnotation().inclusive(), initializationContext ); } @Override @@ -53,6 +37,6 @@ protected Duration getEffectiveTemporalValidationTolerance(Duration absoluteTemp @Override protected boolean isValid(long result) { - return super.inclusive ? result >= 0 : result > 0; + return this.inclusive ? result <= 0 : result < 0; } } diff --git a/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/hv/age/min/AbstractAgeMinTimeBasedValidator.java b/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/hv/age/min/AbstractAgeMinTimeBasedValidator.java index 57a2a9f086..3a73ea77fd 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/hv/age/min/AbstractAgeMinTimeBasedValidator.java +++ b/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/hv/age/min/AbstractAgeMinTimeBasedValidator.java @@ -6,8 +6,6 @@ */ package org.hibernate.validator.internal.constraintvalidators.hv.age.min; -import java.lang.invoke.MethodHandles; -import java.time.Clock; import java.time.Duration; import java.time.temporal.Temporal; import java.time.temporal.TemporalAccessor; @@ -17,11 +15,8 @@ import org.hibernate.validator.constraints.AgeMin; import org.hibernate.validator.constraintvalidation.HibernateConstraintValidatorInitializationContext; import org.hibernate.validator.internal.constraintvalidators.hv.age.AbstractAgeTimeBasedValidator; -import org.hibernate.validator.internal.util.logging.Log; -import org.hibernate.validator.internal.util.logging.LoggerFactory; /** - * * Base class for all {@code @AgeMin} validators that are based on the {@code java.time} package. * * @author Hillmer Chona @@ -30,23 +25,13 @@ public abstract class AbstractAgeMinTimeBasedValidator> extends AbstractAgeTimeBasedValidator { - private static final Log LOG = LoggerFactory.make( MethodHandles.lookup() ); - @Override - public void initialize(ConstraintDescriptor constraintDescriptor, HibernateConstraintValidatorInitializationContext initializationContext) { - try { - super.referenceClock = Clock.offset( - initializationContext.getClockProvider().getClock(), - getEffectiveTemporalValidationTolerance( initializationContext.getTemporalValidationTolerance() ) - ); - super.referenceAge = constraintDescriptor.getAnnotation().value(); - super.inclusive = constraintDescriptor.getAnnotation().inclusive(); - super.unit = constraintDescriptor.getAnnotation().unit(); - - } - catch (Exception e) { - throw LOG.getUnableToGetCurrentTimeFromClockProvider( e ); - } + public void initialize( + ConstraintDescriptor constraintDescriptor, + HibernateConstraintValidatorInitializationContext initializationContext) { + super.initialize( constraintDescriptor.getAnnotation().value(), constraintDescriptor.getAnnotation().unit(), + constraintDescriptor.getAnnotation().inclusive(), initializationContext + ); } @Override @@ -56,6 +41,7 @@ protected Duration getEffectiveTemporalValidationTolerance(Duration absoluteTemp @Override protected boolean isValid(long result) { - return super.inclusive ? result >= 0 : result > 0; + return super.inclusive ? result <= 0 : result < 0; } + } diff --git a/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/hv/age/min/AgeMinValidatorForCalendar.java b/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/hv/age/min/AgeMinValidatorForCalendar.java index 910e7d8731..92ee832d92 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/hv/age/min/AgeMinValidatorForCalendar.java +++ b/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/hv/age/min/AgeMinValidatorForCalendar.java @@ -1,9 +1,13 @@ +/* + * Hibernate Validator, declare and validate application constraints + * + * License: Apache License, Version 2.0 + * See the license.txt file in the root directory or . + */ package org.hibernate.validator.internal.constraintvalidators.hv.age.min; import java.time.Instant; -import java.time.LocalDate; -import java.time.ZoneId; -import java.time.ZonedDateTime; + import java.util.Calendar; /** @@ -15,18 +19,10 @@ * @since 6.0.8 */ public class AgeMinValidatorForCalendar extends AbstractAgeMinInstantBasedValidator { - @Override - protected long getCurrentAge(Calendar value) { - ZonedDateTime zdt1 = getInstant( value ).atZone( ZoneId.systemDefault() ); - LocalDate date1 = zdt1.toLocalDate(); - - ZonedDateTime zdt2 = super.referenceClock.instant().atZone( ZoneId.systemDefault() ); - LocalDate date2 = zdt2.toLocalDate(); - return super.unit.between( date1, date2 ); - } - - private Instant getInstant(Calendar value) { + @Override + protected Instant getInstant(Calendar value) { return value.toInstant(); } + } diff --git a/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/hv/age/min/AgeMinValidatorForDate.java b/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/hv/age/min/AgeMinValidatorForDate.java new file mode 100644 index 0000000000..cfd3dd6171 --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/hv/age/min/AgeMinValidatorForDate.java @@ -0,0 +1,27 @@ +/* + * Hibernate Validator, declare and validate application constraints + * + * License: Apache License, Version 2.0 + * See the license.txt file in the root directory or . + */ +package org.hibernate.validator.internal.constraintvalidators.hv.age.min; + +import java.time.Instant; + +import java.util.Date; + +/** + * Checks that the number of Years, Days, Months, etc. according to an unit {@code java.time.temporal.ChronoUnit} + * from a given {@code java.util.Calendar} to current day is greater than or equal to the specified value if inclusive is true + * or is greater when inclusive is false. + * + * @author Hillmer Chona + * @since 6.0.8 + */ +public class AgeMinValidatorForDate extends AbstractAgeMinInstantBasedValidator { + + @Override + protected Instant getInstant(Date value) { + return value.toInstant(); + } +} diff --git a/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/hv/age/min/AgeMinValidatorForHijrahDate.java b/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/hv/age/min/AgeMinValidatorForHijrahDate.java new file mode 100644 index 0000000000..f12e4da15a --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/hv/age/min/AgeMinValidatorForHijrahDate.java @@ -0,0 +1,26 @@ +/* + * Hibernate Validator, declare and validate application constraints + * + * License: Apache License, Version 2.0 + * See the license.txt file in the root directory or . + */ +package org.hibernate.validator.internal.constraintvalidators.hv.age.min; + +import java.time.Clock; +import java.time.chrono.HijrahDate; +import java.time.temporal.ChronoUnit; + +/** + * Checks that the number of Years, Days, Months, etc. according to an unit {@code java.time.temporal.ChronoUnit} + * from a given {@code java.time.chrono.HijrahDate} to current day is greater than or equal to the specified value if inclusive is true + * or is greater when inclusive is false. + * + * @author Hillmer Chona + * @since 6.0.8 + */ +public class AgeMinValidatorForHijrahDate extends AbstractAgeMinTimeBasedValidator { + @Override + protected HijrahDate getReferenceValue(Clock reference, int referenceAge, ChronoUnit unit) { + return HijrahDate.now( reference ).minus( referenceAge, unit ); + } +} diff --git a/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/hv/age/min/AgeMinValidatorForJapaneseDate.java b/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/hv/age/min/AgeMinValidatorForJapaneseDate.java new file mode 100644 index 0000000000..4e8da49f1a --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/hv/age/min/AgeMinValidatorForJapaneseDate.java @@ -0,0 +1,27 @@ +/* + * Hibernate Validator, declare and validate application constraints + * + * License: Apache License, Version 2.0 + * See the license.txt file in the root directory or . + */ +package org.hibernate.validator.internal.constraintvalidators.hv.age.min; + +import java.time.Clock; +import java.time.chrono.JapaneseDate; +import java.time.temporal.ChronoUnit; + +/** + * Checks that the number of Years, Days, Months, etc. according to an unit {@code java.time.temporal.ChronoUnit} + * from a given {@code java.time.chrono.JapaneseDate} to current day is greater than or equal to the specified value if inclusive is true + * or is greater when inclusive is false. + * + * @author Hillmer Chona + * @since 6.0.8 + */ +public class AgeMinValidatorForJapaneseDate extends AbstractAgeMinTimeBasedValidator { + @Override + protected JapaneseDate getReferenceValue( + Clock reference, int referenceAge, ChronoUnit unit) { + return JapaneseDate.now( reference ).minus( referenceAge, unit ); + } +} diff --git a/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/hv/age/min/AgeMinValidatorForLocalDate.java b/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/hv/age/min/AgeMinValidatorForLocalDate.java index cfa4b6edcb..26e2af26f2 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/hv/age/min/AgeMinValidatorForLocalDate.java +++ b/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/hv/age/min/AgeMinValidatorForLocalDate.java @@ -1,7 +1,14 @@ +/* + * Hibernate Validator, declare and validate application constraints + * + * License: Apache License, Version 2.0 + * See the license.txt file in the root directory or . + */ package org.hibernate.validator.internal.constraintvalidators.hv.age.min; import java.time.Clock; import java.time.LocalDate; +import java.time.temporal.ChronoUnit; /** * Checks that the number of Years, Days, Months, etc. according to an unit {@code java.time.temporal.ChronoUnit} @@ -14,12 +21,8 @@ public class AgeMinValidatorForLocalDate extends AbstractAgeMinTimeBasedValidator { @Override - protected long getCurrentAge(LocalDate value) { - return super.unit.between( value, getReferenceValue( super.referenceClock ) ); - } - - private LocalDate getReferenceValue(Clock reference) { - return LocalDate.now( reference ); + protected LocalDate getReferenceValue(Clock reference, int referenceAge, ChronoUnit unit) { + return LocalDate.now( reference ).minus( referenceAge, unit ); } } diff --git a/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/hv/age/min/AgeMinValidatorForMinguoDate.java b/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/hv/age/min/AgeMinValidatorForMinguoDate.java new file mode 100644 index 0000000000..660d2ca9f1 --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/hv/age/min/AgeMinValidatorForMinguoDate.java @@ -0,0 +1,27 @@ +/* + * Hibernate Validator, declare and validate application constraints + * + * License: Apache License, Version 2.0 + * See the license.txt file in the root directory or . + */ +package org.hibernate.validator.internal.constraintvalidators.hv.age.min; + +import java.time.Clock; +import java.time.chrono.MinguoDate; +import java.time.temporal.ChronoUnit; + +/** + * Checks that the number of Years, Days, Months, etc. according to an unit {@code java.time.temporal.ChronoUnit} + * from a given {@code java.time.chrono.MinguoDate} to current day is greater than or equal to the specified value if inclusive is true + * or is greater when inclusive is false. + * + * @author Hillmer Chona + * @since 6.0.8 + */ +public class AgeMinValidatorForMinguoDate extends AbstractAgeMinTimeBasedValidator { + + @Override + protected MinguoDate getReferenceValue(Clock reference, int referenceAge, ChronoUnit unit) { + return MinguoDate.now( reference ).minus( referenceAge, unit ); + } +} diff --git a/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/hv/age/min/AgeMinValidatorForThaiBuddhistDate.java b/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/hv/age/min/AgeMinValidatorForThaiBuddhistDate.java new file mode 100644 index 0000000000..13fbc11d9b --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/hv/age/min/AgeMinValidatorForThaiBuddhistDate.java @@ -0,0 +1,27 @@ +/* + * Hibernate Validator, declare and validate application constraints + * + * License: Apache License, Version 2.0 + * See the license.txt file in the root directory or . + */ +package org.hibernate.validator.internal.constraintvalidators.hv.age.min; + +import java.time.Clock; +import java.time.chrono.ThaiBuddhistDate; +import java.time.temporal.ChronoUnit; + +/** + * Checks that the number of Years, Days, Months, etc. according to an unit {@code java.time.temporal.ChronoUnit} + * from a given {@code java.time.chrono.ThaiBuddhistDate} to current day is greater than or equal to the specified value if inclusive is true + * or is greater when inclusive is false. + * + * @author Hillmer Chona + * @since 6.0.8 + */ +public class AgeMinValidatorForThaiBuddhistDate extends AbstractAgeMinTimeBasedValidator { + + @Override + protected ThaiBuddhistDate getReferenceValue(Clock reference, int referenceAge, ChronoUnit unit) { + return ThaiBuddhistDate.now( reference ).minus( referenceAge, unit ); + } +} diff --git a/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/hv/age/min/AgeMinValidatorForYear.java b/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/hv/age/min/AgeMinValidatorForYear.java new file mode 100644 index 0000000000..a112eac3eb --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/hv/age/min/AgeMinValidatorForYear.java @@ -0,0 +1,27 @@ +/* + * Hibernate Validator, declare and validate application constraints + * + * License: Apache License, Version 2.0 + * See the license.txt file in the root directory or . + */ +package org.hibernate.validator.internal.constraintvalidators.hv.age.min; + +import java.time.Clock; +import java.time.Year; +import java.time.temporal.ChronoUnit; + +/** + * Checks that the number of Years, Days, Months, etc. according to an unit {@code java.time.temporal.ChronoUnit} + * from a given {@code java.time.Year} to current day is greater than or equal to the specified value if inclusive is true + * or is greater when inclusive is false. + * + * @author Hillmer Chona + * @since 6.0.8 + */ +public class AgeMinValidatorForYear extends AbstractAgeMinTimeBasedValidator { + + @Override + protected Year getReferenceValue(Clock reference, int referenceAge, ChronoUnit unit) { + return Year.now( reference ).minus( referenceAge, unit ); + } +} diff --git a/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/hv/age/min/AgeMinValidatorForYearMonth.java b/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/hv/age/min/AgeMinValidatorForYearMonth.java new file mode 100644 index 0000000000..80e8c73e15 --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/hv/age/min/AgeMinValidatorForYearMonth.java @@ -0,0 +1,27 @@ +/* + * Hibernate Validator, declare and validate application constraints + * + * License: Apache License, Version 2.0 + * See the license.txt file in the root directory or . + */ +package org.hibernate.validator.internal.constraintvalidators.hv.age.min; + +import java.time.Clock; +import java.time.YearMonth; +import java.time.temporal.ChronoUnit; + +/** + * Checks that the number of Years, Days, Months, etc. according to an unit {@code java.time.temporal.ChronoUnit} + * from a given {@code java.time.YearMonth} to current day is greater than or equal to the specified value if inclusive is true + * or is greater when inclusive is false. + * + * @author Hillmer Chona + * @since 6.0.8 + */ +public class AgeMinValidatorForYearMonth extends AbstractAgeMinTimeBasedValidator { + + @Override + protected YearMonth getReferenceValue(Clock reference, int referenceAge, ChronoUnit unit) { + return YearMonth.now( reference ).minus( referenceAge, unit ); + } +} diff --git a/engine/src/main/java/org/hibernate/validator/internal/metadata/core/ConstraintHelper.java b/engine/src/main/java/org/hibernate/validator/internal/metadata/core/ConstraintHelper.java index c9a983847c..3061cb73df 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/metadata/core/ConstraintHelper.java +++ b/engine/src/main/java/org/hibernate/validator/internal/metadata/core/ConstraintHelper.java @@ -268,7 +268,14 @@ import org.hibernate.validator.internal.constraintvalidators.hv.URLValidator; import org.hibernate.validator.internal.constraintvalidators.hv.UniqueElementsValidator; import org.hibernate.validator.internal.constraintvalidators.hv.age.min.AgeMinValidatorForCalendar; +import org.hibernate.validator.internal.constraintvalidators.hv.age.min.AgeMinValidatorForDate; +import org.hibernate.validator.internal.constraintvalidators.hv.age.min.AgeMinValidatorForHijrahDate; +import org.hibernate.validator.internal.constraintvalidators.hv.age.min.AgeMinValidatorForJapaneseDate; import org.hibernate.validator.internal.constraintvalidators.hv.age.min.AgeMinValidatorForLocalDate; +import org.hibernate.validator.internal.constraintvalidators.hv.age.min.AgeMinValidatorForMinguoDate; +import org.hibernate.validator.internal.constraintvalidators.hv.age.min.AgeMinValidatorForThaiBuddhistDate; +import org.hibernate.validator.internal.constraintvalidators.hv.age.min.AgeMinValidatorForYear; +import org.hibernate.validator.internal.constraintvalidators.hv.age.min.AgeMinValidatorForYearMonth; import org.hibernate.validator.internal.constraintvalidators.hv.br.CNPJValidator; import org.hibernate.validator.internal.constraintvalidators.hv.br.CPFValidator; import org.hibernate.validator.internal.constraintvalidators.hv.pl.NIPValidator; @@ -465,9 +472,16 @@ public ConstraintHelper() { ) ); } - List>> ageMinValidators = new ArrayList<>( 2 ); - ageMinValidators.add( AgeMinValidatorForLocalDate.class ); + List>> ageMinValidators = new ArrayList<>( 9 ); ageMinValidators.add( AgeMinValidatorForCalendar.class ); + ageMinValidators.add( AgeMinValidatorForDate.class ); + ageMinValidators.add( AgeMinValidatorForHijrahDate.class ); + ageMinValidators.add( AgeMinValidatorForJapaneseDate.class ); + ageMinValidators.add( AgeMinValidatorForLocalDate.class ); + ageMinValidators.add( AgeMinValidatorForMinguoDate.class ); + ageMinValidators.add( AgeMinValidatorForThaiBuddhistDate.class ); + ageMinValidators.add( AgeMinValidatorForYear.class ); + ageMinValidators.add( AgeMinValidatorForYearMonth.class ); putConstraints( tmpConstraints, AgeMin.class, ageMinValidators ); diff --git a/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/hv/age/AgeValidatorTest.java b/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/hv/age/AgeValidatorTest.java index 129e8df061..3e7d405691 100644 --- a/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/hv/age/AgeValidatorTest.java +++ b/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/hv/age/AgeValidatorTest.java @@ -15,8 +15,12 @@ import static org.testng.Assert.assertTrue; import java.time.LocalDate; +import java.time.Year; +import java.time.chrono.JapaneseDate; +import java.time.chrono.MinguoDate; import java.time.temporal.ChronoUnit; import java.util.Calendar; +import java.util.Date; import java.util.Set; import javax.validation.ConstraintValidator; @@ -30,7 +34,11 @@ import org.hibernate.validator.constraints.AgeMin; import org.hibernate.validator.constraintvalidation.HibernateConstraintValidator; import org.hibernate.validator.internal.constraintvalidators.hv.age.min.AgeMinValidatorForCalendar; +import org.hibernate.validator.internal.constraintvalidators.hv.age.min.AgeMinValidatorForDate; +import org.hibernate.validator.internal.constraintvalidators.hv.age.min.AgeMinValidatorForJapaneseDate; import org.hibernate.validator.internal.constraintvalidators.hv.age.min.AgeMinValidatorForLocalDate; +import org.hibernate.validator.internal.constraintvalidators.hv.age.min.AgeMinValidatorForMinguoDate; +import org.hibernate.validator.internal.constraintvalidators.hv.age.min.AgeMinValidatorForYear; import org.hibernate.validator.internal.util.annotation.ConstraintAnnotationDescriptor; import org.hibernate.validator.testutil.TestForIssue; import org.hibernate.validator.testutils.ConstraintValidatorInitializationHelper; @@ -38,7 +46,9 @@ import org.testng.annotations.Test; /** - * A set of tests for {@link AgeMin} constraint validator ({@link AgeMinValidatorForLocalDate}, {@link AgeMinValidatorForCalendar}), which + * A set of tests for {@link AgeMin} constraint validator ({@link AgeMinValidatorForCalendar}, + * {@link AgeMinValidatorForDate}), {@link AgeMinValidatorForJapaneseDate}), {@link AgeMinValidatorForLocalDate}), + * {@link AgeMinValidatorForMinguoDate}), and {@link AgeMinValidatorForYear}), which * make sure that validation is performed correctly. * * @author Hillmer Chona @@ -51,9 +61,34 @@ public class AgeValidatorTest { private static final int MINIMUM_AGE_MONTHS = 240; + @Test + public void testProgrammaticDefinition() throws Exception { + HibernateValidatorConfiguration config = getConfiguration( HibernateValidator.class ); + ConstraintMapping mapping = config.createConstraintMapping(); + mapping.type( User.class ) + .property( "birthDate", FIELD ) + .constraint( new AgeMinDef().value( MINIMUM_AGE_YEARS ) ); + config.addMapping( mapping ); + Validator validator = config.buildValidatorFactory().getValidator(); + + LocalDate todayMinus18Years = LocalDate.now().minusYears( MINIMUM_AGE_YEARS ); + LocalDate tomorrowMinus18Years = LocalDate.now().plusDays( 1 ).minusYears( MINIMUM_AGE_YEARS ); + + Set> constraintViolations = validator.validate( new User( todayMinus18Years ) ); + assertNoViolations( constraintViolations ); + + constraintViolations = validator.validate( new User( tomorrowMinus18Years ) ); + assertThat( constraintViolations ).containsOnlyViolations( + violationOf( AgeMin.class ) + ); + } + + @Test public void testLocalDate() throws Exception { - ConstraintValidator constraintValidator = getInitializedValidator( MINIMUM_AGE_YEARS, true ); + + ConstraintValidator constraintValidator = getInitializedValidator( new AgeMinValidatorForLocalDate(), + MINIMUM_AGE_YEARS, true ); LocalDate todayMinus18Years = LocalDate.now().minusYears( MINIMUM_AGE_YEARS ); LocalDate todayMinus2MonthAnd18Years = LocalDate.now().minusMonths( 2 ).minusYears( MINIMUM_AGE_YEARS ); @@ -67,11 +102,14 @@ public void testLocalDate() throws Exception { @Test public void testInclusiveLocalDate() throws Exception { + ConstraintValidator constraintValidatorInclusiveTrue = getInitializedValidator( + new AgeMinValidatorForLocalDate(), MINIMUM_AGE_YEARS, true ); ConstraintValidator constraintValidatorInclusiveFalse = getInitializedValidator( + new AgeMinValidatorForLocalDate(), MINIMUM_AGE_YEARS, false ); @@ -82,31 +120,9 @@ public void testInclusiveLocalDate() throws Exception { assertInvalidAge( todayMinus18Years, constraintValidatorInclusiveFalse ); } - @Test - public void testProgrammaticDefinition() throws Exception { - HibernateValidatorConfiguration config = getConfiguration( HibernateValidator.class ); - ConstraintMapping mapping = config.createConstraintMapping(); - mapping.type( User.class ) - .property( "birthDate", FIELD ) - .constraint( new AgeMinDef().value( MINIMUM_AGE_YEARS ) ); - config.addMapping( mapping ); - Validator validator = config.buildValidatorFactory().getValidator(); - - LocalDate todayMinus18Years = LocalDate.now().minusYears( MINIMUM_AGE_YEARS ); - LocalDate tomorrowMinus18Years = LocalDate.now().plusDays( 1 ).minusYears( MINIMUM_AGE_YEARS ); - - Set> constraintViolations = validator.validate( new User( todayMinus18Years ) ); - assertNoViolations( constraintViolations ); - - constraintViolations = validator.validate( new User( tomorrowMinus18Years ) ); - assertThat( constraintViolations ).containsOnlyViolations( - violationOf( AgeMin.class ) - ); - } - @Test public void testLocalDateChronoUnits() throws Exception { - ConstraintValidator constraintValidator = getInitializedValidator( + ConstraintValidator constraintValidator = getInitializedValidatorForLocalDateWithUnit( MINIMUM_AGE_MONTHS, true, ChronoUnit.MONTHS @@ -122,10 +138,11 @@ public void testLocalDateChronoUnits() throws Exception { assertInvalidAge( tomorrowMinus18Years, constraintValidator ); } - @Test public void testCalendar() throws Exception { - ConstraintValidator constraintValidator = getInitializedValidatorForCalendar( + + ConstraintValidator constraintValidator = getInitializedValidator( + new AgeMinValidatorForCalendar(), MINIMUM_AGE_YEARS, true ); @@ -144,13 +161,15 @@ public void testCalendar() throws Exception { assertInvalidAge( tomorrowMinus18Years, constraintValidator ); } - //@Test + @Test public void testInclusiveCalendar() throws Exception { - ConstraintValidator constraintValidatorInclusiveTrue = getInitializedValidatorForCalendar( + ConstraintValidator constraintValidatorInclusiveTrue = getInitializedValidator( + new AgeMinValidatorForCalendar(), MINIMUM_AGE_YEARS, true ); - ConstraintValidator constraintValidatorInclusiveFalse = getInitializedValidatorForCalendar( + ConstraintValidator constraintValidatorInclusiveFalse = getInitializedValidator( + new AgeMinValidatorForCalendar(), MINIMUM_AGE_YEARS, false ); @@ -161,6 +180,89 @@ public void testInclusiveCalendar() throws Exception { assertInvalidAge( todayMinus18Years, constraintValidatorInclusiveFalse ); } + @Test + public void testDate() throws Exception { + + ConstraintValidator constraintValidator = getInitializedValidator( + new AgeMinValidatorForDate(), + MINIMUM_AGE_YEARS, + true + ); + + Calendar todayMinus18Years = getCalendarTodayMinus18Years(); + + Calendar todayMinus2MonthAnd18Years = getCalendarTodayMinus18Years(); + todayMinus2MonthAnd18Years.add( Calendar.MONTH, 2 * -1 ); + + Calendar tomorrowMinus18Years = getCalendarTodayMinus18Years(); + tomorrowMinus18Years.add( Calendar.DATE, 1 ); + + assertValidAge( null, constraintValidator ); + assertValidAge( todayMinus18Years.getTime(), constraintValidator ); + assertValidAge( todayMinus2MonthAnd18Years.getTime(), constraintValidator ); + assertInvalidAge( tomorrowMinus18Years.getTime(), constraintValidator ); + } + + @Test + public void testJapaneseDate() throws Exception { + + ConstraintValidator constraintValidator = getInitializedValidator( + new AgeMinValidatorForJapaneseDate(), + MINIMUM_AGE_YEARS, + true + ); + + JapaneseDate todayMinus18Years = JapaneseDate.now().minus( MINIMUM_AGE_YEARS, ChronoUnit.YEARS ); + JapaneseDate todayMinus2MonthAnd18Years = JapaneseDate.now().minus( 2, ChronoUnit.MONTHS ) + .minus( MINIMUM_AGE_YEARS, ChronoUnit.YEARS ); + JapaneseDate tomorrowMinus18Years = JapaneseDate.now().plus( 1, ChronoUnit.DAYS ) + .minus( MINIMUM_AGE_YEARS, ChronoUnit.YEARS ); + + assertValidAge( null, constraintValidator ); + assertValidAge( todayMinus18Years, constraintValidator ); + assertValidAge( todayMinus2MonthAnd18Years, constraintValidator ); + assertInvalidAge( tomorrowMinus18Years, constraintValidator ); + } + + @Test + public void testMinguoDate() throws Exception { + + ConstraintValidator constraintValidator = getInitializedValidator( + new AgeMinValidatorForMinguoDate(), + MINIMUM_AGE_YEARS, + true + ); + + MinguoDate todayMinus18Years = MinguoDate.now().minus( MINIMUM_AGE_YEARS, ChronoUnit.YEARS ); + MinguoDate todayMinus2MonthAnd18Years = MinguoDate.now().minus( 2, ChronoUnit.MONTHS ) + .minus( MINIMUM_AGE_YEARS, ChronoUnit.YEARS ); + MinguoDate tomorrowMinus18Years = MinguoDate.now().plus( 1, ChronoUnit.DAYS ) + .minus( MINIMUM_AGE_YEARS, ChronoUnit.YEARS ); + + assertValidAge( null, constraintValidator ); + assertValidAge( todayMinus18Years, constraintValidator ); + assertValidAge( todayMinus2MonthAnd18Years, constraintValidator ); + assertInvalidAge( tomorrowMinus18Years, constraintValidator ); + } + + @Test + public void testYear() throws Exception { + + ConstraintValidator constraintValidator = getInitializedValidator( + new AgeMinValidatorForYear(), + MINIMUM_AGE_YEARS, + true + ); + + Year todayMinus18Years = Year.now().minus( MINIMUM_AGE_YEARS, ChronoUnit.YEARS ); + Year nextYearMinus18Years = Year.now().plus( 1, ChronoUnit.YEARS ) + .minus( MINIMUM_AGE_YEARS, ChronoUnit.YEARS ); + + assertValidAge( null, constraintValidator ); + assertValidAge( todayMinus18Years, constraintValidator ); + assertInvalidAge( nextYearMinus18Years, constraintValidator ); + } + private Calendar getCalendarTodayMinus18Years() { Calendar calendar = Calendar.getInstance(); @@ -179,39 +281,26 @@ private Calendar getCalendarTodayMinus18Years() { /** * @return an initialized {@link ConstraintValidator} using {@code DUMMY_CONSTRAINT_VALIDATOR_INITIALIZATION_CONTEXT} */ - private ConstraintValidator getInitializedValidator(int value, boolean inclusive) { - HibernateConstraintValidator validator = new AgeMinValidatorForLocalDate(); - ConstraintAnnotationDescriptor.Builder descriptorBuilder = new ConstraintAnnotationDescriptor.Builder<>( - AgeMin.class ); - descriptorBuilder.setAttribute( "value", value ); - descriptorBuilder.setAttribute( "inclusive", inclusive ); - ConstraintAnnotationDescriptor descriptor = descriptorBuilder.build(); - ConstraintValidatorInitializationHelper.initialize( validator, descriptor ); - return validator; - } + private ConstraintValidator getInitializedValidator(HibernateConstraintValidator validator, + int value, boolean inclusive ) { - /** - * @return an initialized {@link ConstraintValidator} using {@code DUMMY_CONSTRAINT_VALIDATOR_INITIALIZATION_CONTEXT} - */ - private ConstraintValidator getInitializedValidatorForCalendar(int value, boolean inclusive) { - HibernateConstraintValidator validator = new AgeMinValidatorForCalendar(); ConstraintAnnotationDescriptor.Builder descriptorBuilder = new ConstraintAnnotationDescriptor.Builder<>( AgeMin.class ); descriptorBuilder.setAttribute( "value", value ); descriptorBuilder.setAttribute( "inclusive", inclusive ); + ConstraintAnnotationDescriptor descriptor = descriptorBuilder.build(); ConstraintValidatorInitializationHelper.initialize( validator, descriptor ); + return validator; } /** * @return an initialized {@link ConstraintValidator} using {@code DUMMY_CONSTRAINT_VALIDATOR_INITIALIZATION_CONTEXT} */ - private ConstraintValidator getInitializedValidator( - int value, - boolean inclusive, - ChronoUnit unit) { + private ConstraintValidator getInitializedValidatorForLocalDateWithUnit( int value, boolean inclusive, ChronoUnit unit) { HibernateConstraintValidator validator = new AgeMinValidatorForLocalDate(); + ConstraintAnnotationDescriptor.Builder descriptorBuilder = new ConstraintAnnotationDescriptor.Builder<>( AgeMin.class ); descriptorBuilder.setAttribute( "value", value ); @@ -223,28 +312,14 @@ private ConstraintValidator getInitializedValidator( return validator; } - private void assertValidAge(LocalDate birthDate, ConstraintValidator constraintValidator) { - assertTrue( - constraintValidator.isValid( birthDate, null ), - birthDate + " should be a date equal or more than " + MINIMUM_AGE_YEARS + " years before today" - ); - } - - private void assertInvalidAge(LocalDate birthDate, ConstraintValidator constraintValidator) { - assertFalse( - constraintValidator.isValid( birthDate, null ), - birthDate + " should be a date less than " + MINIMUM_AGE_YEARS + " years before today" - ); - } - - private void assertValidAge(Calendar birthDate, ConstraintValidator constraintValidator) { + private void assertValidAge(T birthDate, ConstraintValidator constraintValidator) { assertTrue( constraintValidator.isValid( birthDate, null ), birthDate + " should be a date equal or more than " + MINIMUM_AGE_YEARS + " years before today" ); } - private void assertInvalidAge(Calendar birthDate, ConstraintValidator constraintValidator) { + private void assertInvalidAge(T birthDate, ConstraintValidator constraintValidator) { assertFalse( constraintValidator.isValid( birthDate, null ), birthDate + " should be a date less than " + MINIMUM_AGE_YEARS + " years before today" From aafcad2adbd7ee3f3b472d1a3d027e6ff053a6d1 Mon Sep 17 00:00:00 2001 From: Hilmerc Date: Sun, 11 Feb 2018 10:20:57 -0500 Subject: [PATCH 7/8] HV-1552 Adding new MinAge Constraint --- .../hv/age/AgeValidatorConstrainedTest.java | 63 +++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 engine/src/test/java/org/hibernate/validator/test/constraints/annotations/hv/age/AgeValidatorConstrainedTest.java diff --git a/engine/src/test/java/org/hibernate/validator/test/constraints/annotations/hv/age/AgeValidatorConstrainedTest.java b/engine/src/test/java/org/hibernate/validator/test/constraints/annotations/hv/age/AgeValidatorConstrainedTest.java new file mode 100644 index 0000000000..9250861e61 --- /dev/null +++ b/engine/src/test/java/org/hibernate/validator/test/constraints/annotations/hv/age/AgeValidatorConstrainedTest.java @@ -0,0 +1,63 @@ +/* + * Hibernate Validator, declare and validate application constraints + * + * License: Apache License, Version 2.0 + * See the license.txt file in the root directory or . + */ +package org.hibernate.validator.test.constraints.annotations.hv.age; + +import static org.hibernate.validator.testutil.ConstraintViolationAssert.assertNoViolations; +import static org.hibernate.validator.testutil.ConstraintViolationAssert.assertThat; +import static org.hibernate.validator.testutil.ConstraintViolationAssert.violationOf; + +import java.time.LocalDate; +import java.time.temporal.ChronoUnit; +import java.util.Set; + +import javax.validation.ConstraintViolation; + +import org.hibernate.validator.constraints.AgeMin; +import org.hibernate.validator.test.constraints.annotations.AbstractConstrainedTest; +import org.hibernate.validator.testutil.TestForIssue; + +import org.testng.annotations.Test; + +/** + * Test to make sure that elements annotated with {@link AgeMin} are validated. + * + * @author Hillmer Chona + * @since 6.0.8 + */ +@TestForIssue(jiraKey = "HV-1552") +public class AgeValidatorConstrainedTest extends AbstractConstrainedTest { + + private static final int MINIMUM_AGE = 18; + + @Test + public void testMinAge() { + LocalDate todayMinus18Years = LocalDate.now().minusYears( MINIMUM_AGE ); + Foo foo = new Foo( todayMinus18Years ); + Set> violations = validator.validate( foo ); + assertNoViolations( violations ); + } + + @Test + public void testMinAgeInvalid() { + LocalDate tomorrowMinus18Years = LocalDate.now().plusDays( 1 ).minusYears( MINIMUM_AGE ); + Foo foo = new Foo( tomorrowMinus18Years ); + Set> violations = validator.validate( foo ); + assertThat( violations ).containsOnlyViolations( + violationOf( AgeMin.class ).withMessage( "must be older than " + MINIMUM_AGE ) + ); + } + + private static class Foo { + + @AgeMin(value = MINIMUM_AGE) + private final LocalDate birthDate; + + public Foo(LocalDate birthDate) { + this.birthDate = birthDate; + } + } +} From a46876f34f753e6a924fbd974598e75e81c4d15e Mon Sep 17 00:00:00 2001 From: Hilmerc Date: Thu, 22 Feb 2018 07:11:50 -0500 Subject: [PATCH 8/8] HV-1552 Adding new MinAge Constraint --- .../validator/cfg/defs/AgeMinDef.java | 4 +- .../validator/constraints/AgeMin.java | 48 ++++++++++++------- .../age/AbstractAgeInstantBasedValidator.java | 22 +++++---- .../hv/age/AbstractAgeTimeBasedValidator.java | 23 ++++++--- .../AbstractAgeMinInstantBasedValidator.java | 6 +-- .../min/AbstractAgeMinTimeBasedValidator.java | 10 ++-- .../age/min/AgeMinValidatorForCalendar.java | 2 + .../hv/age/min/AgeMinValidatorForYear.java | 5 +- .../hv/age/AgeValidatorConstrainedTest.java | 1 - .../hv/age/AgeValidatorTest.java | 38 ++++++++++----- 10 files changed, 103 insertions(+), 56 deletions(-) diff --git a/engine/src/main/java/org/hibernate/validator/cfg/defs/AgeMinDef.java b/engine/src/main/java/org/hibernate/validator/cfg/defs/AgeMinDef.java index bd0300ce3c..9b329cd2e7 100644 --- a/engine/src/main/java/org/hibernate/validator/cfg/defs/AgeMinDef.java +++ b/engine/src/main/java/org/hibernate/validator/cfg/defs/AgeMinDef.java @@ -9,8 +9,6 @@ import org.hibernate.validator.cfg.ConstraintDef; import org.hibernate.validator.constraints.AgeMin; -import java.time.temporal.ChronoUnit; - /** * @author Hillmer Chona * @since 6.0.8 @@ -26,7 +24,7 @@ public AgeMinDef value(int value) { return this; } - public AgeMinDef unit(ChronoUnit unit) { + public AgeMinDef unit(AgeMin.Unit unit) { addParameter( "unit", unit ); return this; } diff --git a/engine/src/main/java/org/hibernate/validator/constraints/AgeMin.java b/engine/src/main/java/org/hibernate/validator/constraints/AgeMin.java index de01a5fc72..baf6ce18d2 100644 --- a/engine/src/main/java/org/hibernate/validator/constraints/AgeMin.java +++ b/engine/src/main/java/org/hibernate/validator/constraints/AgeMin.java @@ -30,24 +30,23 @@ *

* Supported types are: *

*

* {@code null} elements are considered valid. * - * * @author Hillmer Chona * @since 6.0.8 */ -@Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE}) +@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE }) @Retention(RUNTIME) @Repeatable(AgeMin.List.class) @Documented @@ -66,20 +65,21 @@ int value(); /** - * Specifies the date period unit ( Years/Days/Months/etc. ) that will be used to compare the given instant, + * Specifies the date period unit ( Years/Days/Months. ) that will be used to compare the given instant, * date or time with the reference value. - * By default, it is ({@link ChronoUnit#YEARS}). + * By default, it is ({@link AgeMin.Unit#YEARS}). * * @return the date period unit */ - ChronoUnit unit() default ChronoUnit.YEARS; + + Unit unit() default Unit.YEARS; /** * Specifies whether the specified value is inclusive or exclusive. * By default, it is inclusive. * * @return {@code true} if the date period units from a given instant, date or time must be higher or equal to the specified value, - * {@code false} if date period units from a given instant, date or time must be higher + * {@code false} if date period units from a given instant, date or time must be higher */ boolean inclusive() default true; @@ -88,12 +88,28 @@ * * @see AgeMin */ - @Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE}) + @Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE }) @Retention(RUNTIME) @Documented @interface List { AgeMin[] value(); } + enum Unit { + YEARS( ChronoUnit.YEARS ), MONTHS( ChronoUnit.MONTHS ), DAYS( ChronoUnit.DAYS ); + + private final ChronoUnit chronoUnit; + + Unit(ChronoUnit chronoUnit) { + this.chronoUnit = chronoUnit; + + } + + public ChronoUnit getChronoUnit() { + return chronoUnit; + } + + } + } diff --git a/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/hv/age/AbstractAgeInstantBasedValidator.java b/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/hv/age/AbstractAgeInstantBasedValidator.java index f6486b6af5..386e548a43 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/hv/age/AbstractAgeInstantBasedValidator.java +++ b/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/hv/age/AbstractAgeInstantBasedValidator.java @@ -35,11 +35,11 @@ public abstract class AbstractAgeInstantBasedValidator private Clock referenceClock; - protected int referenceAge; + private int referenceAge; - protected boolean inclusive; + private boolean inclusive; - protected ChronoUnit unit; + private ChronoUnit unit; public void initialize( int referenceAge, @@ -69,15 +69,19 @@ public boolean isValid(T value, ConstraintValidatorContext context) { // As Instant does not support plus operation on ChronoUnits greater than DAYS we need to convert it to LocalDate // first, which supports such operations. - long result = getInstant( value ).atZone( ZoneOffset.ofHours( 0 ) ) - .toLocalDate() - .plus( referenceAge, unit ) - .compareTo( - LocalDate.now( referenceClock ) ); + int result = getInstant( value ).atZone( ZoneOffset.ofHours( 0 ) ).toLocalDate() + .compareTo( LocalDate.now( referenceClock ).minus( referenceAge, unit ) ); return isValid( result ); } + /** + * Returns whether the specified value is inclusive or exclusive. + */ + protected boolean isInclusive() { + return this.inclusive; + } + /** * Returns the temporal validation tolerance to apply. */ @@ -92,6 +96,6 @@ public boolean isValid(T value, ConstraintValidatorContext context) { * Returns whether the result of the comparison between the validated value and the reference age is considered * valid. */ - protected abstract boolean isValid(long result); + protected abstract boolean isValid(int result); } diff --git a/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/hv/age/AbstractAgeTimeBasedValidator.java b/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/hv/age/AbstractAgeTimeBasedValidator.java index de2421fefa..03146f42fd 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/hv/age/AbstractAgeTimeBasedValidator.java +++ b/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/hv/age/AbstractAgeTimeBasedValidator.java @@ -34,13 +34,17 @@ public abstract class AbstractAgeTimeBasedValidator extends AbstractAge @Override public void initialize(ConstraintDescriptor constraintDescriptor, HibernateConstraintValidatorInitializationContext initializationContext) { - super.initialize( constraintDescriptor.getAnnotation().value(), constraintDescriptor.getAnnotation().unit(), + super.initialize( constraintDescriptor.getAnnotation().value(), constraintDescriptor.getAnnotation().unit().getChronoUnit(), constraintDescriptor.getAnnotation().inclusive(), initializationContext ); } @@ -36,7 +36,7 @@ protected Duration getEffectiveTemporalValidationTolerance(Duration absoluteTemp } @Override - protected boolean isValid(long result) { - return this.inclusive ? result <= 0 : result < 0; + protected boolean isValid(int result) { + return isInclusive() ? result <= 0 : result < 0; } } diff --git a/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/hv/age/min/AbstractAgeMinTimeBasedValidator.java b/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/hv/age/min/AbstractAgeMinTimeBasedValidator.java index 3a73ea77fd..db4cbf2c0c 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/hv/age/min/AbstractAgeMinTimeBasedValidator.java +++ b/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/hv/age/min/AbstractAgeMinTimeBasedValidator.java @@ -29,8 +29,10 @@ public abstract class AbstractAgeMinTimeBasedValidator constraintDescriptor, HibernateConstraintValidatorInitializationContext initializationContext) { - super.initialize( constraintDescriptor.getAnnotation().value(), constraintDescriptor.getAnnotation().unit(), - constraintDescriptor.getAnnotation().inclusive(), initializationContext + super.initialize( constraintDescriptor.getAnnotation().value(), + constraintDescriptor.getAnnotation().unit().getChronoUnit(), + constraintDescriptor.getAnnotation().inclusive(), + initializationContext ); } @@ -40,8 +42,8 @@ protected Duration getEffectiveTemporalValidationTolerance(Duration absoluteTemp } @Override - protected boolean isValid(long result) { - return super.inclusive ? result <= 0 : result < 0; + protected boolean isValid(int result) { + return isInclusive() ? result <= 0 : result < 0; } } diff --git a/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/hv/age/min/AgeMinValidatorForCalendar.java b/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/hv/age/min/AgeMinValidatorForCalendar.java index 92ee832d92..084d29ecef 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/hv/age/min/AgeMinValidatorForCalendar.java +++ b/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/hv/age/min/AgeMinValidatorForCalendar.java @@ -25,4 +25,6 @@ protected Instant getInstant(Calendar value) { return value.toInstant(); } + + } diff --git a/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/hv/age/min/AgeMinValidatorForYear.java b/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/hv/age/min/AgeMinValidatorForYear.java index a112eac3eb..0272804d3b 100644 --- a/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/hv/age/min/AgeMinValidatorForYear.java +++ b/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/hv/age/min/AgeMinValidatorForYear.java @@ -7,6 +7,7 @@ package org.hibernate.validator.internal.constraintvalidators.hv.age.min; import java.time.Clock; +import java.time.LocalDateTime; import java.time.Year; import java.time.temporal.ChronoUnit; @@ -22,6 +23,8 @@ public class AgeMinValidatorForYear extends AbstractAgeMinTimeBasedValidator constraintValidator = getInitializedValidator( new AgeMinValidatorForLocalDate(), - MINIMUM_AGE_YEARS, true ); + MINIMUM_AGE_YEARS, true + ); LocalDate todayMinus18Years = LocalDate.now().minusYears( MINIMUM_AGE_YEARS ); LocalDate todayMinus2MonthAnd18Years = LocalDate.now().minusMonths( 2 ).minusYears( MINIMUM_AGE_YEARS ); @@ -122,10 +123,11 @@ public void testInclusiveLocalDate() throws Exception { @Test public void testLocalDateChronoUnits() throws Exception { - ConstraintValidator constraintValidator = getInitializedValidatorForLocalDateWithUnit( + ConstraintValidator constraintValidator = getInitializedValidatorWithUnit( + new AgeMinValidatorForLocalDate(), MINIMUM_AGE_MONTHS, true, - ChronoUnit.MONTHS + AgeMin.Unit.MONTHS ); LocalDate todayMinus18Years = LocalDate.now().minusMonths( MINIMUM_AGE_MONTHS ); @@ -248,15 +250,23 @@ public void testMinguoDate() throws Exception { @Test public void testYear() throws Exception { - ConstraintValidator constraintValidator = getInitializedValidator( + boolean inclusive = true; + ConstraintValidator constraintValidator = getInitializedValidatorWithUnit( new AgeMinValidatorForYear(), MINIMUM_AGE_YEARS, - true + inclusive, + AgeMin.Unit.MONTHS ); - Year todayMinus18Years = Year.now().minus( MINIMUM_AGE_YEARS, ChronoUnit.YEARS ); - Year nextYearMinus18Years = Year.now().plus( 1, ChronoUnit.YEARS ) - .minus( MINIMUM_AGE_YEARS, ChronoUnit.YEARS ); + Year todayMinus18Years = Year.from( LocalDate.now() + .minus( MINIMUM_AGE_YEARS, AgeMin.Unit.MONTHS.getChronoUnit() ) ); + Year nextYearMinus18Years = Year.from( LocalDate.now() + .plus( 1, ChronoUnit.YEARS ) + .minus( + MINIMUM_AGE_YEARS, + AgeMin.Unit.MONTHS.getChronoUnit() + ) ); + assertValidAge( null, constraintValidator ); assertValidAge( todayMinus18Years, constraintValidator ); @@ -281,8 +291,9 @@ private Calendar getCalendarTodayMinus18Years() { /** * @return an initialized {@link ConstraintValidator} using {@code DUMMY_CONSTRAINT_VALIDATOR_INITIALIZATION_CONTEXT} */ - private ConstraintValidator getInitializedValidator(HibernateConstraintValidator validator, - int value, boolean inclusive ) { + private ConstraintValidator getInitializedValidator( + HibernateConstraintValidator validator, + int value, boolean inclusive) { ConstraintAnnotationDescriptor.Builder descriptorBuilder = new ConstraintAnnotationDescriptor.Builder<>( AgeMin.class ); @@ -298,8 +309,9 @@ private ConstraintValidator getInitializedValidator(HibernateCons /** * @return an initialized {@link ConstraintValidator} using {@code DUMMY_CONSTRAINT_VALIDATOR_INITIALIZATION_CONTEXT} */ - private ConstraintValidator getInitializedValidatorForLocalDateWithUnit( int value, boolean inclusive, ChronoUnit unit) { - HibernateConstraintValidator validator = new AgeMinValidatorForLocalDate(); + private ConstraintValidator getInitializedValidatorWithUnit( + HibernateConstraintValidator validator, + int value, boolean inclusive, AgeMin.Unit unit) { ConstraintAnnotationDescriptor.Builder descriptorBuilder = new ConstraintAnnotationDescriptor.Builder<>( AgeMin.class ); @@ -312,7 +324,7 @@ private ConstraintValidator getInitializedValidatorForLocalDa return validator; } - private void assertValidAge(T birthDate, ConstraintValidator constraintValidator) { + private void assertValidAge(T birthDate, ConstraintValidator constraintValidator) { assertTrue( constraintValidator.isValid( birthDate, null ), birthDate + " should be a date equal or more than " + MINIMUM_AGE_YEARS + " years before today"