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 c1abc9b7c7..6b1cb2fbc2 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
@@ -36,6 +36,18 @@
public class StringConverter extends DefaultTypeConverter {
+ /**
+ * Upper bound on the number of fraction digits emitted when formatting a number.
+ *
+ * Covers every {@code double} and {@code float} value in full - the widest is
+ * {@link Double#MIN_VALUE} at 325 fraction digits - so the round-trip precision
+ * introduced by WW-4871 is preserved. Beyond that bound the length of the output
+ * would follow the scale of the value rather than its precision, so a
+ * {@link BigDecimal} scaled past this limit is rounded to it.
+ */
+ private static final int MAX_FRACTION_DIGITS = 340;
+
+
@Override
public Object convertValue(Map context, Object target, Member member, String propertyName, Object value, Class toType) {
String result;
@@ -86,7 +98,7 @@ protected String convertToString(Locale locale, Object value) {
// TODO: delete this variable and corresponding if statement when jdk fixed java.text.NumberFormat.format's behavior with Float
Object fixedValue = value;
if (BigDecimal.class.isInstance(value) || Double.class.isInstance(value) || Float.class.isInstance(value)) {
- format.setMaximumFractionDigits(Integer.MAX_VALUE);
+ format.setMaximumFractionDigits(MAX_FRACTION_DIGITS);
if (Float.class.isInstance(value)) {
fixedValue = Double.valueOf(value.toString());
}
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 92d0814d66..afc6072cb9 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
@@ -22,6 +22,7 @@
import org.apache.struts2.StrutsInternalTestCase;
import java.math.BigDecimal;
+import java.math.BigInteger;
import java.util.Locale;
import java.util.Map;
@@ -102,6 +103,18 @@ public void testBigDecimalToStringConversionPL() throws Exception {
assertEquals(aBitBiggerThanDouble.substring(0, 309) + "," + aBitBiggerThanDouble.substring(310), value);
}
+ public void testBigDecimalFractionDigitsAreBounded() throws Exception {
+ // given
+ StringConverter converter = new StringConverter();
+ Map context = createContextWithLocale(new Locale("pl", "PL"));
+
+ // when the scale of the value exceeds the supported number of fraction digits
+ Object 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("0", value);
+ }
+
public void testStringArrayToStringConversion() {
// given
StringConverter converter = new StringConverter();