Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -92,6 +102,9 @@ public Object convertValue(Map<String, Object> context, Object target, Member me
}

protected String convertToString(Locale locale, Object value) {
if (value instanceof BigDecimal && integerDigits((BigDecimal) value) > MAX_INTEGER_DIGITS) {
return value.toString();
}
if (Number.class.isInstance(value)) {
NumberFormat format = NumberFormat.getNumberInstance(locale);
format.setGroupingUsed(false);
Expand All @@ -109,4 +122,8 @@ protected String convertToString(Locale locale, Object value) {
}
}

private static long integerDigits(BigDecimal value) {
return (long) value.precision() - value.scale();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,36 @@ public void testBigDecimalFractionDigitsAreBounded() throws Exception {
assertEquals("0", value);
}

public void testBigDecimalIntegerDigitsAreBounded() {
// 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();
Expand Down
Loading