Author: fanningpj
Date: Fri Jun 19 12:20:46 2026
New Revision: 1935511
Log:
make number pasring more robust
Modified:
xmlbeans/trunk/src/main/java/org/apache/xmlbeans/impl/util/XsTypeConverter.java
xmlbeans/trunk/src/test/java/misc/checkin/XsTypeConverterTest.java
Modified:
xmlbeans/trunk/src/main/java/org/apache/xmlbeans/impl/util/XsTypeConverter.java
==============================================================================
---
xmlbeans/trunk/src/main/java/org/apache/xmlbeans/impl/util/XsTypeConverter.java
Fri Jun 19 12:16:39 2026 (r1935510)
+++
xmlbeans/trunk/src/main/java/org/apache/xmlbeans/impl/util/XsTypeConverter.java
Fri Jun 19 12:20:46 2026 (r1935511)
@@ -93,6 +93,7 @@ public final class XsTypeConverter {
*/
public static float lexFloat(CharSequence cs, boolean strict)
throws NumberFormatException {
+ rejectInvalidNumber(cs);
final String v = cs.toString();
switch (v) {
case POS_INF_LEX:
@@ -162,6 +163,7 @@ public final class XsTypeConverter {
*/
public static double lexDouble(CharSequence cs, boolean strict)
throws NumberFormatException {
+ rejectInvalidNumber(cs);
final String v = cs.toString();
switch (v) {
case POS_INF_LEX:
@@ -212,6 +214,7 @@ public final class XsTypeConverter {
// ======================== decimal ========================
public static BigDecimal lexDecimal(CharSequence cs)
throws NumberFormatException {
+ rejectInvalidNumber(cs);
final String v = cs.toString();
//TODO: review this
@@ -300,6 +303,7 @@ public final class XsTypeConverter {
// ======================== long ========================
public static long lexLong(CharSequence cs)
throws NumberFormatException {
+ rejectInvalidNumber(cs);
rejectSignAfterPlus(cs);
final String v = cs.toString();
return Long.parseLong(trimInitialPlus(v));
@@ -358,7 +362,7 @@ public final class XsTypeConverter {
// ======================== int ========================
public static int lexInt(CharSequence cs)
throws NumberFormatException {
- return parseInt(cs);
+ return parseIntXsdNumber(cs, Integer.MIN_VALUE, Integer.MAX_VALUE);
}
public static int lexInt(CharSequence cs, Collection<XmlError> errors) {
@@ -642,10 +646,6 @@ public final class XsTypeConverter {
return xsd_decimal;
}
- private static int parseInt(CharSequence cs) {
- return parseIntXsdNumber(cs, Integer.MIN_VALUE, Integer.MAX_VALUE);
- }
-
private static short parseShort(CharSequence cs) {
return (short) parseIntXsdNumber(cs, Short.MIN_VALUE, Short.MAX_VALUE);
}
@@ -655,57 +655,59 @@ public final class XsTypeConverter {
}
private static int parseIntXsdNumber(CharSequence ch, int min_value, int
max_value) {
- // int parser on a CharSequence
- int length = ch.length();
- if (length < 1) {
- throw new NumberFormatException("For input string: \"" + ch +
"\"");
- }
-
- int sign = 1;
- int result = 0;
- int start = 0;
- int limit;
- int limit2;
-
- char c = ch.charAt(0);
- if (c == '-') {
- start++;
- limit = (min_value / 10);
- limit2 = -(min_value % 10);
- } else if (c == '+') {
- start++;
- sign = -1;
- limit = -(max_value / 10);
- limit2 = (max_value % 10);
- } else {
- sign = -1;
- limit = -(max_value / 10);
- limit2 = (max_value % 10);
+ rejectInvalidNumber(ch);
+
+ final int len = ch.length();
+ int i = 0;
+ boolean negative = false;
+
+ // Sign
+ char first = ch.charAt(0);
+ if (first == '-') {
+ negative = true;
+ i = 1;
+ } else if (first == '+') {
+ i = 1;
}
- for (int i = 0; i < length - start; i++) {
- c = ch.charAt(i + start);
- int v = (c >= '0' && c <= '9') ? c - '0' : -1;
+ if (i == len) {
+ throw new NumberFormatException("For input string: \"" + ch +
"\""); // just "+" or "-"
+ }
- if (v < 0) {
+ long result = 0; // Use long to avoid intermediate overflow
+
+ while (i < len) {
+ char c = ch.charAt(i++);
+ int digit = c - '0';
+ if (digit < 0 || digit > 9) {
throw new NumberFormatException("For input string: \"" + ch +
"\"");
}
- if (result < limit || (result == limit && v > limit2)) {
+ // Early overflow detection
+ if (result > (Long.MAX_VALUE / 10)) {
throw new NumberFormatException("For input string: \"" + ch +
"\"");
}
+ result = result * 10 + digit;
+ }
+
+ if (negative) {
+ result = -result;
+ }
- result = Math.toIntExact(result * 10L - v);
+ if (result < min_value || result > max_value) {
+ throw new NumberFormatException(String.format(
+ "For input string: \"%s\"; min-allowed=%d, max-allowed=%d",
+ ch, min_value, max_value));
}
- return Math.multiplyExact(sign, result);
+ return Math.toIntExact(result);
}
// ======================== anyURI ========================
/**
* Checks the regular expression of URI, defined by RFC2369
http://www.ietf.org/rfc/rfc2396.txt Appendix B.
- * Note: The whitespace normalization rule collapse must be applied priot
to calling this method.
+ * Note: The whitespace normalization rule collapse must be applied prior
to calling this method.
*
* @param lexical_value the lexical value
* @return same input value if input value is in the lexical space
@@ -744,4 +746,10 @@ public final class XsTypeConverter {
return lexical_value;
}
+
+ private static void rejectInvalidNumber(CharSequence cs) {
+ if (cs == null || cs.length() == 0) {
+ throw new NumberFormatException("For input string: \"" + cs +
"\"");
+ }
+ }
}
Modified: xmlbeans/trunk/src/test/java/misc/checkin/XsTypeConverterTest.java
==============================================================================
--- xmlbeans/trunk/src/test/java/misc/checkin/XsTypeConverterTest.java Fri Jun
19 12:16:39 2026 (r1935510)
+++ xmlbeans/trunk/src/test/java/misc/checkin/XsTypeConverterTest.java Fri Jun
19 12:20:46 2026 (r1935511)
@@ -35,8 +35,31 @@ public class XsTypeConverterTest {
@Test
void lexIntAcceptsAscii() {
assertEquals(123, XsTypeConverter.lexInt("123"));
+ assertEquals(123, XsTypeConverter.lexInt("00123"));
assertEquals(-123, XsTypeConverter.lexInt("-123"));
assertEquals(123, XsTypeConverter.lexInt("+123"));
+ assertEquals(Integer.MAX_VALUE,
XsTypeConverter.lexInt(Integer.toString(Integer.MAX_VALUE)));
+ assertEquals(Integer.MIN_VALUE,
XsTypeConverter.lexInt(Integer.toString(Integer.MIN_VALUE)));
+ }
+
+ @Test
+ void lexLongAcceptsAscii() {
+ assertEquals(123L, XsTypeConverter.lexLong("123"));
+ assertEquals(123L, XsTypeConverter.lexLong("00123"));
+ assertEquals(-123L, XsTypeConverter.lexLong("-123"));
+ assertEquals(123L, XsTypeConverter.lexLong("+123"));
+ assertEquals(Long.MAX_VALUE,
XsTypeConverter.lexLong(Long.toString(Long.MAX_VALUE)));
+ assertEquals(Long.MIN_VALUE,
XsTypeConverter.lexLong(Long.toString(Long.MIN_VALUE)));
+ }
+
+ @Test
+ void lexShortAcceptsAscii() {
+ assertEquals(123, XsTypeConverter.lexShort("123"));
+ assertEquals(123, XsTypeConverter.lexShort("00123"));
+ assertEquals(-123, XsTypeConverter.lexShort("-123"));
+ assertEquals(123, XsTypeConverter.lexShort("+123"));
+ assertEquals(Short.MAX_VALUE,
XsTypeConverter.lexShort(Short.toString(Short.MAX_VALUE)));
+ assertEquals(Short.MIN_VALUE,
XsTypeConverter.lexShort(Short.toString(Short.MIN_VALUE)));
}
@Test
@@ -47,8 +70,13 @@ public class XsTypeConverterTest {
}
@Test
+ void lexIntRejectsEmptyOrNull() {
+ assertThrows(NumberFormatException.class, () ->
XsTypeConverter.lexInt(null));
+ assertThrows(NumberFormatException.class, () ->
XsTypeConverter.lexInt(""));
+ }
+
+ @Test
void lexShortRejectsNonAsciiDigits() {
- assertEquals(123, XsTypeConverter.lexShort("123"));
assertThrows(NumberFormatException.class, () ->
XsTypeConverter.lexShort(FULLWIDTH_123));
assertThrows(NumberFormatException.class, () ->
XsTypeConverter.lexShort(ARABIC_123));
}
@@ -105,6 +133,7 @@ public class XsTypeConverterTest {
@Test
void lexFloatStrictAcceptsValidValues() {
assertEquals(1.0f, XsTypeConverter.lexFloat("1.0", true));
+ assertEquals(1.0f, XsTypeConverter.lexFloat("01.0", true));
assertEquals(1500.0f, XsTypeConverter.lexFloat("1.5e3", true));
assertEquals(Float.POSITIVE_INFINITY, XsTypeConverter.lexFloat("INF",
true));
assertEquals(Float.NEGATIVE_INFINITY, XsTypeConverter.lexFloat("-INF",
true));
@@ -114,6 +143,7 @@ public class XsTypeConverterTest {
@Test
void lexDoubleAcceptsValidValues() {
assertEquals(1.0, XsTypeConverter.lexDouble("1.0"));
+ assertEquals(1.0, XsTypeConverter.lexDouble("01.0"));
assertEquals(Double.POSITIVE_INFINITY,
XsTypeConverter.lexDouble("INF"));
assertEquals(Double.NEGATIVE_INFINITY,
XsTypeConverter.lexDouble("-INF"));
assertEquals(1500.0, XsTypeConverter.lexDouble("1.5e3"));
@@ -140,6 +170,7 @@ public class XsTypeConverterTest {
@Test
void lexDoubleStrictAcceptsValidValues() {
assertEquals(1.0, XsTypeConverter.lexDouble("1.0", true));
+ assertEquals(1.0, XsTypeConverter.lexDouble("01.0", true));
assertEquals(1500.0, XsTypeConverter.lexDouble("1.5e3", true));
assertEquals(Double.POSITIVE_INFINITY,
XsTypeConverter.lexDouble("INF", true));
assertEquals(Double.NEGATIVE_INFINITY,
XsTypeConverter.lexDouble("-INF", true));
@@ -177,6 +208,12 @@ public class XsTypeConverterTest {
}
@Test
+ void lexLongRejectsEmptyOrNull() {
+ assertThrows(NumberFormatException.class, () ->
XsTypeConverter.lexLong(null));
+ assertThrows(NumberFormatException.class, () ->
XsTypeConverter.lexLong(""));
+ }
+
+ @Test
void lexIntegerRejectsDoubleSign() {
// "+-5" was already caught; "++5" leaked through as 5.
assertEquals(java.math.BigInteger.valueOf(5),
XsTypeConverter.lexInteger("+5"));
@@ -190,6 +227,7 @@ public class XsTypeConverterTest {
// an empty value used to read charAt(-1) in trimTrailingZeros and
// throw StringIndexOutOfBoundsException instead of
NumberFormatException.
assertThrows(NumberFormatException.class, () ->
XsTypeConverter.lexDecimal(""));
+ assertThrows(NumberFormatException.class, () ->
XsTypeConverter.lexDecimal(null));
}
@Test
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]