diff --git a/src/main/java/org/apache/xmlbeans/impl/common/XmlLocale.java b/src/main/java/org/apache/xmlbeans/impl/common/XmlLocale.java index 93bd75cea..112c14ec1 100755 --- a/src/main/java/org/apache/xmlbeans/impl/common/XmlLocale.java +++ b/src/main/java/org/apache/xmlbeans/impl/common/XmlLocale.java @@ -17,6 +17,8 @@ package org.apache.xmlbeans.impl.common; +import org.apache.xmlbeans.XmlOptions; + public interface XmlLocale { boolean sync ( ); @@ -34,4 +36,9 @@ public interface XmlLocale // (e.g. "1E5"), which is outside the xsd:decimal lexical space. Defaults to // false (reject). Driven by XmlOptions.setLoadAllowDecimalExponent. default boolean isLoadAllowDecimalExponent ( ) { return false; } + + // the maximum number of characters a lexical number may have before it is + // rejected, applied when values are materialised from the store. Driven by + // XmlOptions.setMaxNumberOfCharsForNumbers. + default int getMaxNumberOfCharsForNumbers ( ) { return XmlOptions.DEFAULT_MAX_NUMBER_CHARS; } } diff --git a/src/main/java/org/apache/xmlbeans/impl/store/Locale.java b/src/main/java/org/apache/xmlbeans/impl/store/Locale.java index f27ea53ef..ddaef58e3 100755 --- a/src/main/java/org/apache/xmlbeans/impl/store/Locale.java +++ b/src/main/java/org/apache/xmlbeans/impl/store/Locale.java @@ -107,6 +107,8 @@ private Locale(SchemaTypeLoader stl, XmlOptions options) { _loadAllowDecimalExponent = options.isLoadAllowDecimalExponent(); + _maxNumberOfChars = options.getMaxNumberOfCharsForNumbers(); + // // Check for Saaj implementation request // @@ -2083,6 +2085,10 @@ public boolean isLoadAllowDecimalExponent() { return _loadAllowDecimalExponent; } + public int getMaxNumberOfCharsForNumbers() { + return _maxNumberOfChars; + } + static boolean isWhiteSpace(String s) { int l = s.length(); @@ -2805,6 +2811,8 @@ public QName getQName(char[] uriSrc, int uriPos, int uriCch, boolean _loadAllowDecimalExponent; + int _maxNumberOfChars; + int _posTemp; nthCache _nthCache_A = new nthCache(); diff --git a/src/main/java/org/apache/xmlbeans/impl/util/MathUtil.java b/src/main/java/org/apache/xmlbeans/impl/util/MathUtil.java index feb50d355..e9e51d91e 100644 --- a/src/main/java/org/apache/xmlbeans/impl/util/MathUtil.java +++ b/src/main/java/org/apache/xmlbeans/impl/util/MathUtil.java @@ -91,11 +91,23 @@ public static BigDecimal parseAsBigDecimal(String s, int maxNumberOfChars) { * @throws NullPointerException if string is null */ public static BigInteger parseAsBigInteger(String s) { + return parseAsBigInteger(s, DEFAULT_MAX_NUMBER_CHARS); + } + + /** + * @param s string to parse + * @param maxNumberOfChars maximum number of characters allowed in the string + * @return valid BigInteger + * @throws NumberFormatException if parse fails + * @throws IllegalArgumentException if string is too long + * @throws NullPointerException if string is null + */ + public static BigInteger parseAsBigInteger(String s, int maxNumberOfChars) { if (s == null) { throw new NullPointerException("Cannot parse null as BigInteger"); } - if (s.length() > DEFAULT_MAX_NUMBER_CHARS) { - throw new IllegalArgumentException("Number has more than " + DEFAULT_MAX_NUMBER_CHARS + " characters"); + if (s.length() > maxNumberOfChars) { + throw new IllegalArgumentException("Number has more than " + maxNumberOfChars + " characters"); } return new BigInteger(s); } @@ -166,11 +178,23 @@ public static double parseAsDouble(String s, int maxNumberOfChars) { * @throws NullPointerException if string is null */ public static long parseAsLong(String s) { + return parseAsLong(s, DEFAULT_MAX_NUMBER_CHARS); + } + + /** + * @param s string to parse + * @param maxNumberOfChars maximum number of characters allowed in the string + * @return valid long + * @throws NumberFormatException if parse fails + * @throws IllegalArgumentException if string is too long + * @throws NullPointerException if string is null + */ + public static long parseAsLong(String s, int maxNumberOfChars) { if (s == null) { throw new NullPointerException("Cannot parse null as Long"); } - if (s.length() > DEFAULT_MAX_NUMBER_CHARS) { - throw new IllegalArgumentException("Number has more than " + DEFAULT_MAX_NUMBER_CHARS + " characters"); + if (s.length() > maxNumberOfChars) { + throw new IllegalArgumentException("Number has more than " + maxNumberOfChars + " characters"); } return Long.parseLong(s); } @@ -199,6 +223,17 @@ public static int parseAsInt(String s) { * @throws NullPointerException if value is null */ public static BigInteger toBigInteger(BigDecimal value) { + return toBigInteger(value, DEFAULT_MAX_NUMBER_CHARS); + } + + /** + * @param value BigDecimal to convert + * @param maxNumberOfChars maximum number of integer digits allowed in the value + * @return valid BigInteger + * @throws IllegalArgumentException if the input has an absolute exponent that is too large to safely convert + * @throws NullPointerException if value is null + */ + public static BigInteger toBigInteger(BigDecimal value, int maxNumberOfChars) { if (value == null) { throw new NullPointerException("Cannot convert null to BigInteger"); } @@ -206,10 +241,10 @@ public static BigInteger toBigInteger(BigDecimal value) { int integerDigits = normalized.precision() - normalized.scale(); // the scale check is not redundant: for a very negative scale (eg 1E+2147483647) the // subtraction above overflows and integerDigits comes out negative - if (integerDigits > DEFAULT_MAX_NUMBER_CHARS || normalized.scale() < -DEFAULT_MAX_NUMBER_CHARS) { + if (integerDigits > maxNumberOfChars || normalized.scale() < -maxNumberOfChars) { throw new IllegalArgumentException( "BigDecimal magnitude too large to convert safely: approx " - + integerDigits + " integer digits (limit " + DEFAULT_MAX_NUMBER_CHARS + ")"); + + integerDigits + " integer digits (limit " + maxNumberOfChars + ")"); } if (integerDigits <= 0) { // abs(value) is less than 1, so it truncates to zero - avoid BigDecimal.toBigInteger() diff --git a/src/main/java/org/apache/xmlbeans/impl/util/XsTypeConverter.java b/src/main/java/org/apache/xmlbeans/impl/util/XsTypeConverter.java index f20978f8f..afcd131f3 100644 --- a/src/main/java/org/apache/xmlbeans/impl/util/XsTypeConverter.java +++ b/src/main/java/org/apache/xmlbeans/impl/util/XsTypeConverter.java @@ -239,6 +239,21 @@ public static BigDecimal lexDecimal(CharSequence cs) * @since 5.4.0 */ public static BigDecimal lexDecimal(CharSequence cs, boolean allowExponent) + throws NumberFormatException { + return lexDecimal(cs, allowExponent, XmlOptions.DEFAULT_MAX_NUMBER_CHARS); + } + + /** + * Parses an xsd:decimal lexical value. + * + * @param cs the lexical value + * @param allowExponent see {@link #lexDecimal(CharSequence, boolean)} + * @param maxNumberOfChars maximum number of characters allowed in the lexical value + * @return the parsed decimal + * @throws NumberFormatException if the value is not a valid xsd:decimal + * @since 5.4.1 + */ + public static BigDecimal lexDecimal(CharSequence cs, boolean allowExponent, int maxNumberOfChars) throws NumberFormatException { rejectInvalidNumber(cs); if (!allowExponent) { @@ -252,7 +267,7 @@ public static BigDecimal lexDecimal(CharSequence cs, boolean allowExponent) //equals() method, but the xml value //space does not consider them significant. //See http://www.w3.org/2001/05/xmlschema-errata#e2-44 - return MathUtil.parseAsBigDecimal(trimTrailingZeros(v)); + return MathUtil.parseAsBigDecimal(trimTrailingZeros(v), maxNumberOfChars); } private static final char[] CH_ZEROS = new char[]{'0', '0', '0', '0', '0', '0', '0', '0', @@ -306,13 +321,25 @@ public static String printDecimal(BigDecimal value) { // ======================== integer ======================== public static BigInteger lexInteger(CharSequence cs) + throws NumberFormatException { + return lexInteger(cs, XmlOptions.DEFAULT_MAX_NUMBER_CHARS); + } + + /** + * @param cs the lexical value + * @param maxNumberOfChars maximum number of characters allowed in the lexical value + * @return the parsed integer + * @throws NumberFormatException if the value is not a valid xsd:integer + * @since 5.4.1 + */ + public static BigInteger lexInteger(CharSequence cs, int maxNumberOfChars) throws NumberFormatException { rejectSignAfterPlus(cs); final String v = cs.toString(); //TODO: consider special casing zero and one to return static values //from BigInteger to avoid object creation. - return MathUtil.parseAsBigInteger(trimInitialPlus(v)); + return MathUtil.parseAsBigInteger(trimInitialPlus(v), maxNumberOfChars); } public static BigInteger lexInteger(CharSequence cs, Collection errors) { @@ -331,11 +358,23 @@ public static String printInteger(BigInteger value) { // ======================== long ======================== public static long lexLong(CharSequence cs) + throws NumberFormatException { + return lexLong(cs, XmlOptions.DEFAULT_MAX_NUMBER_CHARS); + } + + /** + * @param cs the lexical value + * @param maxNumberOfChars maximum number of characters allowed in the lexical value + * @return the parsed long + * @throws NumberFormatException if the value is not a valid xsd:long + * @since 5.4.1 + */ + public static long lexLong(CharSequence cs, int maxNumberOfChars) throws NumberFormatException { rejectInvalidNumber(cs); rejectSignAfterPlus(cs); final String v = cs.toString(); - return MathUtil.parseAsLong(trimInitialPlus(v)); + return MathUtil.parseAsLong(trimInitialPlus(v), maxNumberOfChars); } // trimInitialPlus drops a single leading '+', then Long.parseLong / diff --git a/src/main/java/org/apache/xmlbeans/impl/values/JavaDecimalHolder.java b/src/main/java/org/apache/xmlbeans/impl/values/JavaDecimalHolder.java index 022dddb7f..c2bdfbc25 100644 --- a/src/main/java/org/apache/xmlbeans/impl/values/JavaDecimalHolder.java +++ b/src/main/java/org/apache/xmlbeans/impl/values/JavaDecimalHolder.java @@ -44,12 +44,13 @@ protected String compute_text(NamespaceManager nsm) { protected void set_text(String s) { boolean allowExponent = has_store() && get_store().get_locale().isLoadAllowDecimalExponent(); + int maxNumberOfChars = get_max_number_chars(); if (_validateOnSet()) { - validateLexical(s, _voorVc, allowExponent); + validateLexical(s, _voorVc, allowExponent, maxNumberOfChars); } try { - set_BigDecimal(MathUtil.parseAsBigDecimal(s)); + set_BigDecimal(MathUtil.parseAsBigDecimal(s, maxNumberOfChars)); } catch (Exception e) { _voorVc.invalid(XmlErrorCodes.DECIMAL, new Object[]{s}); } @@ -162,7 +163,7 @@ protected int value_hash_code() { } } - BigInteger intval = MathUtil.toBigInteger(_value); + BigInteger intval = MathUtil.toBigInteger(_value, get_max_number_chars()); if (intval.compareTo(_maxlong) > 0 || intval.compareTo(_minlong) < 0) { diff --git a/src/main/java/org/apache/xmlbeans/impl/values/JavaDecimalHolderEx.java b/src/main/java/org/apache/xmlbeans/impl/values/JavaDecimalHolderEx.java index d04dffbf8..70c8d38ac 100644 --- a/src/main/java/org/apache/xmlbeans/impl/values/JavaDecimalHolderEx.java +++ b/src/main/java/org/apache/xmlbeans/impl/values/JavaDecimalHolderEx.java @@ -39,14 +39,15 @@ public JavaDecimalHolderEx(SchemaType type, boolean complex) { } protected void set_text(String s) { + int maxNumberOfChars = get_max_number_chars(); if (_validateOnSet()) { boolean allowExponent = has_store() && get_store().get_locale().isLoadAllowDecimalExponent(); - validateLexical(s, _schemaType, _voorVc, allowExponent); + validateLexical(s, _schemaType, _voorVc, allowExponent, maxNumberOfChars); } BigDecimal v = null; try { - v = MathUtil.parseAsBigDecimal(s); + v = MathUtil.parseAsBigDecimal(s, maxNumberOfChars); } catch (Exception e) { _voorVc.invalid(XmlErrorCodes.DECIMAL, new Object[]{s}); } diff --git a/src/main/java/org/apache/xmlbeans/impl/values/JavaIntHolder.java b/src/main/java/org/apache/xmlbeans/impl/values/JavaIntHolder.java index 48d533474..72d765ef2 100644 --- a/src/main/java/org/apache/xmlbeans/impl/values/JavaIntHolder.java +++ b/src/main/java/org/apache/xmlbeans/impl/values/JavaIntHolder.java @@ -78,7 +78,7 @@ public int getIntValue() { // setters protected void set_BigDecimal(BigDecimal v) { - set_BigInteger(MathUtil.toBigInteger(v)); + set_BigInteger(MathUtil.toBigInteger(v, get_max_number_chars())); } protected void set_BigInteger(BigInteger v) { diff --git a/src/main/java/org/apache/xmlbeans/impl/values/JavaIntHolderEx.java b/src/main/java/org/apache/xmlbeans/impl/values/JavaIntHolderEx.java index dc0e8a4b8..acae15788 100644 --- a/src/main/java/org/apache/xmlbeans/impl/values/JavaIntHolderEx.java +++ b/src/main/java/org/apache/xmlbeans/impl/values/JavaIntHolderEx.java @@ -18,6 +18,7 @@ import org.apache.xmlbeans.SchemaType; import org.apache.xmlbeans.XmlErrorCodes; import org.apache.xmlbeans.XmlObject; +import org.apache.xmlbeans.XmlOptions; import org.apache.xmlbeans.impl.common.QNameHelper; import org.apache.xmlbeans.impl.common.ValidationContext; import org.apache.xmlbeans.impl.util.MathUtil; @@ -47,7 +48,7 @@ protected void set_text(String s) { if (_validateOnSet()) { validateValue(v, _schemaType, _voorVc); - validateLexical(s, _schemaType, _voorVc); + validateLexical(s, _schemaType, _voorVc, get_max_number_chars()); } super.set_int(v); @@ -62,7 +63,12 @@ protected void set_int(int v) { } public static void validateLexical(String v, SchemaType sType, ValidationContext context) { - JavaDecimalHolder.validateLexical(v, context); + validateLexical(v, sType, context, XmlOptions.DEFAULT_MAX_NUMBER_CHARS); + } + + public static void validateLexical(String v, SchemaType sType, ValidationContext context, + int maxNumberOfChars) { + JavaDecimalHolder.validateLexical(v, context, false, maxNumberOfChars); // check pattern if (sType.hasPatternFacet()) { diff --git a/src/main/java/org/apache/xmlbeans/impl/values/JavaIntegerHolder.java b/src/main/java/org/apache/xmlbeans/impl/values/JavaIntegerHolder.java index ffc2bc0be..3c4c8f8ca 100644 --- a/src/main/java/org/apache/xmlbeans/impl/values/JavaIntegerHolder.java +++ b/src/main/java/org/apache/xmlbeans/impl/values/JavaIntegerHolder.java @@ -19,6 +19,7 @@ import org.apache.xmlbeans.SimpleValue; import org.apache.xmlbeans.XmlErrorCodes; import org.apache.xmlbeans.XmlObject; +import org.apache.xmlbeans.XmlOptions; import org.apache.xmlbeans.impl.common.ValidationContext; import org.apache.xmlbeans.impl.schema.BuiltinSchemaTypeSystem; import org.apache.xmlbeans.impl.util.MathUtil; @@ -41,16 +42,20 @@ protected String compute_text(NamespaceManager nsm) { } protected void set_text(String s) { - set_BigInteger(lex(s, _voorVc)); + set_BigInteger(lex(s, _voorVc, get_max_number_chars())); } public static BigInteger lex(String s, ValidationContext vc) { + return lex(s, vc, XmlOptions.DEFAULT_MAX_NUMBER_CHARS); + } + + public static BigInteger lex(String s, ValidationContext vc, int maxNumberOfChars) { if (!s.isEmpty() && s.charAt(0) == '+') { s = s.substring(1); } try { - return MathUtil.parseAsBigInteger(s); + return MathUtil.parseAsBigInteger(s, maxNumberOfChars); } catch (Exception e) { vc.invalid(XmlErrorCodes.INTEGER, new Object[]{s}); return null; @@ -74,7 +79,7 @@ public BigInteger getBigIntegerValue() { // setters protected void set_BigDecimal(BigDecimal v) { - _value = MathUtil.toBigInteger(v); + _value = MathUtil.toBigInteger(v, get_max_number_chars()); } protected void set_BigInteger(BigInteger v) { diff --git a/src/main/java/org/apache/xmlbeans/impl/values/JavaIntegerHolderEx.java b/src/main/java/org/apache/xmlbeans/impl/values/JavaIntegerHolderEx.java index 53ce72999..2f5477416 100644 --- a/src/main/java/org/apache/xmlbeans/impl/values/JavaIntegerHolderEx.java +++ b/src/main/java/org/apache/xmlbeans/impl/values/JavaIntegerHolderEx.java @@ -18,6 +18,7 @@ import org.apache.xmlbeans.SchemaType; import org.apache.xmlbeans.XmlErrorCodes; import org.apache.xmlbeans.XmlObject; +import org.apache.xmlbeans.XmlOptions; import org.apache.xmlbeans.XmlPositiveInteger; import org.apache.xmlbeans.impl.common.QNameHelper; import org.apache.xmlbeans.impl.common.ValidationContext; @@ -39,14 +40,15 @@ public SchemaType schemaType() { } protected void set_text(String s) { - BigInteger v = lex(s, _voorVc); + int maxNumberOfChars = get_max_number_chars(); + BigInteger v = lex(s, _voorVc, maxNumberOfChars); if (_validateOnSet()) { validateValue(v, _schemaType, _voorVc); } if (_validateOnSet()) { - validateLexical(s, _schemaType, _voorVc); + validateLexical(s, _schemaType, _voorVc, maxNumberOfChars); } super.set_BigInteger(v); @@ -61,7 +63,12 @@ protected void set_BigInteger(BigInteger v) { } public static void validateLexical(String v, SchemaType sType, ValidationContext context) { - JavaDecimalHolder.validateLexical(v, context); + validateLexical(v, sType, context, XmlOptions.DEFAULT_MAX_NUMBER_CHARS); + } + + public static void validateLexical(String v, SchemaType sType, ValidationContext context, + int maxNumberOfChars) { + JavaDecimalHolder.validateLexical(v, context, false, maxNumberOfChars); if (v.lastIndexOf('.') >= 0) { context.invalid(XmlErrorCodes.INTEGER, new Object[]{v}); diff --git a/src/main/java/org/apache/xmlbeans/impl/values/JavaLongHolder.java b/src/main/java/org/apache/xmlbeans/impl/values/JavaLongHolder.java index d432a0b20..bc21efe96 100644 --- a/src/main/java/org/apache/xmlbeans/impl/values/JavaLongHolder.java +++ b/src/main/java/org/apache/xmlbeans/impl/values/JavaLongHolder.java @@ -42,7 +42,7 @@ protected String compute_text(NamespaceManager nsm) { protected void set_text(String s) { try { - set_long(XsTypeConverter.lexLong(s)); + set_long(XsTypeConverter.lexLong(s, get_max_number_chars())); } catch (Exception e) { throw new XmlValueOutOfRangeException(XmlErrorCodes.LONG, new Object[]{s}); } @@ -73,7 +73,7 @@ public long getLongValue() { // setters protected void set_BigDecimal(BigDecimal v) { - set_BigInteger(MathUtil.toBigInteger(v)); + set_BigInteger(MathUtil.toBigInteger(v, get_max_number_chars())); } protected void set_BigInteger(BigInteger v) { diff --git a/src/main/java/org/apache/xmlbeans/impl/values/JavaLongHolderEx.java b/src/main/java/org/apache/xmlbeans/impl/values/JavaLongHolderEx.java index d88f1c644..737989e77 100644 --- a/src/main/java/org/apache/xmlbeans/impl/values/JavaLongHolderEx.java +++ b/src/main/java/org/apache/xmlbeans/impl/values/JavaLongHolderEx.java @@ -18,6 +18,7 @@ import org.apache.xmlbeans.SchemaType; import org.apache.xmlbeans.XmlErrorCodes; import org.apache.xmlbeans.XmlObject; +import org.apache.xmlbeans.XmlOptions; import org.apache.xmlbeans.impl.common.QNameHelper; import org.apache.xmlbeans.impl.common.ValidationContext; import org.apache.xmlbeans.impl.util.MathUtil; @@ -47,7 +48,7 @@ protected void set_text(String s) { if (_validateOnSet()) { validateValue(v, _schemaType, _voorVc); - validateLexical(s, _schemaType, _voorVc); + validateLexical(s, _schemaType, _voorVc, get_max_number_chars()); } super.set_long(v); @@ -62,7 +63,12 @@ protected void set_long(long v) { } public static void validateLexical(String v, SchemaType sType, ValidationContext context) { - JavaDecimalHolder.validateLexical(v, context); + validateLexical(v, sType, context, XmlOptions.DEFAULT_MAX_NUMBER_CHARS); + } + + public static void validateLexical(String v, SchemaType sType, ValidationContext context, + int maxNumberOfChars) { + JavaDecimalHolder.validateLexical(v, context, false, maxNumberOfChars); // check pattern if (sType.hasPatternFacet()) { diff --git a/src/main/java/org/apache/xmlbeans/impl/values/XmlObjectBase.java b/src/main/java/org/apache/xmlbeans/impl/values/XmlObjectBase.java index 8623ff50f..daf812a49 100644 --- a/src/main/java/org/apache/xmlbeans/impl/values/XmlObjectBase.java +++ b/src/main/java/org/apache/xmlbeans/impl/values/XmlObjectBase.java @@ -815,6 +815,16 @@ protected final boolean has_store() { return (_flags & FLAG_STORE) != 0; } + /** + * The maximum number of characters a lexical number may have, as configured by + * XmlOptions.setMaxNumberOfCharsForNumbers on the options the document was loaded with. + */ + protected final int get_max_number_chars() { + return has_store() + ? get_store().get_locale().getMaxNumberOfCharsForNumbers() + : XmlOptions.DEFAULT_MAX_NUMBER_CHARS; + } + /** * Called by a TypeStore to pull out the most reasonable * text value from us. This is done after we have invalidated @@ -1347,7 +1357,7 @@ public BigDecimal getBigDecimalValue() { // numerics: integral public BigInteger getBigIntegerValue() { BigDecimal bd = getBigDecimalValue(); - return bd == null ? null : MathUtil.toBigInteger(bd); + return bd == null ? null : MathUtil.toBigInteger(bd, get_max_number_chars()); } public byte getByteValue() { diff --git a/src/test/java/misc/checkin/MaxNumberOfCharsTest.java b/src/test/java/misc/checkin/MaxNumberOfCharsTest.java new file mode 100644 index 000000000..ad7f32311 --- /dev/null +++ b/src/test/java/misc/checkin/MaxNumberOfCharsTest.java @@ -0,0 +1,98 @@ +/* Copyright 2004 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package misc.checkin; + +import org.apache.xmlbeans.SimpleValue; +import org.apache.xmlbeans.XmlDecimal; +import org.apache.xmlbeans.XmlException; +import org.apache.xmlbeans.XmlInteger; +import org.apache.xmlbeans.XmlOptions; +import org.apache.xmlbeans.impl.values.XmlValueOutOfRangeException; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +/** + * XmlOptions.setMaxNumberOfCharsForNumbers has to apply to values materialised from the + * store, not just to the explicit XmlObject.validate(options) path. + */ +public class MaxNumberOfCharsTest { + /** a number of exactly {@code n} characters */ + private static String digits(int n) { + StringBuilder sb = new StringBuilder(n); + sb.append('1'); + for (int i = 1; i < n; i++) { + sb.append('0'); + } + return sb.toString(); + } + + private static String frag(String value) { + return "" + value + ""; + } + + private static XmlOptions maxChars(int max) { + XmlOptions options = new XmlOptions(); + options.setMaxNumberOfCharsForNumbers(max); + return options; + } + + @Test + public void testDecimalHonoursRaisedLimit() throws XmlException { + XmlDecimal value = XmlDecimal.Factory.parse(frag(digits(2000)), maxChars(4096)); + assertEquals(2000, value.getBigDecimalValue().precision()); + } + + @Test + public void testDecimalHonoursLoweredLimit() throws XmlException { + XmlDecimal value = XmlDecimal.Factory.parse(frag("123456789012"), maxChars(8)); + assertThrows(XmlValueOutOfRangeException.class, value::getBigDecimalValue); + } + + @Test + public void testDecimalUsesDefaultLimitWhenUnset() throws XmlException { + XmlDecimal withinDefault = XmlDecimal.Factory.parse(frag(digits(1000))); + assertEquals(1000, withinDefault.getBigDecimalValue().precision()); + + XmlDecimal overDefault = XmlDecimal.Factory.parse(frag(digits(2000))); + assertThrows(XmlValueOutOfRangeException.class, overDefault::getBigDecimalValue); + } + + @Test + public void testIntegerHonoursRaisedLimit() throws XmlException { + XmlInteger value = XmlInteger.Factory.parse(frag(digits(2000)), maxChars(4096)); + assertEquals(new java.math.BigInteger(digits(2000)), value.getBigIntegerValue()); + } + + @Test + public void testIntegerUsesDefaultLimitWhenUnset() throws XmlException { + XmlInteger overDefault = XmlInteger.Factory.parse(frag(digits(2000))); + assertThrows(XmlValueOutOfRangeException.class, overDefault::getBigIntegerValue); + } + + @Test + public void testDecimalToBigIntegerHonoursRaisedLimit() throws XmlException { + // getBigIntegerValue() over a decimal goes through MathUtil.toBigInteger + SimpleValue value = (SimpleValue) XmlDecimal.Factory.parse(frag(digits(2000)), maxChars(4096)); + assertEquals(new java.math.BigInteger(digits(2000)), value.getBigIntegerValue()); + } + + @Test + public void testDecimalToBigIntegerUsesDefaultLimitWhenUnset() throws XmlException { + SimpleValue value = (SimpleValue) XmlDecimal.Factory.parse(frag(digits(2000))); + assertThrows(XmlValueOutOfRangeException.class, value::getBigIntegerValue); + } +} diff --git a/src/test/java/org/apache/xmlbeans/impl/util/TestMathUtil.java b/src/test/java/org/apache/xmlbeans/impl/util/TestMathUtil.java index 5e304b115..d70666e3a 100644 --- a/src/test/java/org/apache/xmlbeans/impl/util/TestMathUtil.java +++ b/src/test/java/org/apache/xmlbeans/impl/util/TestMathUtil.java @@ -18,6 +18,8 @@ Licensed to the Apache Software Foundation (ASF) under one or more import org.junit.jupiter.api.Test; +import org.apache.xmlbeans.XmlOptions; + import java.math.BigDecimal; import java.math.BigInteger; import java.time.Duration; @@ -55,6 +57,37 @@ public void testToLongWithValueOutOfRange() { assertThrows(IllegalArgumentException.class, () -> MathUtil.toLong(expected2)); } + @Test + public void testParseAsBigIntegerWithMaxNumberOfChars() { + assertEquals(new BigInteger("12345"), MathUtil.parseAsBigInteger("12345", 5)); + assertThrows(IllegalArgumentException.class, () -> MathUtil.parseAsBigInteger("12345", 4)); + // the default overload still applies DEFAULT_MAX_NUMBER_CHARS + String tooLong = repeat('1', XmlOptions.DEFAULT_MAX_NUMBER_CHARS + 1); + assertThrows(IllegalArgumentException.class, () -> MathUtil.parseAsBigInteger(tooLong)); + assertEquals(new BigInteger(tooLong), MathUtil.parseAsBigInteger(tooLong, tooLong.length())); + } + + @Test + public void testParseAsLongWithMaxNumberOfChars() { + assertEquals(12345L, MathUtil.parseAsLong("12345", 5)); + assertThrows(IllegalArgumentException.class, () -> MathUtil.parseAsLong("12345", 4)); + } + + @Test + public void testToBigIntegerWithMaxNumberOfChars() { + BigDecimal value = new BigDecimal("1E+2000"); + assertThrows(IllegalArgumentException.class, () -> MathUtil.toBigInteger(value)); + assertEquals(new BigDecimal("1E+2000").toBigInteger(), MathUtil.toBigInteger(value, 4096)); + } + + private static String repeat(char c, int n) { + StringBuilder sb = new StringBuilder(n); + for (int i = 0; i < n; i++) { + sb.append(c); + } + return sb.toString(); + } + @Test public void testToBigIntegerNull() { assertThrows(NullPointerException.class, () -> MathUtil.toBigInteger(null));