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]

Reply via email to