From 976aec59bac79fb46be963091b356367b9d4f9a4 Mon Sep 17 00:00:00 2001 From: David Pilar Date: Thu, 27 Aug 2026 00:07:57 +0200 Subject: [PATCH] Fix printDecimal regression for decimal values wider than a long Regression introduced in 5.4.0: printing an xsd:decimal that does not fit into a long throws IllegalArgumentException("Value can't be converted to long"). 5.3.0 prints the same value correctly. XsTypeConverter.printDecimal used MathUtil.toLong(value) == 0 to test whether the value is zero. toLong() throws for anything wider than a long, so the check blew up on values it was only ever meant to classify. 5.3.0 used BigDecimal.longValue() here, which truncates silently; MathUtil was added in 5.4.0 and toLong() replaced longValue() on this line. Use signum() instead, as in the original Harmony code this method is derived from: same semantics, cannot throw, O(1), and it keeps MathUtil off the serialization path entirely. Seen in the wild as a WSDL2Java code generation failure: xmlbeans could no longer save a compiled schema type system containing a high-precision decimal. --- .../xmlbeans/impl/util/XsTypeConverter.java | 3 ++- .../misc/checkin/XsTypeConverterTest.java | 25 +++++++++++++++++++ 2 files changed, 27 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 afcd131f3..405a4b252 100644 --- a/src/main/java/org/apache/xmlbeans/impl/util/XsTypeConverter.java +++ b/src/main/java/org/apache/xmlbeans/impl/util/XsTypeConverter.java @@ -279,7 +279,8 @@ public static String printDecimal(BigDecimal value) { // The following code comes from Apache Harmony String intStr = value.unscaledValue().toString(); int scale = value.scale(); - if (scale == 0 || (MathUtil.toLong(value) == 0 && scale < 0)) { + // the second branch only needs a zero check, as in the original Harmony code + if (scale == 0 || (value.signum() == 0 && scale < 0)) { return intStr; } diff --git a/src/test/java/misc/checkin/XsTypeConverterTest.java b/src/test/java/misc/checkin/XsTypeConverterTest.java index 1865e7f81..acb23a16e 100644 --- a/src/test/java/misc/checkin/XsTypeConverterTest.java +++ b/src/test/java/misc/checkin/XsTypeConverterTest.java @@ -22,6 +22,8 @@ import org.apache.xmlbeans.impl.values.XmlValueOutOfRangeException; import org.junit.jupiter.api.Test; +import java.math.BigDecimal; + import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; @@ -309,4 +311,27 @@ public java.util.Iterator getPrefixes(String uri) { assertEquals("", empty.getLocalPart()); assertEquals(1, errors.size()); } + + @Test + void printDecimalHandlesValuesWiderThanLong() { + assertEquals("123456789012345678901234567890.5", + XsTypeConverter.printDecimal(new BigDecimal("123456789012345678901234567890.5"))); + assertEquals("-123456789012345678901234567890.5", + XsTypeConverter.printDecimal(new BigDecimal("-123456789012345678901234567890.5"))); + } + + @Test + void printDecimalHandlesNegativeScaleWiderThanLong() { + // stripTrailingZeros() and BigDecimal.valueOf(unscaled, negativeScale) both + // produce negative-scale values, which reach printDecimal via setBigDecimalValue. + assertEquals("100000000000000000000", XsTypeConverter.printDecimal(new BigDecimal("1E+20"))); + assertEquals("-100000000000000000000", XsTypeConverter.printDecimal(new BigDecimal("-1E+20"))); + assertEquals("100", XsTypeConverter.printDecimal(new BigDecimal("1E+2"))); + } + + @Test + void printDecimalHandlesZeroWithScale() { + assertEquals("0", XsTypeConverter.printDecimal(new BigDecimal("0E+5"))); + assertEquals("0.00000", XsTypeConverter.printDecimal(new BigDecimal("0E-5"))); + } }