-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #403 from raynigon/feature/validation
Add Validation annotations for quantities
- Loading branch information
Showing
9 changed files
with
314 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
dependencies { | ||
implementation("jakarta.validation:jakarta.validation-api:3.0.2") | ||
implementation project(":unit-api-core") | ||
|
||
testImplementation 'com.fasterxml.jackson.core:jackson-databind:2.15.2' | ||
testImplementation 'org.hibernate.validator:hibernate-validator:8.0.1.Final' | ||
testImplementation("org.glassfish:jakarta.el:4.0.2") | ||
} |
55 changes: 55 additions & 0 deletions
55
unit-api-validation/src/main/java/com/raynigon/unit/api/validation/annotation/UnitMax.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
package com.raynigon.unit.api.validation.annotation; | ||
|
||
import com.raynigon.unit.api.validation.validator.UnitMaxValidator; | ||
import jakarta.validation.Constraint; | ||
import jakarta.validation.Payload; | ||
|
||
import javax.measure.Unit; | ||
import java.lang.annotation.Documented; | ||
import java.lang.annotation.Retention; | ||
import java.lang.annotation.Target; | ||
|
||
import static java.lang.annotation.ElementType.*; | ||
import static java.lang.annotation.RetentionPolicy.RUNTIME; | ||
|
||
/** | ||
* The annotated element must be a number whose value must be higher or | ||
* equal to the specified minimum. | ||
* <p> | ||
* Supported types are: | ||
* <ul> | ||
* <li>{@code Quantity}</li> | ||
* </ul> | ||
* Note that {@code double} and {@code float} are not supported due to rounding errors | ||
* (some providers might provide some approximative support). | ||
* <p> | ||
* {@code null} elements are considered valid. | ||
* | ||
* @author Simon Schneider | ||
*/ | ||
@Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE}) | ||
@Retention(RUNTIME) | ||
@Documented | ||
@Constraint(validatedBy = {UnitMaxValidator.class}) | ||
public @interface UnitMax { | ||
|
||
String message() default "{com.raynigon.unit.api.validation.UnitMax.message}"; | ||
|
||
Class<?>[] groups() default {}; | ||
|
||
Class<? extends Payload>[] payload() default {}; | ||
|
||
/** | ||
* The {@code String} representation of the min value according to the | ||
* {@code BigDecimal} string representation. | ||
* | ||
* @return value the element must be higher or equal to | ||
*/ | ||
double value(); | ||
|
||
/** | ||
* The unit which should be used to compare the value to | ||
*/ | ||
Class<? extends Unit<?>> unit(); | ||
} | ||
|
55 changes: 55 additions & 0 deletions
55
unit-api-validation/src/main/java/com/raynigon/unit/api/validation/annotation/UnitMin.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
package com.raynigon.unit.api.validation.annotation; | ||
|
||
import com.raynigon.unit.api.validation.validator.UnitMinValidator; | ||
import jakarta.validation.Constraint; | ||
import jakarta.validation.Payload; | ||
|
||
import javax.measure.Unit; | ||
import java.lang.annotation.Documented; | ||
import java.lang.annotation.Retention; | ||
import java.lang.annotation.Target; | ||
|
||
import static java.lang.annotation.ElementType.*; | ||
import static java.lang.annotation.RetentionPolicy.RUNTIME; | ||
|
||
/** | ||
* The annotated element must be a number whose value must be higher or | ||
* equal to the specified minimum. | ||
* <p> | ||
* Supported types are: | ||
* <ul> | ||
* <li>{@code Quantity}</li> | ||
* </ul> | ||
* Note that {@code double} and {@code float} are not supported due to rounding errors | ||
* (some providers might provide some approximative support). | ||
* <p> | ||
* {@code null} elements are considered valid. | ||
* | ||
* @author Simon Schneider | ||
*/ | ||
@Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE}) | ||
@Retention(RUNTIME) | ||
@Documented | ||
@Constraint(validatedBy = {UnitMinValidator.class}) | ||
public @interface UnitMin { | ||
|
||
String message() default "{com.raynigon.unit.api.validation.UnitMin.message}"; | ||
|
||
Class<?>[] groups() default {}; | ||
|
||
Class<? extends Payload>[] payload() default {}; | ||
|
||
/** | ||
* The {@code String} representation of the min value according to the | ||
* {@code BigDecimal} string representation. | ||
* | ||
* @return value the element must be higher or equal to | ||
*/ | ||
double value(); | ||
|
||
/** | ||
* The unit which should be used to compare the value to | ||
*/ | ||
Class<? extends Unit<?>> unit(); | ||
} | ||
|
46 changes: 46 additions & 0 deletions
46
...ation/src/main/java/com/raynigon/unit/api/validation/validator/AbstractUnitValidator.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
package com.raynigon.unit.api.validation.validator; | ||
|
||
import jakarta.validation.ConstraintValidator; | ||
|
||
import javax.measure.Quantity; | ||
import javax.measure.Unit; | ||
import java.lang.annotation.Annotation; | ||
import java.lang.reflect.Constructor; | ||
import java.lang.reflect.InvocationTargetException; | ||
import java.util.function.BiPredicate; | ||
|
||
abstract class AbstractUnitValidator<A extends Annotation> | ||
implements ConstraintValidator<A, Quantity<?>> { | ||
|
||
private Unit<?> unit = null; | ||
private double value = 0.0; | ||
|
||
@Override | ||
public void initialize(A constraintAnnotation) { | ||
unit = createUnit(getUnit(constraintAnnotation)); | ||
value = getValue(constraintAnnotation); | ||
} | ||
|
||
@SuppressWarnings({"rawtypes", "unchecked"}) | ||
protected boolean check(Quantity<?> quantity, BiPredicate<Double, Double> comparator) { | ||
if (quantity == null) return true; | ||
if (unit == null) return false; | ||
double quantityAsNumber = ((Quantity) quantity) | ||
.to(unit).getValue().doubleValue(); | ||
return comparator.test(quantityAsNumber, value); | ||
} | ||
|
||
protected abstract Class<? extends Unit<?>> getUnit(A constraintAnnotation); | ||
|
||
protected abstract double getValue(A constraintAnnotation); | ||
|
||
private static Unit<?> createUnit(Class<? extends Unit<?>> unitType) { | ||
try { | ||
Constructor<? extends Unit<?>> ctor = unitType.getConstructor(); | ||
return ctor.newInstance(); | ||
} catch (NoSuchMethodException | InstantiationException | IllegalAccessException | | ||
InvocationTargetException e) { | ||
throw new IllegalArgumentException("Unable to create Unit " + unitType, e); | ||
} | ||
} | ||
} |
25 changes: 25 additions & 0 deletions
25
...validation/src/main/java/com/raynigon/unit/api/validation/validator/UnitMaxValidator.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
package com.raynigon.unit.api.validation.validator; | ||
|
||
import com.raynigon.unit.api.validation.annotation.UnitMax; | ||
import jakarta.validation.ConstraintValidatorContext; | ||
|
||
import javax.measure.Quantity; | ||
import javax.measure.Unit; | ||
|
||
public class UnitMaxValidator extends AbstractUnitValidator<UnitMax> { | ||
|
||
@Override | ||
protected Class<? extends Unit<?>> getUnit(UnitMax constraintAnnotation) { | ||
return constraintAnnotation.unit(); | ||
} | ||
|
||
@Override | ||
protected double getValue(UnitMax constraintAnnotation) { | ||
return constraintAnnotation.value(); | ||
} | ||
|
||
@Override | ||
public boolean isValid(Quantity<?> value, ConstraintValidatorContext context) { | ||
return check(value, ((q, v) -> q <= v)); | ||
} | ||
} |
25 changes: 25 additions & 0 deletions
25
...validation/src/main/java/com/raynigon/unit/api/validation/validator/UnitMinValidator.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
package com.raynigon.unit.api.validation.validator; | ||
|
||
import com.raynigon.unit.api.validation.annotation.UnitMin; | ||
import jakarta.validation.ConstraintValidatorContext; | ||
|
||
import javax.measure.Quantity; | ||
import javax.measure.Unit; | ||
|
||
public class UnitMinValidator extends AbstractUnitValidator<UnitMin> { | ||
|
||
@Override | ||
protected Class<? extends Unit<?>> getUnit(UnitMin constraintAnnotation) { | ||
return constraintAnnotation.unit(); | ||
} | ||
|
||
@Override | ||
protected double getValue(UnitMin constraintAnnotation) { | ||
return constraintAnnotation.value(); | ||
} | ||
|
||
@Override | ||
public boolean isValid(Quantity<?> value, ConstraintValidatorContext context) { | ||
return check(value, ((q, v) -> q >= v)); | ||
} | ||
} |
49 changes: 49 additions & 0 deletions
49
...on/src/test/groovy/com/raynigon/unit/api/validation/validator/UnitMaxValidatorSpec.groovy
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
package com.raynigon.unit.api.validation.validator | ||
|
||
import com.raynigon.unit.api.core.units.si.SISystemUnitsConstants | ||
import com.raynigon.unit.api.core.units.si.power.Watt | ||
import com.raynigon.unit.api.validation.annotation.UnitMax | ||
import jakarta.validation.Validation | ||
import jakarta.validation.Validator | ||
import spock.lang.Specification | ||
|
||
import javax.measure.Quantity | ||
import javax.measure.quantity.Power | ||
|
||
class UnitMaxValidatorSpec extends Specification { | ||
|
||
Validator validator | ||
|
||
def setup() { | ||
validator = Validation.buildDefaultValidatorFactory().getValidator() | ||
} | ||
|
||
def "validate unit max with value=#input"() { | ||
given: | ||
DummyEntity entity = new DummyEntity(SISystemUnitsConstants.Watt(input)) | ||
|
||
when: | ||
def result = validator.validate(entity) | ||
|
||
then: | ||
result.size() == expected | ||
|
||
where: | ||
input | expected | ||
1 | 0 | ||
9 | 0 | ||
10 | 1 | ||
11 | 1 | ||
} | ||
|
||
static class DummyEntity { | ||
|
||
@UnitMax(value = 9.0, unit = Watt.class) | ||
Quantity<Power> power | ||
|
||
DummyEntity(Quantity<Power> power) { | ||
this.power = power | ||
} | ||
} | ||
} | ||
|
50 changes: 50 additions & 0 deletions
50
...on/src/test/groovy/com/raynigon/unit/api/validation/validator/UnitMinValidatorSpec.groovy
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
package com.raynigon.unit.api.validation.validator | ||
|
||
import com.raynigon.unit.api.core.units.si.SISystemUnitsConstants | ||
import com.raynigon.unit.api.core.units.si.power.Watt | ||
import com.raynigon.unit.api.validation.annotation.UnitMax | ||
import com.raynigon.unit.api.validation.annotation.UnitMin | ||
import jakarta.validation.Validation | ||
import jakarta.validation.Validator | ||
import spock.lang.Specification | ||
|
||
import javax.measure.Quantity | ||
import javax.measure.quantity.Power | ||
|
||
class UnitMinValidatorSpec extends Specification { | ||
|
||
Validator validator | ||
|
||
def setup() { | ||
validator = Validation.buildDefaultValidatorFactory().getValidator() | ||
} | ||
|
||
def "validate unit max with value=#input"() { | ||
given: | ||
DummyEntity entity = new DummyEntity(SISystemUnitsConstants.Watt(input)) | ||
|
||
when: | ||
def result = validator.validate(entity) | ||
|
||
then: | ||
result.size() == expected | ||
|
||
where: | ||
input | expected | ||
1 | 1 | ||
8 | 1 | ||
9 | 0 | ||
10 | 0 | ||
} | ||
|
||
static class DummyEntity { | ||
|
||
@UnitMin(value = 9.0, unit = Watt.class) | ||
Quantity<Power> power | ||
|
||
DummyEntity(Quantity<Power> power) { | ||
this.power = power | ||
} | ||
} | ||
} | ||
|