diff --git a/src/main/java/org/apache/xmlbeans/impl/xsd2inst/SampleXmlUtil.java b/src/main/java/org/apache/xmlbeans/impl/xsd2inst/SampleXmlUtil.java
index ca9248357..fc19e4aaa 100644
--- a/src/main/java/org/apache/xmlbeans/impl/xsd2inst/SampleXmlUtil.java
+++ b/src/main/java/org/apache/xmlbeans/impl/xsd2inst/SampleXmlUtil.java
@@ -382,6 +382,24 @@ private String formatToLength(String s, SchemaType sType) {
return result;
}
+ /**
+ * The number of digits before the decimal point, which is the same for every
+ * representation of a value.
+ */
+ private static long integerDigits(BigDecimal value) {
+ return (long) value.precision() - value.scale();
+ }
+
+ /**
+ * The largest value that fits in the given number of digits, ie 999...9. Computed as
+ * 10^digits - 1 rather than built as a string of nines, which the maximum number of
+ * characters allowed for a number rejects once the schema asks for more digits than
+ * that - xsd:totalDigits is a positiveInteger and has no such ceiling.
+ */
+ private static BigDecimal widestValue(int digits) {
+ return BigDecimal.TEN.pow(digits).subtract(BigDecimal.ONE);
+ }
+
private String formatDecimal(String start, SchemaType sType) {
BigDecimal result = MathUtil.parseAsBigDecimal(start);
XmlDecimal xmlD;
@@ -411,19 +429,22 @@ private String formatDecimal(String start, SchemaType sType) {
if (xmlD != null) {
totalDigits = MathUtil.toInt(xmlD.getBigDecimalValue());
- StringBuilder sb = new StringBuilder(totalDigits);
- for (int i = 0; i < totalDigits; i++) {
- sb.append('9');
- }
- BigDecimal digitsLimit = MathUtil.parseAsBigDecimal(sb.toString());
- if (max != null && max.compareTo(digitsLimit) > 0) {
- max = digitsLimit;
- maxInclusive = true;
+ // the widest value the facet allows can only narrow a bound that is at least
+ // as wide, so it is computed only when it can bind - which also keeps it away
+ // from a totalDigits far larger than any bound the schema declares
+ if (max != null && integerDigits(max) >= totalDigits) {
+ BigDecimal digitsLimit = widestValue(totalDigits);
+ if (max.compareTo(digitsLimit) > 0) {
+ max = digitsLimit;
+ maxInclusive = true;
+ }
}
- digitsLimit = digitsLimit.negate();
- if (min != null && min.compareTo(digitsLimit) < 0) {
- min = digitsLimit;
- minInclusive = true;
+ if (min != null && integerDigits(min) >= totalDigits) {
+ BigDecimal digitsLimit = widestValue(totalDigits).negate();
+ if (min.compareTo(digitsLimit) < 0) {
+ min = digitsLimit;
+ minInclusive = true;
+ }
}
}
@@ -441,12 +462,8 @@ private String formatDecimal(String start, SchemaType sType) {
} else {
fractionDigits = MathUtil.toInt(xmlD.getBigDecimalValue());
if (fractionDigits > 0) {
- StringBuilder sb = new StringBuilder("0.");
- for (int i = 1; i < fractionDigits; i++) {
- sb.append('0');
- }
- sb.append('1');
- increment = MathUtil.parseAsBigDecimal(sb.toString());
+ // 1 shifted fractionDigits places right, ie 0.00...1
+ increment = BigDecimal.ONE.scaleByPowerOfTen(-fractionDigits);
} else {
increment = BigDecimal.ONE;
}
diff --git a/src/test/java/tools/xsd2inst/checkin/Xsd2InstTest.java b/src/test/java/tools/xsd2inst/checkin/Xsd2InstTest.java
index ed9f47cc9..3bad771fa 100644
--- a/src/test/java/tools/xsd2inst/checkin/Xsd2InstTest.java
+++ b/src/test/java/tools/xsd2inst/checkin/Xsd2InstTest.java
@@ -62,6 +62,50 @@ void testBase64Binary() throws Exception {
}
}
+ private static String decimalSchema(String facets) {
+ return "" +
+ "" +
+ "" +
+ "" + facets + "" +
+ "";
+ }
+
+ private static String sampleFor(String facets) throws Exception {
+ XmlObject xsd = XmlObject.Factory.parse(decimalSchema(facets));
+ SchemaTypeSystem sts = XmlBeans.compileXsd(new XmlObject[]{xsd},
+ XmlBeans.getBuiltinTypeSystem(), new XmlOptions());
+ return SampleXmlUtil.createSampleForType(sts.globalElements()[0]);
+ }
+
+ @Test
+ void testDigitFacetsWiderThanTheNumberLengthLimit() throws Exception {
+ // xsd:totalDigits and xsd:fractionDigits are positiveInteger and so have no
+ // ceiling of their own; building the bounds they imply as strings of digits ran
+ // them into the maximum number of characters allowed for a number
+ String result = sampleFor("");
+ assertTrue(result.contains(""), result);
+ try (InputStream docStream = new ByteArrayInputStream(result.getBytes(StandardCharsets.UTF_8))) {
+ assertNotNull(DocumentHelper.readDocument(new XmlOptions(), docStream));
+ }
+
+ result = sampleFor("");
+ assertTrue(result.contains(""), result);
+ try (InputStream docStream = new ByteArrayInputStream(result.getBytes(StandardCharsets.UTF_8))) {
+ assertNotNull(DocumentHelper.readDocument(new XmlOptions(), docStream));
+ }
+ }
+
+ @Test
+ void testTotalDigitsStillNarrowsTheBounds() throws Exception {
+ // the sample seed for a decimal is 1000.00, which xsd:totalDigits has to pull
+ // down to the widest value the facet allows
+ String result = sampleFor("");
+ assertTrue(result.contains("999"), result);
+
+ String unconstrained = sampleFor("");
+ assertTrue(unconstrained.contains("1000.00"), unconstrained);
+ }
+
@Test
void testSampleXmlUtil() throws Exception {
XmlObject xobj;