This is an automated email from the ASF dual-hosted git repository. garydgregory pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/commons-validator.git
commit 0e4d0cda735f422ce2625b3139561d6d7dc9f480 Author: Gary Gregory <[email protected]> AuthorDate: Fri Jul 3 14:30:09 2026 +0000 Sort members --- .../routines/AbstractCalendarValidator.java | 42 +++++++-------- .../validator/routines/DoubleValidator.java | 36 ++++++------- .../apache/commons/validator/ExtensionTest.java | 62 +++++++++++----------- .../routines/BigIntegerValidatorTest.java | 38 ++++++------- .../validator/routines/CalendarValidatorTest.java | 40 +++++++------- .../validator/routines/DoubleValidatorTest.java | 40 +++++++------- 6 files changed, 129 insertions(+), 129 deletions(-) diff --git a/src/main/java/org/apache/commons/validator/routines/AbstractCalendarValidator.java b/src/main/java/org/apache/commons/validator/routines/AbstractCalendarValidator.java index be88d07b..75468905 100644 --- a/src/main/java/org/apache/commons/validator/routines/AbstractCalendarValidator.java +++ b/src/main/java/org/apache/commons/validator/routines/AbstractCalendarValidator.java @@ -218,6 +218,27 @@ public abstract class AbstractCalendarValidator extends AbstractFormatValidator } + /** + * Compares the week two calendars fall in, ordering by the actual week rather than by the + * {@code WEEK_OF_YEAR} or {@code WEEK_OF_MONTH} number alone. Those numbers repeat across the + * boundaries they reset on (for example 31 December may be week 1 of the following year, and + * the first week of a month can hold days carried over from the previous month), so the gap + * between the two instants is checked first: dates a week or more apart are always in different + * weeks, and nearer dates share a week only when the week number also matches. + * + * @param value The Calendar value. + * @param compare The {@link Calendar} to check the value against. + * @param field {@code Calendar.WEEK_OF_YEAR} or {@code Calendar.WEEK_OF_MONTH}. + * @return Zero if both calendars are in the same week, -1 or +1 otherwise. + */ + private int compareWeek(final Calendar value, final Calendar compare, final int field) { + final long millis = value.getTimeInMillis() - compare.getTimeInMillis(); + if (Math.abs(millis) >= MILLIS_PER_WEEK || calculateCompareResult(value, compare, field) != 0) { + return Long.signum(millis); + } + return 0; + } + /** * Format a value with the specified {@code DateFormat}. * @@ -418,25 +439,4 @@ public abstract class AbstractCalendarValidator extends AbstractFormatValidator */ @Override protected abstract Object processParsedValue(Object value, Format formatter); - - /** - * Compares the week two calendars fall in, ordering by the actual week rather than by the - * {@code WEEK_OF_YEAR} or {@code WEEK_OF_MONTH} number alone. Those numbers repeat across the - * boundaries they reset on (for example 31 December may be week 1 of the following year, and - * the first week of a month can hold days carried over from the previous month), so the gap - * between the two instants is checked first: dates a week or more apart are always in different - * weeks, and nearer dates share a week only when the week number also matches. - * - * @param value The Calendar value. - * @param compare The {@link Calendar} to check the value against. - * @param field {@code Calendar.WEEK_OF_YEAR} or {@code Calendar.WEEK_OF_MONTH}. - * @return Zero if both calendars are in the same week, -1 or +1 otherwise. - */ - private int compareWeek(final Calendar value, final Calendar compare, final int field) { - final long millis = value.getTimeInMillis() - compare.getTimeInMillis(); - if (Math.abs(millis) >= MILLIS_PER_WEEK || calculateCompareResult(value, compare, field) != 0) { - return Long.signum(millis); - } - return 0; - } } diff --git a/src/main/java/org/apache/commons/validator/routines/DoubleValidator.java b/src/main/java/org/apache/commons/validator/routines/DoubleValidator.java index a8311600..a5fc7581 100644 --- a/src/main/java/org/apache/commons/validator/routines/DoubleValidator.java +++ b/src/main/java/org/apache/commons/validator/routines/DoubleValidator.java @@ -159,6 +159,24 @@ public class DoubleValidator extends AbstractNumberValidator { return maxValue(value.doubleValue(), max); } + /** + * Tests if the value is less than or equal to a maximum, comparing the exact values. + * + * <p> + * This overrides the {@link Number} overload inherited from the superclass, which narrows the bound to a {@code double} before comparing and so loses + * precision for a {@code BigDecimal} or {@code BigInteger} bound that carries more significant digits than a {@code double} can hold. A non-finite + * {@link Double} or {@link Float} operand keeps the {@code doubleValue()} comparison so the documented infinity behaviour is unchanged. + * </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. + */ + @Override + public boolean maxValue(final Number value, final Number max) { + return isFinite(value) && isFinite(max) ? compareTo(value, max) <= 0 : value.doubleValue() <= max.doubleValue(); + } + /** * Check if the value is greater than or equal to a minimum. * @@ -183,24 +201,6 @@ public class DoubleValidator extends AbstractNumberValidator { return minValue(value.doubleValue(), min); } - /** - * Tests if the value is less than or equal to a maximum, comparing the exact values. - * - * <p> - * This overrides the {@link Number} overload inherited from the superclass, which narrows the bound to a {@code double} before comparing and so loses - * precision for a {@code BigDecimal} or {@code BigInteger} bound that carries more significant digits than a {@code double} can hold. A non-finite - * {@link Double} or {@link Float} operand keeps the {@code doubleValue()} comparison so the documented infinity behaviour is unchanged. - * </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. - */ - @Override - public boolean maxValue(final Number value, final Number max) { - 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, comparing the exact values. * diff --git a/src/test/java/org/apache/commons/validator/ExtensionTest.java b/src/test/java/org/apache/commons/validator/ExtensionTest.java index 5737beb6..2bafa2de 100644 --- a/src/test/java/org/apache/commons/validator/ExtensionTest.java +++ b/src/test/java/org/apache/commons/validator/ExtensionTest.java @@ -79,37 +79,6 @@ class ExtensionTest { protected void tearDown() { } - /** - * Tests if the order is maintained when extending a form. Parent form fields should preceed self form fields, except if we override the rules. - */ - @Test - void testOrder() { - final Locale defaultLocale = Locale.getDefault(); - final Form form = resources.getForm(defaultLocale, FORM_KEY); - final Form form2 = resources.getForm(defaultLocale, FORM_KEY2); - - assertNotNull(form, FORM_KEY + " is null."); - assertEquals(2, form.getFields().size(), "There should only be 2 fields in " + FORM_KEY); - - assertNotNull(form2, FORM_KEY2 + " is null."); - assertEquals(2, form2.getFields().size(), "There should only be 2 fields in " + FORM_KEY2); - - // get the first field - Field fieldFirstName = form.getFields().get(0); - // get the second field - Field fieldLastName = form.getFields().get(1); - assertEquals("firstName", fieldFirstName.getKey(), "firstName in " + FORM_KEY + " should be the first in the list"); - assertEquals("lastName", fieldLastName.getKey(), "lastName in " + FORM_KEY + " should be the first in the list"); - -// get the second field - fieldLastName = form2.getFields().get(0); - // get the first field - fieldFirstName = form2.getFields().get(1); - assertEquals("firstName", fieldFirstName.getKey(), "firstName in " + FORM_KEY2 + " should be the first in the list"); - assertEquals("lastName", fieldLastName.getKey(), "lastName in " + FORM_KEY2 + " should be the first in the list"); - - } - /** * A form-set constant overrides a global constant of the same name, because {@link Field#process} applies the * form-set constants before the global ones. This precedence must also hold for a form reached through extension: @@ -147,6 +116,37 @@ class ExtensionTest { "the form-set constant should override the global constant for an extended form"); } + /** + * Tests if the order is maintained when extending a form. Parent form fields should preceed self form fields, except if we override the rules. + */ + @Test + void testOrder() { + final Locale defaultLocale = Locale.getDefault(); + final Form form = resources.getForm(defaultLocale, FORM_KEY); + final Form form2 = resources.getForm(defaultLocale, FORM_KEY2); + + assertNotNull(form, FORM_KEY + " is null."); + assertEquals(2, form.getFields().size(), "There should only be 2 fields in " + FORM_KEY); + + assertNotNull(form2, FORM_KEY2 + " is null."); + assertEquals(2, form2.getFields().size(), "There should only be 2 fields in " + FORM_KEY2); + + // get the first field + Field fieldFirstName = form.getFields().get(0); + // get the second field + Field fieldLastName = form.getFields().get(1); + assertEquals("firstName", fieldFirstName.getKey(), "firstName in " + FORM_KEY + " should be the first in the list"); + assertEquals("lastName", fieldLastName.getKey(), "lastName in " + FORM_KEY + " should be the first in the list"); + +// get the second field + fieldLastName = form2.getFields().get(0); + // get the first field + fieldFirstName = form2.getFields().get(1); + assertEquals("firstName", fieldFirstName.getKey(), "firstName in " + FORM_KEY2 + " should be the first in the list"); + assertEquals("lastName", fieldLastName.getKey(), "lastName in " + FORM_KEY2 + " should be the first in the list"); + + } + /** * Tests if we can override a rule. We "can" override a rule if the message shown when the firstName required test fails and the lastName test is null. */ diff --git a/src/test/java/org/apache/commons/validator/routines/BigIntegerValidatorTest.java b/src/test/java/org/apache/commons/validator/routines/BigIntegerValidatorTest.java index b99159bf..8e196ac8 100644 --- a/src/test/java/org/apache/commons/validator/routines/BigIntegerValidatorTest.java +++ b/src/test/java/org/apache/commons/validator/routines/BigIntegerValidatorTest.java @@ -220,25 +220,6 @@ class BigIntegerValidatorTest extends AbstractNumberValidatorTest { assertTrue(instance.maxValue(BigInteger.valueOf(-6), Double.valueOf(-5.9))); } - /** - * The {@link Number} overloads inherited from the superclass must compare the exact value, not a value narrowed to a long, for BigIntegers outside the long - * range. - */ - @Test - void testNumberRangeOutsideLongRange() { - final AbstractNumberValidator instance = BigIntegerValidator.getInstance(); - final Number min = BigInteger.valueOf(5); - final Number max = BigInteger.valueOf(100); - // 2^63 narrows to Long.MIN_VALUE, which the long-based comparison wrongly reports as below the range - final Number aboveMax = BigInteger.valueOf(Long.MAX_VALUE).add(BigInteger.ONE); - assertTrue(instance.minValue(aboveMax, min)); - assertFalse(instance.maxValue(aboveMax, max)); - // 2^64 + 50 narrows to 50, which the long-based comparison wrongly reports as in range - final Number wrapsIntoRange = BigInteger.ONE.shiftLeft(Long.SIZE).add(BigInteger.valueOf(50)); - assertEquals(50L, wrapsIntoRange.longValue()); - assertFalse(instance.isInRange(wrapsIntoRange, min, max)); - } - /** * A non-finite {@link Double} bound must not be routed through {@link BigDecimal}, which cannot represent {@code NaN} or an infinity. The {@link Number} * overloads previously converted every bound to a {@code BigDecimal} and so threw {@code NumberFormatException} for such a bound, whereas the sibling @@ -278,4 +259,23 @@ class BigIntegerValidatorTest extends AbstractNumberValidatorTest { assertFalse(instance.maxValue(posInf, nan)); assertFalse(instance.minValue(posInf, nan)); } + + /** + * The {@link Number} overloads inherited from the superclass must compare the exact value, not a value narrowed to a long, for BigIntegers outside the long + * range. + */ + @Test + void testNumberRangeOutsideLongRange() { + final AbstractNumberValidator instance = BigIntegerValidator.getInstance(); + final Number min = BigInteger.valueOf(5); + final Number max = BigInteger.valueOf(100); + // 2^63 narrows to Long.MIN_VALUE, which the long-based comparison wrongly reports as below the range + final Number aboveMax = BigInteger.valueOf(Long.MAX_VALUE).add(BigInteger.ONE); + assertTrue(instance.minValue(aboveMax, min)); + assertFalse(instance.maxValue(aboveMax, max)); + // 2^64 + 50 narrows to 50, which the long-based comparison wrongly reports as in range + final Number wrapsIntoRange = BigInteger.ONE.shiftLeft(Long.SIZE).add(BigInteger.valueOf(50)); + assertEquals(50L, wrapsIntoRange.longValue()); + assertFalse(instance.isInRange(wrapsIntoRange, min, max)); + } } diff --git a/src/test/java/org/apache/commons/validator/routines/CalendarValidatorTest.java b/src/test/java/org/apache/commons/validator/routines/CalendarValidatorTest.java index a28105ac..c74313fb 100644 --- a/src/test/java/org/apache/commons/validator/routines/CalendarValidatorTest.java +++ b/src/test/java/org/apache/commons/validator/routines/CalendarValidatorTest.java @@ -232,6 +232,26 @@ class CalendarValidatorTest extends AbstractCalendarValidatorTest { assertEquals("Invalid field: -1", e.getMessage(), "check message"); } + /** + * Test WEEK_OF_MONTH comparison across a month/year boundary, where the week number resets and + * the first week of a month can hold days carried over from the previous month. + */ + @Test + @DefaultLocale(country = "US", language = "en") + void testCompareWeekOfMonthAcrossBoundary() { + final int noon = 120000; + final Calendar dec30y2018 = createCalendar(TimeZones.GMT, 20181230, noon); + final Calendar dec31y2018 = createCalendar(TimeZones.GMT, 20181231, noon); + final Calendar jan01y2019 = createCalendar(TimeZones.GMT, 20190101, noon); + + // 30 and 31 Dec 2018 fall in the same week of December + assertEquals(0, calValidator.compare(dec30y2018, dec31y2018, Calendar.WEEK_OF_MONTH), "Dec 30 and 31 2018 are the same week of month"); + + // 31 Dec 2018 and 1 Jan 2019 are one day apart but lie in different weeks of their months + assertEquals(-1, calValidator.compare(dec31y2018, jan01y2019, Calendar.WEEK_OF_MONTH), "Dec 31 2018 is before Jan 1 2019 by week of month"); + assertEquals(1, calValidator.compare(jan01y2019, dec31y2018, Calendar.WEEK_OF_MONTH), "Jan 1 2019 is after Dec 31 2018 by week of month"); + } + /** * Test compareWeeks() across the year boundary, where WEEK_OF_YEAR repeats: 31 December can be * week 1 of the next year while 1 January of the same calendar year is also week 1. @@ -258,26 +278,6 @@ class CalendarValidatorTest extends AbstractCalendarValidatorTest { assertEquals(0, calValidator.compareWeeks(dec30y2018, jan05y2019), "Dec 30 2018 is the same week as Jan 5 2019"); } - /** - * Test WEEK_OF_MONTH comparison across a month/year boundary, where the week number resets and - * the first week of a month can hold days carried over from the previous month. - */ - @Test - @DefaultLocale(country = "US", language = "en") - void testCompareWeekOfMonthAcrossBoundary() { - final int noon = 120000; - final Calendar dec30y2018 = createCalendar(TimeZones.GMT, 20181230, noon); - final Calendar dec31y2018 = createCalendar(TimeZones.GMT, 20181231, noon); - final Calendar jan01y2019 = createCalendar(TimeZones.GMT, 20190101, noon); - - // 30 and 31 Dec 2018 fall in the same week of December - assertEquals(0, calValidator.compare(dec30y2018, dec31y2018, Calendar.WEEK_OF_MONTH), "Dec 30 and 31 2018 are the same week of month"); - - // 31 Dec 2018 and 1 Jan 2019 are one day apart but lie in different weeks of their months - assertEquals(-1, calValidator.compare(dec31y2018, jan01y2019, Calendar.WEEK_OF_MONTH), "Dec 31 2018 is before Jan 1 2019 by week of month"); - assertEquals(1, calValidator.compare(jan01y2019, dec31y2018, Calendar.WEEK_OF_MONTH), "Jan 1 2019 is after Dec 31 2018 by week of month"); - } - /** * Test Date/Time style Validator (there isn't an implementation for this) */ diff --git a/src/test/java/org/apache/commons/validator/routines/DoubleValidatorTest.java b/src/test/java/org/apache/commons/validator/routines/DoubleValidatorTest.java index 369a44b9..4ce4b103 100644 --- a/src/test/java/org/apache/commons/validator/routines/DoubleValidatorTest.java +++ b/src/test/java/org/apache/commons/validator/routines/DoubleValidatorTest.java @@ -72,6 +72,26 @@ class DoubleValidatorTest extends AbstractNumberValidatorTest { localeExpected = testNumber; } + /** + * Test the {@link Number} range checks against a bound that carries more precision than a {@code double}. + * {@code 2^53} is the largest integer with an exact {@code double} representation, so {@code 2^53} + 1 cannot be narrowed + * onto the value: a value of {@code 2^53} is below a minimum of {@code 2^53 + 1} and above a maximum of {@code 2^53 - 0.5}. + */ + @Test + void testDoubleNumberRangeExactBound() { + final DoubleValidator validator = (DoubleValidator) strictValidator; + final long maxExactInt = 1L << 53; // 2^53 + final Double value = Double.valueOf(maxExactInt); + final BigInteger above = BigInteger.valueOf(maxExactInt).add(BigInteger.ONE); // 2^53 + 1 + final BigInteger below = BigInteger.valueOf(maxExactInt).subtract(BigInteger.ONE); // 2^53 - 1 + final BigDecimal justBelow = BigDecimal.valueOf(maxExactInt).subtract(BigDecimal.valueOf(0.5)); // 2^53 - 0.5 + assertFalse(validator.minValue(value, above), "minValue() bound above value"); + assertTrue(validator.minValue(value, below), "minValue() bound below value"); + assertFalse(validator.maxValue(value, justBelow), "maxValue() bound below value"); + assertTrue(validator.maxValue(value, above), "maxValue() bound above value"); + assertFalse(validator.isInRange(value, above, above.add(BigInteger.ONE)), "isInRange() value below range"); + } + /** * Test Double Range/Min/Max */ @@ -134,26 +154,6 @@ class DoubleValidatorTest extends AbstractNumberValidatorTest { assertFalse(validator.maxValue(Double.NaN, 20), "maxValue() NaN"); } - /** - * Test the {@link Number} range checks against a bound that carries more precision than a {@code double}. - * {@code 2^53} is the largest integer with an exact {@code double} representation, so {@code 2^53} + 1 cannot be narrowed - * onto the value: a value of {@code 2^53} is below a minimum of {@code 2^53 + 1} and above a maximum of {@code 2^53 - 0.5}. - */ - @Test - void testDoubleNumberRangeExactBound() { - final DoubleValidator validator = (DoubleValidator) strictValidator; - final long maxExactInt = 1L << 53; // 2^53 - final Double value = Double.valueOf(maxExactInt); - final BigInteger above = BigInteger.valueOf(maxExactInt).add(BigInteger.ONE); // 2^53 + 1 - final BigInteger below = BigInteger.valueOf(maxExactInt).subtract(BigInteger.ONE); // 2^53 - 1 - final BigDecimal justBelow = BigDecimal.valueOf(maxExactInt).subtract(BigDecimal.valueOf(0.5)); // 2^53 - 0.5 - assertFalse(validator.minValue(value, above), "minValue() bound above value"); - assertTrue(validator.minValue(value, below), "minValue() bound below value"); - assertFalse(validator.maxValue(value, justBelow), "maxValue() bound below value"); - assertTrue(validator.maxValue(value, above), "maxValue() bound above value"); - assertFalse(validator.isInRange(value, above, above.add(BigInteger.ONE)), "isInRange() value below range"); - } - /** * Test DoubleValidator validate Methods */
