This is an automated email from the ASF dual-hosted git repository.

lukaszlenart pushed a commit to branch 
WW-5828-bound-bigdecimal-integer-digits-6x
in repository https://gitbox.apache.org/repos/asf/struts.git

commit c8727141429797aa2e60d5564fef1843fc6841df
Author: Lukasz Lenart <[email protected]>
AuthorDate: Sat Oct 31 14:32:50 2026 +0100

    WW-5828 fix(conversion): bound integer digits when formatting BigDecimal
    
    WW-5711 bounded maximumFractionDigits to 340, so the length of a
    formatted BigDecimal no longer follows a large positive scale. The
    integer side had no equivalent bound: DecimalFormat keeps its default
    maximumIntegerDigits of Integer.MAX_VALUE and pads the integer part of
    a value with a large negative scale out in full, so the output length
    followed the scale of the value rather than its precision.
    
    setMaximumIntegerDigits is not the right bound, because DecimalFormat
    applies it by dropping the leading digits rather than by rounding.
    
    Bound the integer digit count (precision - scale, computed in long
    because the subtraction overflows int at the edge of the scale range)
    to 340 and, past it, fall back to BigDecimal.toString(), which renders
    the value compactly in scientific notation. Every double still formats
    in full; the widest, Double.MAX_VALUE, has 309 integer digits and the
    existing test pins it.
    
    Backport of the same change on main, adjusted for the
    com.opensymphony.xwork2 package layout of this line.
    
    Fixes: https://issues.apache.org/jira/browse/WW-5828
    
    Co-Authored-By: Claude Mythos 5.1 <[email protected]>
---
 .../xwork2/conversion/impl/StringConverter.java    | 17 ++++++++++++
 .../conversion/impl/StringConverterTest.java       | 30 ++++++++++++++++++++++
 2 files changed, 47 insertions(+)

diff --git 
a/core/src/main/java/com/opensymphony/xwork2/conversion/impl/StringConverter.java
 
b/core/src/main/java/com/opensymphony/xwork2/conversion/impl/StringConverter.java
index 6b1cb2fbc..9a065a7e4 100644
--- 
a/core/src/main/java/com/opensymphony/xwork2/conversion/impl/StringConverter.java
+++ 
b/core/src/main/java/com/opensymphony/xwork2/conversion/impl/StringConverter.java
@@ -47,6 +47,16 @@ public class StringConverter extends DefaultTypeConverter {
      */
     private static final int MAX_FRACTION_DIGITS = 340;
 
+    /**
+     * Upper bound on the number of integer digits emitted when formatting a 
number.
+     * <p>
+     * Covers every {@code double} value in full - the widest is {@link 
Double#MAX_VALUE}
+     * at 309 integer digits. A {@link BigDecimal} wider than this is rendered 
by
+     * {@link BigDecimal#toString()} instead, so the length of the output 
follows its
+     * precision rather than its scale.
+     */
+    private static final int MAX_INTEGER_DIGITS = 340;
+
 
     @Override
     public Object convertValue(Map<String, Object> context, Object target, 
Member member, String propertyName, Object value, Class toType) {
@@ -92,6 +102,9 @@ public class StringConverter extends DefaultTypeConverter {
     }
 
     protected String convertToString(Locale locale, Object value) {
+        if (BigDecimal.class.isInstance(value) && integerDigits((BigDecimal) 
value) > MAX_INTEGER_DIGITS) {
+            return value.toString();
+        }
         if (Number.class.isInstance(value)) {
             NumberFormat format = NumberFormat.getNumberInstance(locale);
             format.setGroupingUsed(false);
@@ -109,4 +122,8 @@ public class StringConverter extends DefaultTypeConverter {
         }
     }
 
+    private static long integerDigits(BigDecimal value) {
+        return (long) value.precision() - value.scale();
+    }
+
 }
diff --git 
a/core/src/test/java/com/opensymphony/xwork2/conversion/impl/StringConverterTest.java
 
b/core/src/test/java/com/opensymphony/xwork2/conversion/impl/StringConverterTest.java
index afc6072cb..4160a7eab 100644
--- 
a/core/src/test/java/com/opensymphony/xwork2/conversion/impl/StringConverterTest.java
+++ 
b/core/src/test/java/com/opensymphony/xwork2/conversion/impl/StringConverterTest.java
@@ -115,6 +115,36 @@ public class StringConverterTest extends 
StrutsInternalTestCase {
         assertEquals("0", value);
     }
 
+    public void testBigDecimalIntegerDigitsAreBounded() throws Exception {
+        // given
+        StringConverter converter = new StringConverter();
+        Map<String, Object> context = createContextWithLocale(new Locale("pl", 
"PL"));
+
+        // when the number of integer digits is within the bound
+        Object value = converter.convertValue(context, null, null, null, new 
BigDecimal(BigInteger.ONE, -339), null);
+
+        // then the value is rendered in full
+        assertEquals("1" + StringUtils.repeat('0', 339), value);
+
+        // when the number of integer digits exceeds the bound
+        value = converter.convertValue(context, null, null, null, new 
BigDecimal(BigInteger.ONE, -340), null);
+
+        // then the value is rendered in its compact form rather than padded 
out to its scale
+        assertEquals("1E+340", value);
+
+        // when the scale is far beyond the bound
+        value = converter.convertValue(context, null, null, null, new 
BigDecimal(BigInteger.ONE, -100_000), null);
+
+        // then the length of the output is bounded by the converter, not by 
the scale of the value
+        assertEquals("1E+100000", value);
+
+        // when the scale sits at the edge of its range
+        value = converter.convertValue(context, null, null, null, new 
BigDecimal(BigInteger.ONE, Integer.MIN_VALUE + 1), null);
+
+        // then the bound still applies
+        assertEquals("1E+2147483647", value);
+    }
+
     public void testStringArrayToStringConversion() {
         // given
         StringConverter converter = new StringConverter();

Reply via email to