Skip to content

Stop xsd2inst building digit-facet bounds as strings - #99

Merged
pjfanning merged 1 commit into
apache:trunkfrom
pjfanning:fix-xsd2inst-digit-facets
Aug 26, 2026
Merged

Stop xsd2inst building digit-facet bounds as strings#99
pjfanning merged 1 commit into
apache:trunkfrom
pjfanning:fix-xsd2inst-digit-facets

Conversation

@pjfanning

Copy link
Copy Markdown
Member

Branched from trunk, independent of #98.

The bug

SampleXmlUtil.formatDecimal derived both digit-facet bounds by building a string and parsing it back — xsd:totalDigits as that many nines, xsd:fractionDigits as "0.00...1". Both facets are positiveInteger with no ceiling of their own, so as soon as a schema asks for more digits than maxNumberOfCharsForNumbers allows, parseAsBigDecimal rejects the string the method just built:

IllegalArgumentException: Number has more than 1024 characters

and xsd2inst cannot generate a sample for the schema at all. 5.3.0 used new BigDecimal(...) here and was unaffected; the limit arrived in 5.4.0.

A minimal reproducer — nothing else in the schema:

<xs:simpleType name="constrainedDecimal">
  <xs:restriction base="xs:decimal">
    <xs:totalDigits value="2000"/>
  </xs:restriction>
</xs:simpleType>

The fractionDigits branch fails the same way at 1023.

The fix

Compute both values instead of spelling them out:

  • 10^totalDigits - 1 for the widest value the facet allows;
  • BigDecimal.ONE.scaleByPowerOfTen(-fractionDigits) for the increment, which only sets a scale and expands nothing.

The totalDigits bound can only narrow a min/max that is at least as wide, so it is now computed only when it can actually bind. That is what keeps 10^n away from a totalDigits far larger than any bound the schema declares — the previous code built its string of nines unconditionally, even when no min or max facet existed for it to constrain.

Behaviour for schemas within the limit is unchanged: totalDigits still pulls the seed value down to the widest value it permits.

Tests

testDigitFacetsWiderThanTheNumberLengthLimit covers both facets past the limit — it fails on trunk with the exception above. testTotalDigitsStillNarrowsTheBounds pins the normal case (seed 1000.00, totalDigits=3999) and passes both before and after, so it guards the semantics rather than the fix.

Full suite: 3084 tests, 0 failures.

🤖 Generated with Claude Code

formatDecimal turned xsd:totalDigits into a string of that many nines and
xsd:fractionDigits into "0.00...1", then parsed each back. Both facets are
positiveInteger and have no ceiling, so once a schema asked for more digits
than the maximum allowed for a number, parsing the string it had just built
threw and no sample could be generated:

  IllegalArgumentException: Number has more than 1024 characters

5.3.0 used new BigDecimal(...) here and was unaffected.

Compute both directly instead - 10^totalDigits - 1, and 1 shifted
fractionDigits places right. The totalDigits bound can only narrow a limit
at least as wide, so it is computed only when it can bind, which also keeps
10^n away from a totalDigits far larger than any bound the schema declares.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@pjfanning
pjfanning merged commit 4673629 into apache:trunk Aug 26, 2026
3 checks passed
@pjfanning
pjfanning deleted the fix-xsd2inst-digit-facets branch August 26, 2026 23:40
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