Skip to content

WW-5836 Bound integer digits in number tag and message arguments - #2095

Open
lukaszlenart wants to merge 1 commit into
mainfrom
WW-5836-bound-bigdecimal-formatting
Open

lukaszlenart wants to merge 1 commit into
mainfrom
WW-5836-bound-bigdecimal-formatting

Conversation

@lukaszlenart

Copy link
Copy Markdown
Member

Fixes WW-5836

WW-5828 bounded the integer digits StringConverter emits for a BigDecimal, falling back to BigDecimal.toString() past 340. Two other paths format a BigDecimal without going through StringConverter, and neither applied the bound:

  • the number tag (Number.end()), which formats the value from the stack with NumberFormat directly;
  • MessageFormat arguments: AbstractLocalizedTextProvider.formatWithNullDetection (reached from getText(key, args), <s:text> with <s:param>, and validator messageParams) and DefaultTextProvider.getText(key, defaultValue, List).

The bound and its check (precision - scale, computed in long) move to BigDecimalBounds, shared by StringConverter, the number tag and message formatting.

Number tag. Past the bound the value renders with toString(). The tag's maximumIntegerDigits attribute is not a substitute, because DecimalFormat applies it by dropping leading digits.

Message arguments. The argument itself cannot be replaced with a String, because a typed element such as {0,number} throws IllegalArgumentException on a String. Instead, when an argument is past the bound, BoundedMessageFormat formats through a clone whose elements delegate to their original formats and render the over-bound value with toString().

MessageFormat re-parses a choice branch that contains {n} only when the element's format is an actual ChoiceFormat. Wrapping a choice element would therefore print the nested {0} literally, and leaving it unwrapped would format the nested pattern without the bound. The wrapper repeats that step itself, with the bound applied to the nested pattern at every level. A call with no argument past the bound goes to MessageFormat unchanged.

Tests pin both sides of the bound for:

  • the number tag, including type="currency";
  • plain {0}, {0,number} and {0,number,currency};
  • mixed elements (number pattern, String, date, null) next to an over-bound argument;
  • choice elements: nested one and two levels deep, a small choice argument with an over-bound nested {1}, and a branch with no nested argument.

Below the bound, the output is compared byte for byte with plain MessageFormat. Full core suite green (3633).

Observable change: a value with more than 340 integer digits renders in 1E+340 form in these paths, as it already does through StringConverter. A {0,date} element given such a value prints that form too, instead of a date built from longValue().

A 6.x backport follows in a separate PR.

🤖 Generated with Claude Code

…uments

WW-5828 bounded the integer digits StringConverter emits for a BigDecimal
and falls back to BigDecimal.toString() past 340. The number tag and
MessageFormat arguments format a BigDecimal without StringConverter, so
neither applied the bound.

The bound moves to BigDecimalBounds and is shared by StringConverter, the
number tag and message argument formatting.

The number tag falls back to toString() past the bound. Its
maximumIntegerDigits attribute is not a substitute: DecimalFormat applies
it by dropping leading digits.

For message arguments the argument itself cannot be replaced, because a
typed element such as {0,number} rejects a String. When an argument is
past the bound, BoundedMessageFormat formats through a clone whose
elements delegate to their original formats and render the over-bound
value with toString(). A ChoiceFormat element whose branch contains {n}
is re-parsed by MessageFormat only when the element's format is a
ChoiceFormat, so the wrapper repeats that step with the bound applied to
the nested pattern. A call with no argument past the bound is formatted
by MessageFormat unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@sonarqubecloud

sonarqubecloud Bot commented Nov 3, 2026

Copy link
Copy Markdown

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant