-
-
Notifications
You must be signed in to change notification settings - Fork 586
HV-1552 Adding new MinAge Constraint #913
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 5 commits
32f6e73
0c450ec
1d34b29
a93a9e5
4e74cd4
edd9954
aafcad2
a46876f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
/* | ||
* Hibernate Validator, declare and validate application constraints | ||
* | ||
* License: Apache License, Version 2.0 | ||
* See the license.txt file in the root directory or <http://www.apache.org/licenses/LICENSE-2.0>. | ||
*/ | ||
package org.hibernate.validator.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<AgeMinDef, AgeMin> { | ||
|
||
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; | ||
} | ||
|
||
public AgeMinDef unit(ChronoUnit unit) { | ||
addParameter( "unit", unit ); | ||
return this; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
/* | ||
* Hibernate Validator, declare and validate application constraints | ||
* | ||
* License: Apache License, Version 2.0 | ||
* See the license.txt file in the root directory or <http://www.apache.org/licenses/LICENSE-2.0>. | ||
*/ | ||
package org.hibernate.validator.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 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. | ||
* <p> | ||
* <p> | ||
* The supported type is {@code LocalDate}. {@code null} is considered valid. | ||
* The supported type is {@code Calendar}. {@code null} is considered valid. | ||
* <p> | ||
* | ||
* @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<? extends Payload>[] payload() default {}; | ||
|
||
/** | ||
* @return value the referenceAge according to unit from a given date must be greater or equal to | ||
|
||
*/ | ||
int value(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think in the previous discussion, there was an idea to try out to support other units ( |
||
|
||
/** | ||
* 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; | ||
|
||
|
||
/** | ||
* 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. | ||
* | ||
* @see AgeMin | ||
*/ | ||
@Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE}) | ||
@Retention(RUNTIME) | ||
@Documented | ||
@interface List { | ||
AgeMin[] value(); | ||
} | ||
|
||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 <http://www.apache.org/licenses/LICENSE-2.0>. | ||
*/ | ||
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<C extends Annotation, T> implements HibernateConstraintValidator<C, T> { | ||
|
||
protected Clock referenceClock; | ||
|
||
protected int referenceAge; | ||
|
||
protected boolean inclusive; | ||
|
||
protected ChronoUnit unit; | ||
|
||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think we can add 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;
} here. This way we will not need to repeat same logic for There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I tried to do something like you say, but no good ideas came to me, thanks. Done. |
||
@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); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 <http://www.apache.org/licenses/LICENSE-2.0>. | ||
*/ | ||
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<C extends Annotation, T extends TemporalAccessor & Comparable<? super T>> | ||
implements HibernateConstraintValidator<C, T> { | ||
|
||
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); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 <http://www.apache.org/licenses/LICENSE-2.0>. | ||
*/ | ||
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<T> extends AbstractAgeInstantBasedValidator<AgeMin, T> { | ||
|
||
private static final Log LOG = LoggerFactory.make( MethodHandles.lookup() ); | ||
|
||
@Override | ||
public void initialize(ConstraintDescriptor<AgeMin> 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; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
/* | ||
* Hibernate Validator, declare and validate application constraints | ||
* | ||
* License: Apache License, Version 2.0 | ||
* See the license.txt file in the root directory or <http://www.apache.org/licenses/LICENSE-2.0>. | ||
*/ | ||
package org.hibernate.validator.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.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 | ||
* @since 6.0.8 | ||
*/ | ||
public abstract class AbstractAgeMinTimeBasedValidator<T extends Temporal & TemporalAccessor & Comparable<? super T>> | ||
extends AbstractAgeTimeBasedValidator<AgeMin, T> { | ||
|
||
private static final Log LOG = LoggerFactory.make( MethodHandles.lookup() ); | ||
|
||
@Override | ||
public void initialize(ConstraintDescriptor<AgeMin> 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.negated(); | ||
} | ||
|
||
@Override | ||
protected boolean isValid(long result) { | ||
return super.inclusive ? result >= 0 : result > 0; | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Needs a license header comment as in the other classes
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi @marko-bekhta I have done all your suggestions except the one for ChronoUnit attribute. I'm not sure what you mean. Is it something like this: add to
@interface AgeMin
a attribute likeChronoUnit unit();
, so users can define the value when use the annotation like this:@AgeMin( value = MINIMUM_AGE , inclusive = true, unit= ChronoUnit.YEARS)
or
@AgeMin( value = MINIMUM_AGE , inclusive = true, unit= ChronoUnit.MONTHS)
?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi @Hilmerc yes, that's exactly it! :) This will make the constraint more versatile.
I've also prepared a short plan, with items that are still needed to finish this work. I'll post it in the separate comment. I hope it'll be helpful.