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..9b329cd2e7 --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/cfg/defs/AgeMinDef.java @@ -0,0 +1,36 @@ +/* + * 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; + +/** + * @author Hillmer Chona + * @since 6.0.8 + */ +public class AgeMinDef extends ConstraintDef { + + public AgeMinDef() { + super( AgeMin.class ); + } + + public AgeMinDef value(int value) { + addParameter( "value", value ); + return this; + } + + public AgeMinDef unit(AgeMin.Unit unit) { + addParameter( "unit", unit ); + return this; + } + + 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 new file mode 100644 index 0000000000..baf6ce18d2 --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/constraints/AgeMin.java @@ -0,0 +1,115 @@ +/* + * 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 java.time.temporal.ChronoUnit; + + +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 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: + *

    + *
  • {@code java.util.Calendar}
  • + *
  • {@code java.util.Date}
  • + *
  • {@code java.time.chrono.HijrahDate}
  • + *
  • {@code java.time.chrono.JapaneseDate}
  • + *
  • {@code java.time.LocalDate}
  • + *
  • {@code java.time.chrono.MinguoDate}
  • + *
  • {@code java.time.chrono.ThaiBuddhistDate}
  • + *
  • {@code java.time.Year}
  • + *
  • {@code java.time.YearMonth}
  • + *
+ *

+ * {@code null} elements are considered valid. + * + * @author Hillmer Chona + * @since 6.0.8 + */ +@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE }) +@Retention(RUNTIME) +@Repeatable(AgeMin.List.class) +@Documented +@Constraint(validatedBy = {}) +public @interface AgeMin { + + String message() default "{org.hibernate.validator.constraints.AgeMin.message}"; + + Class[] groups() default {}; + + Class[] payload() default {}; + + /** + * @return the age according to unit from a given instant, date or time must be greater or equal to + */ + int value(); + + /** + * 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 AgeMin.Unit#YEARS}). + * + * @return the date period unit + */ + + 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 + */ + boolean inclusive() default true; + + /** + * Defines several {@link AgeMin} annotations on the same element. + * + * @see AgeMin + */ + @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 new file mode 100644 index 0000000000..386e548a43 --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/hv/age/AbstractAgeInstantBasedValidator.java @@ -0,0 +1,101 @@ +/* + * 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.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. + * + * @author Hillmer Chona + * @since 6.0.8 + */ +public abstract class AbstractAgeInstantBasedValidator + implements HibernateConstraintValidator { + + private static final Log LOG = LoggerFactory.make( MethodHandles.lookup() ); + + private Clock referenceClock; + + private int referenceAge; + + private boolean inclusive; + + private 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. + + 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. + */ + protected abstract Duration getEffectiveTemporalValidationTolerance(Duration absoluteTemporalValidationTolerance); + + /** + * Returns the {@link Instant} measured from Epoch. + */ + protected abstract Instant getInstant(T value); + + /** + * Returns whether the result of the comparison between the validated value and the reference age is considered + * valid. + */ + 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 new file mode 100644 index 0000000000..03146f42fd --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/hv/age/AbstractAgeTimeBasedValidator.java @@ -0,0 +1,99 @@ +/* + * 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.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. + * + * @author Hillmer Chona + * @since 6.0.8 + */ +public abstract class AbstractAgeTimeBasedValidator> + implements HibernateConstraintValidator { + + private static final Log LOG = LoggerFactory.make( MethodHandles.lookup() ); + + private Clock referenceClock; + + private int referenceAge; + + private boolean inclusive; + + private 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; + } + + int result = value.compareTo( getReferenceValue( referenceClock, 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. + */ + protected abstract Duration getEffectiveTemporalValidationTolerance(Duration absoluteTemporalValidationTolerance); + + /** + * Returns an object of the validated type corresponding to the time reference as provided by the + * {@link ClockProvider} increased or decreased with the specified referenceAge of Years/Days/Months/etc. + * defined by {@link ChronoUnit}. + */ + protected abstract T getReferenceValue(Clock reference, int referenceAge, ChronoUnit unit); + + /** + * Returns whether the result of the comparison between the validated value and the age reference is considered + * valid. + */ + protected abstract boolean isValid(int 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..65d3e00ade --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/hv/age/min/AbstractAgeMinInstantBasedValidator.java @@ -0,0 +1,42 @@ +/* + * 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.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; + + +/** + * 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 { + + @Override + public void initialize(ConstraintDescriptor constraintDescriptor, HibernateConstraintValidatorInitializationContext initializationContext) { + super.initialize( constraintDescriptor.getAnnotation().value(), constraintDescriptor.getAnnotation().unit().getChronoUnit(), + constraintDescriptor.getAnnotation().inclusive(), initializationContext ); + } + + @Override + protected Duration getEffectiveTemporalValidationTolerance(Duration absoluteTemporalValidationTolerance) { + return absoluteTemporalValidationTolerance; + } + + @Override + 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 new file mode 100644 index 0000000000..db4cbf2c0c --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/hv/age/min/AbstractAgeMinTimeBasedValidator.java @@ -0,0 +1,49 @@ +/* + * 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.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.HibernateConstraintValidatorInitializationContext; +import org.hibernate.validator.internal.constraintvalidators.hv.age.AbstractAgeTimeBasedValidator; + +/** + * Base class for all {@code @AgeMin} validators that are based on the {@code java.time} package. + * + * @author Hillmer Chona + * @since 6.0.8 + */ +public abstract class AbstractAgeMinTimeBasedValidator> + extends AbstractAgeTimeBasedValidator { + + @Override + public void initialize( + ConstraintDescriptor constraintDescriptor, + HibernateConstraintValidatorInitializationContext initializationContext) { + super.initialize( constraintDescriptor.getAnnotation().value(), + constraintDescriptor.getAnnotation().unit().getChronoUnit(), + constraintDescriptor.getAnnotation().inclusive(), + initializationContext + ); + } + + @Override + protected Duration getEffectiveTemporalValidationTolerance(Duration absoluteTemporalValidationTolerance) { + return absoluteTemporalValidationTolerance.negated(); + } + + @Override + 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 new file mode 100644 index 0000000000..084d29ecef --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/hv/age/min/AgeMinValidatorForCalendar.java @@ -0,0 +1,30 @@ +/* + * 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.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 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 new file mode 100644 index 0000000000..26e2af26f2 --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/hv/age/min/AgeMinValidatorForLocalDate.java @@ -0,0 +1,28 @@ +/* + * 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} + * 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 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..0272804d3b --- /dev/null +++ b/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/hv/age/min/AgeMinValidatorForYear.java @@ -0,0 +1,30 @@ +/* + * 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.LocalDateTime; +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.from( LocalDateTime.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 9d0ca63446..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 @@ -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.AgeMin; import org.hibernate.validator.constraints.Mod10Check; import org.hibernate.validator.constraints.Mod11Check; import org.hibernate.validator.constraints.ModCheck; @@ -266,6 +267,15 @@ 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.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; @@ -462,6 +472,19 @@ public ConstraintHelper() { ) ); } + 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 ); + + 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..1a062109e4 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.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 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..5c24e3000e --- /dev/null +++ b/engine/src/test/java/org/hibernate/validator/test/constraints/annotations/hv/age/AgeValidatorConstrainedTest.java @@ -0,0 +1,62 @@ +/* + * 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.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; + } + } +} 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..336000b6d3 --- /dev/null +++ b/engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/hv/age/AgeValidatorTest.java @@ -0,0 +1,348 @@ +/* + * 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.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; +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.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; + +import org.testng.annotations.Test; + +/** + * 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 + * @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 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( new AgeMinValidatorForLocalDate(), + 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( + new AgeMinValidatorForLocalDate(), + MINIMUM_AGE_YEARS, + true + ); + ConstraintValidator constraintValidatorInclusiveFalse = getInitializedValidator( + new AgeMinValidatorForLocalDate(), + MINIMUM_AGE_YEARS, + false + ); + + LocalDate todayMinus18Years = LocalDate.now().minusYears( MINIMUM_AGE_YEARS ); + + assertValidAge( todayMinus18Years, constraintValidatorInclusiveTrue ); + assertInvalidAge( todayMinus18Years, constraintValidatorInclusiveFalse ); + } + + @Test + public void testLocalDateChronoUnits() throws Exception { + ConstraintValidator constraintValidator = getInitializedValidatorWithUnit( + new AgeMinValidatorForLocalDate(), + MINIMUM_AGE_MONTHS, + true, + AgeMin.Unit.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 = getInitializedValidator( + new AgeMinValidatorForCalendar(), + 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 = getInitializedValidator( + new AgeMinValidatorForCalendar(), + MINIMUM_AGE_YEARS, + true + ); + ConstraintValidator constraintValidatorInclusiveFalse = getInitializedValidator( + new AgeMinValidatorForCalendar(), + MINIMUM_AGE_YEARS, + false + ); + + Calendar todayMinus18Years = getCalendarTodayMinus18Years(); + + assertValidAge( todayMinus18Years, constraintValidatorInclusiveTrue ); + 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 { + + boolean inclusive = true; + ConstraintValidator constraintValidator = getInitializedValidatorWithUnit( + new AgeMinValidatorForYear(), + MINIMUM_AGE_YEARS, + inclusive, + AgeMin.Unit.MONTHS + ); + + 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 ); + assertInvalidAge( nextYearMinus18Years, constraintValidator ); + } + + 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( + HibernateConstraintValidator validator, + int value, boolean inclusive) { + + 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 getInitializedValidatorWithUnit( + HibernateConstraintValidator validator, + int value, boolean inclusive, AgeMin.Unit unit) { + + 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(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(T 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; + } + } +}