Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,12 @@ public boolean isValid(final String value, final String pattern, final Locale lo
/**
* Tests if the value is less than or equal to a maximum.
*
* <p>
* For an integer validator the bound is compared exactly rather than narrowed to a {@code long}, so a {@code BigInteger} or {@code BigDecimal} bound outside
* the long range keeps its magnitude and a fractional bound keeps its fractional part. A non-finite {@link Double} or {@link Float} operand keeps the
* {@code doubleValue()} comparison, since {@code BigDecimal} cannot represent {@code NaN} or an infinity.
* </p>
*
* @param value The value validation is being performed on.
* @param max The maximum value.
* @return {@code true} if the value is less than or equal to the maximum.
Expand All @@ -280,12 +286,18 @@ public boolean maxValue(final Number value, final Number max) {
if (isAllowFractions()) {
return value.doubleValue() <= max.doubleValue();
}
return value.longValue() <= max.longValue();
return isFinite(value) && isFinite(max) ? compareTo(value, max) <= 0 : value.doubleValue() <= max.doubleValue();
}

/**
* Tests if the value is greater than or equal to a minimum.
*
* <p>
* For an integer validator the bound is compared exactly rather than narrowed to a {@code long}, so a {@code BigInteger} or {@code BigDecimal} bound outside
* the long range keeps its magnitude and a fractional bound keeps its fractional part. A non-finite {@link Double} or {@link Float} operand keeps the
* {@code doubleValue()} comparison, since {@code BigDecimal} cannot represent {@code NaN} or an infinity.
* </p>
*
* @param value The value validation is being performed on.
* @param min The minimum value.
* @return {@code true} if the value is greater than or equal to the minimum.
Expand All @@ -294,7 +306,7 @@ public boolean minValue(final Number value, final Number min) {
if (isAllowFractions()) {
return value.doubleValue() >= min.doubleValue();
}
return value.longValue() >= min.longValue();
return isFinite(value) && isFinite(min) ? compareTo(value, min) >= 0 : value.doubleValue() >= min.doubleValue();
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertTrue;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Locale;

import org.junit.jupiter.api.BeforeEach;
Expand Down Expand Up @@ -111,6 +113,21 @@ void testByteRangeMinMax() {
assertFalse(validator.maxValue(number21, max), "maxValue() > max");
}

/**
* The inherited {@link Number}-typed range overloads must compare the exact bound rather than one narrowed to a {@code long}.
*/
@Test
void testNumberRangeExactBound() {
final AbstractNumberValidator instance = ByteValidator.getInstance();
final Number value = Byte.valueOf((byte) 100);
// A bound above the long range narrows to a negative long, wrongly reporting 100 as above the maximum.
final Number aboveLongMax = BigInteger.valueOf(Long.MAX_VALUE).add(BigInteger.ONE);
assertTrue(instance.maxValue(value, aboveLongMax));
assertTrue(instance.isInRange(value, BigInteger.ZERO, aboveLongMax));
// A fractional bound is not floored: 5 >= 5.5 is false.
assertFalse(instance.minValue(Byte.valueOf((byte) 5), new BigDecimal("5.5")));
}

/**
* Test ByteValidator validate Methods
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,10 @@
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertTrue;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.text.DecimalFormatSymbols;
import java.text.NumberFormat;
import java.util.Locale;
Expand All @@ -45,6 +47,22 @@ protected void setUp() {
ukPound = new DecimalFormatSymbols(Locale.UK).getCurrencySymbol();
}

/**
* The {@link Number}-typed range overloads inherited through {@link BigDecimalValidator} from {@link AbstractNumberValidator} must compare the exact bound,
* so a {@code BigInteger} or {@code BigDecimal} bound outside the long range or a fractional bound is not silently truncated.
*/
@Test
void testNumberRangeExactBound() {
final AbstractNumberValidator instance = CurrencyValidator.getInstance();
final Number value = new BigDecimal("100");
// A bound above the long range must not narrow to a negative long and wrongly report 100 as above the maximum.
final Number aboveLongMax = BigInteger.valueOf(Long.MAX_VALUE).add(BigInteger.ONE);
assertTrue(instance.maxValue(value, aboveLongMax));
assertTrue(instance.isInRange(value, BigInteger.ZERO, aboveLongMax));
// A fractional bound is not floored: 5 >= 5.5 is false.
assertFalse(instance.minValue(new BigDecimal("5"), new BigDecimal("5.5")));
}

/**
* Test Format Type
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.text.NumberFormat;
Expand Down Expand Up @@ -136,6 +138,22 @@ void testFloatRangeMinMaxNaN() {
assertFalse(validator.maxValue(Float.NaN, 20), "maxValue() NaN");
}

/**
* The {@link Number}-typed range overloads inherited from {@link AbstractNumberValidator} must compare the exact bound, so a {@code BigInteger} or
* {@code BigDecimal} bound outside the long range or a fractional bound is not silently truncated.
*/
@Test
void testNumberRangeExactBound() {
final AbstractNumberValidator instance = FloatValidator.getInstance();
final Number value = Float.valueOf(100);
// A bound above the long range must not narrow to a negative long and wrongly report 100 as above the maximum.
final Number aboveLongMax = BigInteger.valueOf(Long.MAX_VALUE).add(BigInteger.ONE);
assertTrue(instance.maxValue(value, aboveLongMax));
assertTrue(instance.isInRange(value, BigInteger.ZERO, aboveLongMax));
// A fractional bound is not floored: 5 >= 5.5 is false.
assertFalse(instance.minValue(Float.valueOf(5), new BigDecimal("5.5")));
}

/**
* Test Float validation for values too small to handle. (slightly different from max/min which are the largest +ve/-ve
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertTrue;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Locale;

import org.junit.jupiter.api.BeforeEach;
Expand Down Expand Up @@ -149,4 +151,19 @@ void testMinMaxValues() {
assertTrue(validator.isValid("-2147483648"), "-2147483648 is min integer");
assertFalse(validator.isValid("-2147483649"), "-2147483649 < min integer");
}

/**
* The inherited {@link Number}-typed range overloads must compare the exact bound rather than one narrowed to a {@code long}.
*/
@Test
void testNumberRangeExactBound() {
final AbstractNumberValidator instance = IntegerValidator.getInstance();
final Number value = Integer.valueOf(100);
// A bound above the long range narrows to a negative long, wrongly reporting 100 as above the maximum.
final Number aboveLongMax = BigInteger.valueOf(Long.MAX_VALUE).add(BigInteger.ONE);
assertTrue(instance.maxValue(value, aboveLongMax));
assertTrue(instance.isInRange(value, BigInteger.ZERO, aboveLongMax));
// A fractional bound is not floored: 5 >= 5.5 is false.
assertFalse(instance.minValue(Integer.valueOf(5), new BigDecimal("5.5")));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertTrue;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Locale;

import org.junit.jupiter.api.BeforeEach;
Expand Down Expand Up @@ -111,6 +113,28 @@ void testLongRangeMinMax() {
assertFalse(validator.maxValue(number21, 20), "maxValue() > max");
}

/**
* The {@link Number}-typed range overloads inherited from {@link AbstractNumberValidator} must compare the exact bound rather than one narrowed to a
* {@code long}, so a {@code BigInteger}/{@code BigDecimal} bound outside the long range or a fractional bound is not silently truncated.
*/
@Test
void testNumberRangeExactBound() {
final AbstractNumberValidator instance = LongValidator.getInstance();
final Number value = Long.valueOf(Long.MAX_VALUE);
// One above Long.MAX_VALUE narrows to Long.MIN_VALUE, wrongly reporting Long.MAX_VALUE as above the maximum.
final Number aboveLongMax = BigInteger.valueOf(Long.MAX_VALUE).add(BigInteger.ONE);
assertTrue(instance.maxValue(value, aboveLongMax));
assertTrue(instance.isInRange(value, BigInteger.ZERO, aboveLongMax));
// A fractional bound is not floored: 5 >= 5.5 is false, 5 <= 5.5 is true.
assertFalse(instance.minValue(Long.valueOf(5), Double.valueOf(5.5)));
assertFalse(instance.minValue(Long.valueOf(5), new BigDecimal("5.5")));
assertTrue(instance.maxValue(Long.valueOf(5), new BigDecimal("5.5")));
// A non-finite bound falls back to the double comparison, matching the Big* validators.
assertFalse(instance.minValue(value, Double.NaN));
assertTrue(instance.maxValue(value, Double.POSITIVE_INFINITY));
assertTrue(instance.minValue(value, Double.NEGATIVE_INFINITY));
}

/**
* Test LongValidator validate Methods
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,10 @@
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertTrue;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Locale;

import org.junit.jupiter.api.AfterEach;
Expand Down Expand Up @@ -51,6 +53,22 @@ protected void tearDown() {
validator = null;
}

/**
* The {@link Number}-typed range overloads inherited through {@link BigDecimalValidator} from {@link AbstractNumberValidator} must compare the exact bound,
* so a {@code BigInteger} or {@code BigDecimal} bound outside the long range or a fractional bound is not silently truncated.
*/
@Test
void testNumberRangeExactBound() {
final AbstractNumberValidator instance = PercentValidator.getInstance();
final Number value = new BigDecimal("100");
// A bound above the long range must not narrow to a negative long and wrongly report 100 as above the maximum.
final Number aboveLongMax = BigInteger.valueOf(Long.MAX_VALUE).add(BigInteger.ONE);
assertTrue(instance.maxValue(value, aboveLongMax));
assertTrue(instance.isInRange(value, BigInteger.ZERO, aboveLongMax));
// A fractional bound is not floored: 5 >= 5.5 is false.
assertFalse(instance.minValue(new BigDecimal("5"), new BigDecimal("5.5")));
}

/**
* Test Format Type
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertTrue;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Locale;

import org.junit.jupiter.api.BeforeEach;
Expand Down Expand Up @@ -102,6 +104,21 @@ void testShortRangeMinMax() {
assertFalse(validator.maxValue(number21, max), "maxValue() > max");
}

/**
* The inherited {@link Number}-typed range overloads must compare the exact bound rather than one narrowed to a {@code long}.
*/
@Test
void testNumberRangeExactBound() {
final AbstractNumberValidator instance = ShortValidator.getInstance();
final Number value = Short.valueOf((short) 100);
// A bound above the long range narrows to a negative long, wrongly reporting 100 as above the maximum.
final Number aboveLongMax = BigInteger.valueOf(Long.MAX_VALUE).add(BigInteger.ONE);
assertTrue(instance.maxValue(value, aboveLongMax));
assertTrue(instance.isInRange(value, BigInteger.ZERO, aboveLongMax));
// A fractional bound is not floored: 5 >= 5.5 is false.
assertFalse(instance.minValue(Short.valueOf((short) 5), new BigDecimal("5.5")));
}

/**
* Test ShortValidator validate Methods
*/
Expand Down
Loading