From 7c84bc7b3e22f18b123317fda1c1a617635a78f8 Mon Sep 17 00:00:00 2001 From: PJ Fanning Date: Fri, 19 Jun 2026 02:12:01 +0100 Subject: [PATCH 1/9] Update XsTypeConverterTest.java --- .../misc/checkin/XsTypeConverterTest.java | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/test/java/misc/checkin/XsTypeConverterTest.java b/src/test/java/misc/checkin/XsTypeConverterTest.java index 18a6b085e..3c4be05aa 100644 --- a/src/test/java/misc/checkin/XsTypeConverterTest.java +++ b/src/test/java/misc/checkin/XsTypeConverterTest.java @@ -37,6 +37,26 @@ void lexIntAcceptsAscii() { assertEquals(123, XsTypeConverter.lexInt("123")); 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("-123")); + assertEquals(123L, XsTypeConverter.lexLong("+123")); + assertEquals(Long.MAX_VALUE, XsTypeConverter.lexInt(Long.toString(Long.MAX_VALUE))); + assertEquals(Long.MIN_VALUE, XsTypeConverter.lexInt(Long.toString(Long.MIN_VALUE))); + } + + @Test + void lexShortAcceptsAscii() { + assertEquals(123L, XsTypeConverter.lexShort("123")); + assertEquals(-123L, XsTypeConverter.lexShort("-123")); + assertEquals(123L, 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 From 78a6602b714b8659885acb720e7f6b486c7993f8 Mon Sep 17 00:00:00 2001 From: PJ Fanning Date: Fri, 19 Jun 2026 02:13:03 +0100 Subject: [PATCH 2/9] Update XsTypeConverterTest.java --- src/test/java/misc/checkin/XsTypeConverterTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/test/java/misc/checkin/XsTypeConverterTest.java b/src/test/java/misc/checkin/XsTypeConverterTest.java index 3c4be05aa..a79f45063 100644 --- a/src/test/java/misc/checkin/XsTypeConverterTest.java +++ b/src/test/java/misc/checkin/XsTypeConverterTest.java @@ -46,8 +46,8 @@ void lexLongAcceptsAscii() { assertEquals(123L, XsTypeConverter.lexLong("123")); assertEquals(-123L, XsTypeConverter.lexLong("-123")); assertEquals(123L, XsTypeConverter.lexLong("+123")); - assertEquals(Long.MAX_VALUE, XsTypeConverter.lexInt(Long.toString(Long.MAX_VALUE))); - assertEquals(Long.MIN_VALUE, XsTypeConverter.lexInt(Long.toString(Long.MIN_VALUE))); + assertEquals(Long.MAX_VALUE, XsTypeConverter.lexLong(Long.toString(Long.MAX_VALUE))); + assertEquals(Long.MIN_VALUE, XsTypeConverter.lexLong(Long.toString(Long.MIN_VALUE))); } @Test From bad05e2c1acc0340f660fac785ca48cf66156a16 Mon Sep 17 00:00:00 2001 From: PJ Fanning Date: Fri, 19 Jun 2026 02:23:40 +0100 Subject: [PATCH 3/9] Update XsTypeConverter.java --- .../xmlbeans/impl/util/XsTypeConverter.java | 70 +++++++++++-------- 1 file changed, 40 insertions(+), 30 deletions(-) diff --git a/src/main/java/org/apache/xmlbeans/impl/util/XsTypeConverter.java b/src/main/java/org/apache/xmlbeans/impl/util/XsTypeConverter.java index 236abe859..58d597897 100644 --- a/src/main/java/org/apache/xmlbeans/impl/util/XsTypeConverter.java +++ b/src/main/java/org/apache/xmlbeans/impl/util/XsTypeConverter.java @@ -655,50 +655,60 @@ private static byte parseByte(CharSequence cs) { } private static int parseIntXsdNumber(CharSequence ch, int min_value, int max_value) { - // int parser on a CharSequence - int length = ch.length(); - if (length < 1) { + if (ch == null || ch.length() == 0) { 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); + 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 + int digitCount = 0; + + 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)) { + digitCount++; + + // 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); + // Optional: reject leading zeros (strict XSD canonical lexical) + // if (digitCount > 1 && ch.charAt(negative || first== '+' ? 1 : 0) == '0') { ... } + + return Math.toIntExact(result); } // ======================== anyURI ======================== From 53582ee12e91064981193f32b4c872f805d0ae11 Mon Sep 17 00:00:00 2001 From: PJ Fanning Date: Fri, 19 Jun 2026 02:38:35 +0100 Subject: [PATCH 4/9] Update XsTypeConverter.java --- .../java/org/apache/xmlbeans/impl/util/XsTypeConverter.java | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/main/java/org/apache/xmlbeans/impl/util/XsTypeConverter.java b/src/main/java/org/apache/xmlbeans/impl/util/XsTypeConverter.java index 58d597897..8008f8f9e 100644 --- a/src/main/java/org/apache/xmlbeans/impl/util/XsTypeConverter.java +++ b/src/main/java/org/apache/xmlbeans/impl/util/XsTypeConverter.java @@ -677,7 +677,6 @@ private static int parseIntXsdNumber(CharSequence ch, int min_value, int max_val } long result = 0; // Use long to avoid intermediate overflow - int digitCount = 0; while (i < len) { char c = ch.charAt(i++); @@ -686,8 +685,6 @@ private static int parseIntXsdNumber(CharSequence ch, int min_value, int max_val throw new NumberFormatException("For input string: \"" + ch + "\""); } - digitCount++; - // Early overflow detection if (result > (Long.MAX_VALUE / 10)) { throw new NumberFormatException("For input string: \"" + ch + "\""); @@ -705,9 +702,6 @@ private static int parseIntXsdNumber(CharSequence ch, int min_value, int max_val ch, min_value, max_value)); } - // Optional: reject leading zeros (strict XSD canonical lexical) - // if (digitCount > 1 && ch.charAt(negative || first== '+' ? 1 : 0) == '0') { ... } - return Math.toIntExact(result); } From c7b56ff28671a452ebfcb9fe915d5d15f2bfa0be Mon Sep 17 00:00:00 2001 From: PJ Fanning Date: Fri, 19 Jun 2026 02:42:45 +0100 Subject: [PATCH 5/9] Update XsTypeConverterTest.java --- src/test/java/misc/checkin/XsTypeConverterTest.java | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/test/java/misc/checkin/XsTypeConverterTest.java b/src/test/java/misc/checkin/XsTypeConverterTest.java index a79f45063..d90840a23 100644 --- a/src/test/java/misc/checkin/XsTypeConverterTest.java +++ b/src/test/java/misc/checkin/XsTypeConverterTest.java @@ -35,6 +35,7 @@ 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))); @@ -44,6 +45,7 @@ void lexIntAcceptsAscii() { @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))); @@ -52,9 +54,10 @@ void lexLongAcceptsAscii() { @Test void lexShortAcceptsAscii() { - assertEquals(123L, XsTypeConverter.lexShort("123")); - assertEquals(-123L, XsTypeConverter.lexShort("-123")); - assertEquals(123L, XsTypeConverter.lexShort("+123")); + 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))); } @@ -68,7 +71,6 @@ void lexIntRejectsNonAsciiDigits() { @Test void lexShortRejectsNonAsciiDigits() { - assertEquals(123, XsTypeConverter.lexShort("123")); assertThrows(NumberFormatException.class, () -> XsTypeConverter.lexShort(FULLWIDTH_123)); assertThrows(NumberFormatException.class, () -> XsTypeConverter.lexShort(ARABIC_123)); } @@ -125,6 +127,7 @@ void lexFloatStrictRejectsNonXsdLexicalForms() { @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)); @@ -134,6 +137,7 @@ void lexFloatStrictAcceptsValidValues() { @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")); @@ -160,6 +164,7 @@ void lexDoubleStrictRejectsNonXsdLexicalForms() { @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)); From dca0f665f016c85276e0a836f666c82344c94c22 Mon Sep 17 00:00:00 2001 From: PJ Fanning Date: Fri, 19 Jun 2026 11:06:08 +0100 Subject: [PATCH 6/9] add tests --- .../java/org/apache/xmlbeans/impl/util/XsTypeConverter.java | 2 +- src/test/java/misc/checkin/XsTypeConverterTest.java | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/apache/xmlbeans/impl/util/XsTypeConverter.java b/src/main/java/org/apache/xmlbeans/impl/util/XsTypeConverter.java index 8008f8f9e..cdf065fb7 100644 --- a/src/main/java/org/apache/xmlbeans/impl/util/XsTypeConverter.java +++ b/src/main/java/org/apache/xmlbeans/impl/util/XsTypeConverter.java @@ -709,7 +709,7 @@ private static int parseIntXsdNumber(CharSequence ch, int min_value, int max_val /** * 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 diff --git a/src/test/java/misc/checkin/XsTypeConverterTest.java b/src/test/java/misc/checkin/XsTypeConverterTest.java index d90840a23..e683c2072 100644 --- a/src/test/java/misc/checkin/XsTypeConverterTest.java +++ b/src/test/java/misc/checkin/XsTypeConverterTest.java @@ -69,6 +69,12 @@ void lexIntRejectsNonAsciiDigits() { assertThrows(NumberFormatException.class, () -> XsTypeConverter.lexInt(DEVANAGARI_123)); } + @Test + void lexIntRejectsEmptyOrNull() { + assertThrows(NumberFormatException.class, () -> XsTypeConverter.lexInt(null)); + assertThrows(NumberFormatException.class, () -> XsTypeConverter.lexInt("")); + } + @Test void lexShortRejectsNonAsciiDigits() { assertThrows(NumberFormatException.class, () -> XsTypeConverter.lexShort(FULLWIDTH_123)); From 234a5b41de055e7d339892d37b8e9550ef0291fe Mon Sep 17 00:00:00 2001 From: PJ Fanning Date: Fri, 19 Jun 2026 12:37:53 +0100 Subject: [PATCH 7/9] Update XsTypeConverterTest.java --- src/test/java/misc/checkin/XsTypeConverterTest.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/test/java/misc/checkin/XsTypeConverterTest.java b/src/test/java/misc/checkin/XsTypeConverterTest.java index e683c2072..615bfbfb9 100644 --- a/src/test/java/misc/checkin/XsTypeConverterTest.java +++ b/src/test/java/misc/checkin/XsTypeConverterTest.java @@ -207,6 +207,12 @@ void lexLongRejectsDoubleSign() { assertThrows(NumberFormatException.class, () -> XsTypeConverter.lexLong("+-5")); } + @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. @@ -221,6 +227,7 @@ void lexDecimalRejectsEmptyString() { // 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 From c262405846dbc730f4259aead286556e7bc2adae Mon Sep 17 00:00:00 2001 From: PJ Fanning Date: Fri, 19 Jun 2026 12:54:16 +0100 Subject: [PATCH 8/9] Update XsTypeConverter.java --- .../xmlbeans/impl/util/XsTypeConverter.java | 21 +++++++++++-------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/src/main/java/org/apache/xmlbeans/impl/util/XsTypeConverter.java b/src/main/java/org/apache/xmlbeans/impl/util/XsTypeConverter.java index cdf065fb7..ea3f21549 100644 --- a/src/main/java/org/apache/xmlbeans/impl/util/XsTypeConverter.java +++ b/src/main/java/org/apache/xmlbeans/impl/util/XsTypeConverter.java @@ -93,6 +93,7 @@ public static float lexFloat(CharSequence cs) */ 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 static double lexDouble(CharSequence cs) */ public static double lexDouble(CharSequence cs, boolean strict) throws NumberFormatException { + rejectInvalidNumber(cs); final String v = cs.toString(); switch (v) { case POS_INF_LEX: @@ -300,6 +302,7 @@ public static String printInteger(BigInteger value) { // ======================== 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 +361,7 @@ public static String printShort(short value) { // ======================== 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 errors) { @@ -642,10 +645,6 @@ private static String trimTrailingZeros(String xsd_decimal) { 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,11 +654,9 @@ private static byte parseByte(CharSequence cs) { } private static int parseIntXsdNumber(CharSequence ch, int min_value, int max_value) { - if (ch == null || ch.length() == 0) { - throw new NumberFormatException("For input string: \"" + ch + "\""); - } + rejectInvalidNumber(ch); - int len = ch.length(); + final int len = ch.length(); int i = 0; boolean negative = false; @@ -748,4 +745,10 @@ public static CharSequence lexAnyURI(CharSequence lexical_value) { return lexical_value; } + + private static void rejectInvalidNumber(CharSequence cs) { + if (cs == null || cs.length() == 0) { + throw new NumberFormatException("For input string: \"" + cs + "\""); + } + } } From 30f8d28b2f488c52e36fec0477bd112cc6fce605 Mon Sep 17 00:00:00 2001 From: PJ Fanning Date: Fri, 19 Jun 2026 12:59:56 +0100 Subject: [PATCH 9/9] Update XsTypeConverter.java --- src/main/java/org/apache/xmlbeans/impl/util/XsTypeConverter.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/org/apache/xmlbeans/impl/util/XsTypeConverter.java b/src/main/java/org/apache/xmlbeans/impl/util/XsTypeConverter.java index ea3f21549..c988639e2 100644 --- a/src/main/java/org/apache/xmlbeans/impl/util/XsTypeConverter.java +++ b/src/main/java/org/apache/xmlbeans/impl/util/XsTypeConverter.java @@ -214,6 +214,7 @@ public static String printDouble(double value) { // ======================== decimal ======================== public static BigDecimal lexDecimal(CharSequence cs) throws NumberFormatException { + rejectInvalidNumber(cs); final String v = cs.toString(); //TODO: review this