Skip to content

Commit 2c60f6b

Browse files
marko-bekhtagsmet
authored andcommitted
HV-1699 Change how numbers are compared in Max/Min validators
- for Min/Max `Number` validators check if actual numbers are not with floating point (float,double) and if they are direct them to correct number comparator helper method - for big numbers (BigDecimal, BigInteger) check in number comparator helper before converting to long. If actual numbers are from a 'Big*' classes direct the call to corresponding overloaded versions of the method so that no data will be lost - Compare floating point numbers using `Double#compare()`
1 parent 077827f commit 2c60f6b

File tree

5 files changed

+49
-5
lines changed

5 files changed

+49
-5
lines changed

engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/bv/number/bound/MaxValidatorForNumber.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
*/
77
package org.hibernate.validator.internal.constraintvalidators.bv.number.bound;
88

9+
import org.hibernate.validator.internal.constraintvalidators.bv.number.InfinityNumberComparatorHelper;
10+
911
/**
1012
* Check that the number being validated is less than or equal to the maximum
1113
* value specified.
@@ -16,6 +18,6 @@ public class MaxValidatorForNumber extends AbstractMaxValidator<Number> {
1618

1719
@Override
1820
protected int compare(Number number) {
19-
return NumberComparatorHelper.compare( number, maxValue );
21+
return NumberComparatorHelper.compare( number, maxValue, InfinityNumberComparatorHelper.GREATER_THAN );
2022
}
2123
}

engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/bv/number/bound/MinValidatorForNumber.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
*/
77
package org.hibernate.validator.internal.constraintvalidators.bv.number.bound;
88

9+
import org.hibernate.validator.internal.constraintvalidators.bv.number.InfinityNumberComparatorHelper;
10+
911
/**
1012
* Check that the number being validated is greater than or equal to the minimum
1113
* value specified.
@@ -16,6 +18,6 @@ public class MinValidatorForNumber extends AbstractMinValidator<Number> {
1618

1719
@Override
1820
protected int compare(Number number) {
19-
return NumberComparatorHelper.compare( number, minValue );
21+
return NumberComparatorHelper.compare( number, minValue, InfinityNumberComparatorHelper.LESS_THAN );
2022
}
2123
}

engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/bv/number/bound/NumberComparatorHelper.java

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,24 @@ public static int compare(Long number, long value) {
3232
return number.compareTo( value );
3333
}
3434

35-
public static int compare(Number number, long value) {
35+
public static int compare(Number number, long value, OptionalInt treatNanAs) {
36+
// In case of comparing numbers before we compare them as two longs:
37+
// 1. We need to check for floating point number as it should be treated differently in such case:
38+
if ( number instanceof Double ) {
39+
return compare( (Double) number, value, treatNanAs );
40+
}
41+
if ( number instanceof Float ) {
42+
return compare( (Float) number, value, treatNanAs );
43+
}
44+
45+
// 2. We need to check for big numbers so that we don't lose data when converting them to long:
46+
if ( number instanceof BigDecimal ) {
47+
return compare( (BigDecimal) number, value );
48+
}
49+
if ( number instanceof BigInteger ) {
50+
return compare( (BigInteger) number, value );
51+
}
52+
3653
return Long.compare( number.longValue(), value );
3754
}
3855

@@ -41,14 +58,14 @@ public static int compare(Double number, long value, OptionalInt treatNanAs) {
4158
if ( infinity.isPresent() ) {
4259
return infinity.getAsInt();
4360
}
44-
return Long.compare( number.longValue(), value );
61+
return Double.compare( number, value );
4562
}
4663

4764
public static int compare(Float number, long value, OptionalInt treatNanAs) {
4865
OptionalInt infinity = InfinityNumberComparatorHelper.infinityCheck( number, treatNanAs );
4966
if ( infinity.isPresent() ) {
5067
return infinity.getAsInt();
5168
}
52-
return Long.compare( number.longValue(), value );
69+
return Float.compare( number, value );
5370
}
5471
}

engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/bv/BaseMinMaxValidatorForNumberTest.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@ protected void testValidatorDouble(ConstraintValidator<?, Double> constraint, bo
9898

9999
assertTrue( constraint.isValid( null, null ) );
100100
assertEquals( constraint.isValid( 14.99, null ), isMax );
101+
assertEquals( constraint.isValid( 15.001, null ), !isMax );
101102
assertEquals( constraint.isValid( -14.99, null ), isMax );
102103
assertEquals( constraint.isValid( -1560000000D, null ), isMax );
103104
assertEquals( constraint.isValid( Double.NEGATIVE_INFINITY, null ), isMax );

engine/src/test/java/org/hibernate/validator/test/internal/constraintvalidators/bv/MaxValidatorForNumberTest.java

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,11 @@
66
*/
77
package org.hibernate.validator.test.internal.constraintvalidators.bv;
88

9+
import static org.testng.Assert.assertFalse;
10+
11+
import java.math.BigDecimal;
12+
import java.math.BigInteger;
13+
914
import javax.validation.constraints.DecimalMax;
1015
import javax.validation.constraints.Max;
1116

@@ -79,6 +84,23 @@ public void testIsValidDecimalMaxExclusive() {
7984

8085
}
8186

87+
@Test
88+
@TestForIssue(jiraKey = "HV-1699")
89+
public void testIsValidNumberForFloatingPointOrBigNumbersStoredAsNumber() {
90+
ConstraintAnnotationDescriptor.Builder<Max> descriptorBuilder = new ConstraintAnnotationDescriptor.Builder<>( Max.class );
91+
descriptorBuilder.setAttribute( "value", 1L );
92+
Max m = descriptorBuilder.build().getAnnotation();
93+
MaxValidatorForNumber validator = new MaxValidatorForNumber();
94+
validator.initialize( m );
95+
96+
assertFalse( validator.isValid( 1.01, null ) );
97+
assertFalse( validator.isValid( 1.01F, null ) );
98+
assertFalse( validator.isValid( new BigDecimal( "1.01" ), null ) );
99+
assertFalse( validator.isValid( new BigInteger( "2" ), null ) );
100+
assertFalse( validator.isValid( Double.POSITIVE_INFINITY, null ) );
101+
assertFalse( validator.isValid( Float.POSITIVE_INFINITY, null ) );
102+
}
103+
82104
private void testDecimalMax(DecimalMax m, boolean inclusive) {
83105
AbstractDecimalMaxValidator constraint = new DecimalMaxValidatorForNumber();
84106
constraint.initialize( m );

0 commit comments

Comments
 (0)