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-beanutils.git
The following commit(s) were added to refs/heads/master by this push:
new 37b11859 reject non-integer strings in integer locale converters (#411)
37b11859 is described below
commit 37b118597f402e0c41a89ce9edc1420a8d4e1c78
Author: Dexter.k <[email protected]>
AuthorDate: Mon Jul 6 19:31:19 2026 +0000
reject non-integer strings in integer locale converters (#411)
The integer locale converters parsed a value like 5.5 with a DecimalFormat
that allows decimals, then narrowed it, silently truncating to 5. Reject a
non-integer parse result before narrowing; BigIntegerLocaleConverter uses
toBigIntegerExact.
---
.../locale/converters/BigIntegerLocaleConverter.java | 6 +++++-
.../locale/converters/ByteLocaleConverter.java | 4 ++++
.../locale/converters/IntegerLocaleConverter.java | 4 ++++
.../locale/converters/LongLocaleConverter.java | 3 +++
.../locale/converters/ShortLocaleConverter.java | 4 ++++
.../converters/BigIntegerLocaleConverterTest.java | 19 ++++++++++++++-----
.../converters/ByteLocaleConverterTest.java | 19 ++++++++++++++-----
.../converters/IntegerLocaleConverterTest.java | 19 ++++++++++++++-----
.../converters/LongLocaleConverterTest.java | 19 ++++++++++++++-----
.../converters/ShortLocaleConverterTest.java | 19 ++++++++++++++-----
10 files changed, 90 insertions(+), 26 deletions(-)
diff --git
a/src/main/java/org/apache/commons/beanutils2/locale/converters/BigIntegerLocaleConverter.java
b/src/main/java/org/apache/commons/beanutils2/locale/converters/BigIntegerLocaleConverter.java
index 41dc4a47..73433fff 100644
---
a/src/main/java/org/apache/commons/beanutils2/locale/converters/BigIntegerLocaleConverter.java
+++
b/src/main/java/org/apache/commons/beanutils2/locale/converters/BigIntegerLocaleConverter.java
@@ -80,7 +80,11 @@ public class BigIntegerLocaleConverter extends
DecimalLocaleConverter<BigInteger
return (BigInteger) result;
}
if (result instanceof BigDecimal) {
- return ((BigDecimal) result).toBigInteger();
+ try {
+ return ((BigDecimal) result).toBigIntegerExact();
+ } catch (final ArithmeticException e) {
+ throw new ConversionException("Supplied number is not an
integer: " + result, e);
+ }
}
return BigInteger.valueOf(result.longValue());
}
diff --git
a/src/main/java/org/apache/commons/beanutils2/locale/converters/ByteLocaleConverter.java
b/src/main/java/org/apache/commons/beanutils2/locale/converters/ByteLocaleConverter.java
index 9119629f..a3286dda 100644
---
a/src/main/java/org/apache/commons/beanutils2/locale/converters/ByteLocaleConverter.java
+++
b/src/main/java/org/apache/commons/beanutils2/locale/converters/ByteLocaleConverter.java
@@ -74,6 +74,10 @@ public class ByteLocaleConverter extends
DecimalLocaleConverter<Byte> {
if (parsed.longValue() != parsed.byteValue()) {
throw new ConversionException("Supplied number is not of type
Byte: " + parsed.longValue());
}
+ final double doubleValue = parsed.doubleValue();
+ if (doubleValue != Math.rint(doubleValue)) {
+ throw new ConversionException("Supplied number is not an integer:
" + parsed);
+ }
// now returns property Byte
return Byte.valueOf(parsed.byteValue());
}
diff --git
a/src/main/java/org/apache/commons/beanutils2/locale/converters/IntegerLocaleConverter.java
b/src/main/java/org/apache/commons/beanutils2/locale/converters/IntegerLocaleConverter.java
index e805e492..0cfe6957 100644
---
a/src/main/java/org/apache/commons/beanutils2/locale/converters/IntegerLocaleConverter.java
+++
b/src/main/java/org/apache/commons/beanutils2/locale/converters/IntegerLocaleConverter.java
@@ -74,6 +74,10 @@ public class IntegerLocaleConverter extends
DecimalLocaleConverter<Integer> {
if (parsed.longValue() != parsed.intValue()) {
throw new ConversionException("Supplied number is not of type
Integer: " + parsed.longValue());
}
+ final double doubleValue = parsed.doubleValue();
+ if (doubleValue != Math.rint(doubleValue)) {
+ throw new ConversionException("Supplied number is not an integer:
" + parsed);
+ }
return Integer.valueOf(parsed.intValue()); // unlike superclass it
will return proper Integer
}
}
diff --git
a/src/main/java/org/apache/commons/beanutils2/locale/converters/LongLocaleConverter.java
b/src/main/java/org/apache/commons/beanutils2/locale/converters/LongLocaleConverter.java
index 0fe51987..7f9ae377 100644
---
a/src/main/java/org/apache/commons/beanutils2/locale/converters/LongLocaleConverter.java
+++
b/src/main/java/org/apache/commons/beanutils2/locale/converters/LongLocaleConverter.java
@@ -79,6 +79,9 @@ public class LongLocaleConverter extends
DecimalLocaleConverter<Long> {
if (doubleValue < Long.MIN_VALUE || doubleValue > Long.MAX_VALUE) {
throw new ConversionException("Supplied number is not of type
Long: " + result);
}
+ if (doubleValue != Math.rint(doubleValue)) {
+ throw new ConversionException("Supplied number is not an integer:
" + result);
+ }
return Long.valueOf(result.longValue());
}
}
diff --git
a/src/main/java/org/apache/commons/beanutils2/locale/converters/ShortLocaleConverter.java
b/src/main/java/org/apache/commons/beanutils2/locale/converters/ShortLocaleConverter.java
index 570190cc..e2d2fad4 100644
---
a/src/main/java/org/apache/commons/beanutils2/locale/converters/ShortLocaleConverter.java
+++
b/src/main/java/org/apache/commons/beanutils2/locale/converters/ShortLocaleConverter.java
@@ -84,6 +84,10 @@ public class ShortLocaleConverter extends
DecimalLocaleConverter<Short> {
if (parsed.longValue() != parsed.shortValue()) {
throw new ConversionException("Supplied number is not of type
Short: " + parsed.longValue());
}
+ final double doubleValue = parsed.doubleValue();
+ if (doubleValue != Math.rint(doubleValue)) {
+ throw new ConversionException("Supplied number is not an integer:
" + parsed);
+ }
// now returns property Short
return Short.valueOf(parsed.shortValue());
}
diff --git
a/src/test/java/org/apache/commons/beanutils2/converters/BigIntegerLocaleConverterTest.java
b/src/test/java/org/apache/commons/beanutils2/converters/BigIntegerLocaleConverterTest.java
index 11d7f09e..476a6f8e 100644
---
a/src/test/java/org/apache/commons/beanutils2/converters/BigIntegerLocaleConverterTest.java
+++
b/src/test/java/org/apache/commons/beanutils2/converters/BigIntegerLocaleConverterTest.java
@@ -176,12 +176,12 @@ class BigIntegerLocaleConverterTest extends
AbstractLocaleConverterTest<BigInteg
convertInvalid(converter, "(A)", defaultValue);
convertNull(converter, "(A)", defaultValue);
//
**************************************************************************
- // Convert value in the wrong format - maybe you would expect it to
throw an
- // exception and return the default - it doesn't, DecimalFormat parses
it
- // quite happily turning "1,234" into "1"
- // I guess this is one of the limitations of DecimalFormat
+ // Convert value in the wrong format - the localized (German)
converter reads
+ // ',' as the decimal separator, so "1,234" parses to the fractional
value
+ // 1.234; the integer converter now rejects a non-integer result and
returns
+ // the default.
//
**************************************************************************
- convertValueNoPattern(converter, "(B)", defaultIntegerValue, new
BigInteger("1"));
+ convertValueNoPattern(converter, "(B)", defaultIntegerValue,
defaultValue);
//
**************************************************************************
// Convert with non-localized pattern - the trailing characters left
after the
// partial parse are now rejected, so the converter returns the
default.
@@ -203,4 +203,13 @@ class BigIntegerLocaleConverterTest extends
AbstractLocaleConverterTest<BigInteg
convertInvalid(converter, "(C)", defaultValue);
convertNull(converter, "(C)", defaultValue);
}
+
+ /**
+ * Tests that a non-integer value is rejected rather than silently
truncated to an integer.
+ */
+ @Test
+ void testNonIntegerRejected() {
+ converter =
BigIntegerLocaleConverter.builder().setDefault(defaultValue).setLocale(defaultLocale).get();
+ convertValueNoPattern(converter, "non-integer", "5.5", defaultValue);
+ }
}
diff --git
a/src/test/java/org/apache/commons/beanutils2/converters/ByteLocaleConverterTest.java
b/src/test/java/org/apache/commons/beanutils2/converters/ByteLocaleConverterTest.java
index 609805c8..383d24fe 100644
---
a/src/test/java/org/apache/commons/beanutils2/converters/ByteLocaleConverterTest.java
+++
b/src/test/java/org/apache/commons/beanutils2/converters/ByteLocaleConverterTest.java
@@ -169,12 +169,12 @@ class ByteLocaleConverterTest extends
AbstractLocaleConverterTest<Byte> {
convertInvalid(converter, "(A)", defaultValue);
convertNull(converter, "(A)", defaultValue);
//
**************************************************************************
- // Convert value in the wrong format - maybe you would expect it to
throw an
- // exception and return the default - it doesn't, DecimalFormat parses
it
- // quite happily turning ",123" into "0"
- // I guess this is one of the limitations of DecimalFormat
+ // Convert value in the wrong format - the localized (German)
converter reads
+ // ',' as the decimal separator, so ",123" parses to the fractional
value
+ // 0.123; the integer converter now rejects a non-integer result and
returns
+ // the default.
//
**************************************************************************
- convertValueNoPattern(converter, "(B)", defaultIntegerValue,
Byte.valueOf("0"));
+ convertValueNoPattern(converter, "(B)", defaultIntegerValue,
defaultValue);
//
**************************************************************************
// Convert with non-localized pattern
//
**************************************************************************
@@ -196,4 +196,13 @@ class ByteLocaleConverterTest extends
AbstractLocaleConverterTest<Byte> {
convertInvalid(converter, "(C)", defaultValue);
convertNull(converter, "(C)", defaultValue);
}
+
+ /**
+ * Tests that a non-integer value is rejected rather than silently
truncated to an integer.
+ */
+ @Test
+ void testNonIntegerRejected() {
+ converter =
ByteLocaleConverter.builder().setDefault(defaultValue).setLocale(defaultLocale).get();
+ convertValueNoPattern(converter, "non-integer", "5.5", defaultValue);
+ }
}
diff --git
a/src/test/java/org/apache/commons/beanutils2/converters/IntegerLocaleConverterTest.java
b/src/test/java/org/apache/commons/beanutils2/converters/IntegerLocaleConverterTest.java
index 213986ef..9fcefe7c 100644
---
a/src/test/java/org/apache/commons/beanutils2/converters/IntegerLocaleConverterTest.java
+++
b/src/test/java/org/apache/commons/beanutils2/converters/IntegerLocaleConverterTest.java
@@ -164,12 +164,12 @@ class IntegerLocaleConverterTest extends
AbstractLocaleConverterTest<Integer> {
convertInvalid(converter, "(A)", defaultValue);
convertNull(converter, "(A)", defaultValue);
//
**************************************************************************
- // Convert value in the wrong format - maybe you would expect it to
throw an
- // exception and return the default - it doesn't, DecimalFormat parses
it
- // quite happily turning "1,234" into "1"
- // I guess this is one of the limitations of DecimalFormat
+ // Convert value in the wrong format - the localized (German)
converter reads
+ // ',' as the decimal separator, so "1,234" parses to the fractional
value
+ // 1.234; the integer converter now rejects a non-integer result and
returns
+ // the default.
//
**************************************************************************
- convertValueNoPattern(converter, "(B)", defaultIntegerValue,
Integer.valueOf("1"));
+ convertValueNoPattern(converter, "(B)", defaultIntegerValue,
defaultValue);
//
**************************************************************************
// Convert with non-localized pattern - the trailing characters left
after the
// partial parse are now rejected, so the converter returns the
default.
@@ -215,4 +215,13 @@ class IntegerLocaleConverterTest extends
AbstractLocaleConverterTest<Integer> {
final int result = converter.convert(target, (Object)
value.toString());
assertEquals(value.intValue(), result, "Wrong result");
}
+
+ /**
+ * Tests that a non-integer value is rejected rather than silently
truncated to an integer.
+ */
+ @Test
+ void testNonIntegerRejected() {
+ converter =
IntegerLocaleConverter.builder().setDefault(defaultValue).setLocale(defaultLocale).get();
+ convertValueNoPattern(converter, "non-integer", "5.5", defaultValue);
+ }
}
diff --git
a/src/test/java/org/apache/commons/beanutils2/converters/LongLocaleConverterTest.java
b/src/test/java/org/apache/commons/beanutils2/converters/LongLocaleConverterTest.java
index dac385f3..ae15809b 100644
---
a/src/test/java/org/apache/commons/beanutils2/converters/LongLocaleConverterTest.java
+++
b/src/test/java/org/apache/commons/beanutils2/converters/LongLocaleConverterTest.java
@@ -168,12 +168,12 @@ class LongLocaleConverterTest extends
AbstractLocaleConverterTest<Long> {
convertInvalid(converter, "(A)", defaultValue);
convertNull(converter, "(A)", defaultValue);
//
**************************************************************************
- // Convert value in the wrong format - maybe you would expect it to
throw an
- // exception and return the default - it doesn't, DecimalFormat parses
it
- // quite happily turning "1,234" into "1"
- // I guess this is one of the limitations of DecimalFormat
+ // Convert value in the wrong format - the localized (German)
converter reads
+ // ',' as the decimal separator, so "1,234" parses to the fractional
value
+ // 1.234; the integer converter now rejects a non-integer result and
returns
+ // the default.
//
**************************************************************************
- convertValueNoPattern(converter, "(B)", defaultIntegerValue,
Long.valueOf("1"));
+ convertValueNoPattern(converter, "(B)", defaultIntegerValue,
defaultValue);
//
**************************************************************************
// Convert with non-localized pattern - the trailing characters left
after the
// partial parse are now rejected, so the converter returns the
default.
@@ -208,4 +208,13 @@ class LongLocaleConverterTest extends
AbstractLocaleConverterTest<Long> {
assertThrows(ConversionException.class, () ->
converter.convert("99999999999999999999"));
assertThrows(ConversionException.class, () ->
converter.convert("-99999999999999999999"));
}
+
+ /**
+ * Tests that a non-integer value is rejected rather than silently
truncated to an integer.
+ */
+ @Test
+ void testNonIntegerRejected() {
+ converter =
LongLocaleConverter.builder().setDefault(defaultValue).setLocale(defaultLocale).get();
+ convertValueNoPattern(converter, "non-integer", "5.5", defaultValue);
+ }
}
diff --git
a/src/test/java/org/apache/commons/beanutils2/converters/ShortLocaleConverterTest.java
b/src/test/java/org/apache/commons/beanutils2/converters/ShortLocaleConverterTest.java
index 7c752bf9..76b6834c 100644
---
a/src/test/java/org/apache/commons/beanutils2/converters/ShortLocaleConverterTest.java
+++
b/src/test/java/org/apache/commons/beanutils2/converters/ShortLocaleConverterTest.java
@@ -162,12 +162,12 @@ class ShortLocaleConverterTest extends
AbstractLocaleConverterTest<Short> {
convertInvalid(converter, "(A)", defaultValue);
convertNull(converter, "(A)", defaultValue);
//
**************************************************************************
- // Convert value in the wrong format - maybe you would expect it to
throw an
- // exception and return the default - it doesn't, DecimalFormat parses
it
- // quite happily turning "1,234" into "1"
- // I guess this is one of the limitations of DecimalFormat
+ // Convert value in the wrong format - the localized (German)
converter reads
+ // ',' as the decimal separator, so "1,234" parses to the fractional
value
+ // 1.234; the integer converter now rejects a non-integer result and
returns
+ // the default.
//
**************************************************************************
- convertValueNoPattern(converter, "(B)", defaultIntegerValue,
Short.valueOf("1"));
+ convertValueNoPattern(converter, "(B)", defaultIntegerValue,
defaultValue);
//
**************************************************************************
// Convert with non-localized pattern - the trailing characters left
after the
// partial parse are now rejected, so the converter returns the
default.
@@ -189,4 +189,13 @@ class ShortLocaleConverterTest extends
AbstractLocaleConverterTest<Short> {
convertInvalid(converter, "(C)", defaultValue);
convertNull(converter, "(C)", defaultValue);
}
+
+ /**
+ * Tests that a non-integer value is rejected rather than silently
truncated to an integer.
+ */
+ @Test
+ void testNonIntegerRejected() {
+ converter =
ShortLocaleConverter.builder().setDefault(defaultValue).setLocale(defaultLocale).get();
+ convertValueNoPattern(converter, "non-integer", "5.5", defaultValue);
+ }
}