diff --git a/core/src/main/java/org/apache/struts2/conversion/impl/StringConverter.java b/core/src/main/java/org/apache/struts2/conversion/impl/StringConverter.java index 807c82f87c..e79fcf7c41 100644 --- a/core/src/main/java/org/apache/struts2/conversion/impl/StringConverter.java +++ b/core/src/main/java/org/apache/struts2/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 (value instanceof BigDecimal || value instanceof Double || value instanceof Float) { - format.setMaximumFractionDigits(Integer.MAX_VALUE); + format.setMaximumFractionDigits(MAX_FRACTION_DIGITS); if (value instanceof Float) { fixedValue = Double.valueOf(value.toString()); } diff --git a/core/src/test/java/org/apache/struts2/conversion/impl/StringConverterTest.java b/core/src/test/java/org/apache/struts2/conversion/impl/StringConverterTest.java index a0310de191..fd3f673ab3 100644 --- a/core/src/test/java/org/apache/struts2/conversion/impl/StringConverterTest.java +++ b/core/src/test/java/org/apache/struts2/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();