Skip to content
Closed
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
37 changes: 36 additions & 1 deletion src/main/java/org/apache/xmlbeans/XmlOptions.java
Original file line number Diff line number Diff line change
Expand Up @@ -159,11 +159,13 @@ public enum XmlOptionsKeys {
XPATH_USE_XMLBEANS,
ATTRIBUTE_VALIDATION_COMPAT_MODE,
LOAD_STRICT_FLOATING_POINT,
LOAD_ALLOW_DECIMAL_EXPONENT
LOAD_ALLOW_DECIMAL_EXPONENT,
MAX_NUMBER_CHARS
}


public static final int DEFAULT_ENTITY_EXPANSION_LIMIT = 2048;
public static final int DEFAULT_MAX_NUMBER_CHARS = 1024;

private static final XmlOptions EMPTY_OPTIONS;

Expand Down Expand Up @@ -1601,6 +1603,39 @@ public void setCompilePartialMethod(Set<BeanMethod> list) {
}
}

/**
* @return the maximum number of characters allowed for a number
* @since 5.4.0
*/
public int getMaxNumberOfCharsForNumbers() {
Object value = get(XmlOptionsKeys.MAX_NUMBER_CHARS);
if (value instanceof Number) {
return ((Number) value).intValue();
}
return DEFAULT_MAX_NUMBER_CHARS;
}

/**
* Sets the maximum number of characters allowed for a number
* @param max the maximum number of characters
* @since 5.4.0
*/
public void setMaxNumberOfCharsForNumbers(int max) {
set(XmlOptionsKeys.MAX_NUMBER_CHARS, max);
}

/**
* Sets the maximum number of characters allowed for a number
* @param max the maximum number of characters, null means apply the default of 1024
* @since 5.4.0
*/
public void setMaxNumberOfCharsForNumbers(Integer max) {
if (max == null) {
remove(XmlOptionsKeys.MAX_NUMBER_CHARS);
} else {
set(XmlOptionsKeys.MAX_NUMBER_CHARS, max);
}
}

/**
* If passed null, returns an empty options object. Otherwise, returns its argument.
Expand Down
45 changes: 28 additions & 17 deletions src/main/java/org/apache/xmlbeans/impl/util/MathUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ Licensed to the Apache Software Foundation (ASF) under one or more
import java.math.BigDecimal;
import java.math.BigInteger;

import static org.apache.xmlbeans.XmlOptions.DEFAULT_MAX_NUMBER_CHARS;

/**
* Internal Use Only. Utility methods for dealing with conversions
*/
Expand Down Expand Up @@ -51,9 +53,6 @@ public static int safeDoubleToInt(double d) {
return (int) d;
}

// TODO try to make this configurable
private static final int MAX_NUMBER_LENGTH = 1024;

/**
* @param s string to parse
* @return valid BigDecimal
Expand All @@ -62,11 +61,23 @@ public static int safeDoubleToInt(double d) {
* @throws NullPointerException if string is null
*/
public static BigDecimal parseAsBigDecimal(String s) {
return parseAsBigDecimal(s, DEFAULT_MAX_NUMBER_CHARS);
}

/**
* @param s string to parse
* @param maxNumberOfChars maximum number of characters allowed in the string
* @return valid BigDecimal
* @throws NumberFormatException if parse fails
* @throws IllegalArgumentException if string is too long
* @throws NullPointerException if string is null
*/
public static BigDecimal parseAsBigDecimal(String s, int maxNumberOfChars) {
if (s == null) {
throw new NullPointerException("Cannot parse null as BigDecimal");
}
if (s.length() > MAX_NUMBER_LENGTH) {
throw new IllegalArgumentException("Number has more than " + MAX_NUMBER_LENGTH + " characters");
if (s.length() > maxNumberOfChars) {
throw new IllegalArgumentException("Number has more than " + maxNumberOfChars + " characters");
}
return new BigDecimal(s);
}
Expand All @@ -82,8 +93,8 @@ public static BigInteger parseAsBigInteger(String s) {
if (s == null) {
throw new NullPointerException("Cannot parse null as BigInteger");
}
if (s.length() > MAX_NUMBER_LENGTH) {
throw new IllegalArgumentException("Number has more than " + MAX_NUMBER_LENGTH + " characters");
if (s.length() > DEFAULT_MAX_NUMBER_CHARS) {
throw new IllegalArgumentException("Number has more than " + DEFAULT_MAX_NUMBER_CHARS + " characters");
}
return new BigInteger(s);
}
Expand All @@ -99,8 +110,8 @@ public static Float parseAsFloat(String s) {
if (s == null) {
throw new NullPointerException("Cannot parse null as Float");
}
if (s.length() > MAX_NUMBER_LENGTH) {
throw new IllegalArgumentException("Number has more than " + MAX_NUMBER_LENGTH + " characters");
if (s.length() > DEFAULT_MAX_NUMBER_CHARS) {
throw new IllegalArgumentException("Number has more than " + DEFAULT_MAX_NUMBER_CHARS + " characters");
}
return Float.parseFloat(s);
}
Expand All @@ -116,8 +127,8 @@ public static Double parseAsDouble(String s) {
if (s == null) {
throw new NullPointerException("Cannot parse null as Double");
}
if (s.length() > MAX_NUMBER_LENGTH) {
throw new IllegalArgumentException("Number has more than " + MAX_NUMBER_LENGTH + " characters");
if (s.length() > DEFAULT_MAX_NUMBER_CHARS) {
throw new IllegalArgumentException("Number has more than " + DEFAULT_MAX_NUMBER_CHARS + " characters");
}
return Double.parseDouble(s);
}
Expand All @@ -133,8 +144,8 @@ public static Long parseAsLong(String s) {
if (s == null) {
throw new NullPointerException("Cannot parse null as Long");
}
if (s.length() > MAX_NUMBER_LENGTH) {
throw new IllegalArgumentException("Number has more than " + MAX_NUMBER_LENGTH + " characters");
if (s.length() > DEFAULT_MAX_NUMBER_CHARS) {
throw new IllegalArgumentException("Number has more than " + DEFAULT_MAX_NUMBER_CHARS + " characters");
}
return Long.parseLong(s);
}
Expand All @@ -150,8 +161,8 @@ public static Integer parseAsInteger(String s) {
if (s == null) {
throw new NullPointerException("Cannot parse null as Integer");
}
if (s.length() > MAX_NUMBER_LENGTH) {
throw new IllegalArgumentException("Number has more than " + MAX_NUMBER_LENGTH + " characters");
if (s.length() > DEFAULT_MAX_NUMBER_CHARS) {
throw new IllegalArgumentException("Number has more than " + DEFAULT_MAX_NUMBER_CHARS + " characters");
}
return Integer.parseInt(s);
}
Expand All @@ -165,10 +176,10 @@ public static Integer parseAsInteger(String s) {
public static BigInteger toBigInteger(BigDecimal value) {
BigDecimal normalized = value.stripTrailingZeros();
int integerDigits = normalized.precision() - normalized.scale();
if (integerDigits > MAX_NUMBER_LENGTH || normalized.scale() < -MAX_NUMBER_LENGTH) {
if (integerDigits > DEFAULT_MAX_NUMBER_CHARS || normalized.scale() < -DEFAULT_MAX_NUMBER_CHARS) {
throw new IllegalArgumentException(
"BigDecimal magnitude too large to convert safely: approx "
+ integerDigits + " integer digits (limit " + MAX_NUMBER_LENGTH + ")");
+ integerDigits + " integer digits (limit " + DEFAULT_MAX_NUMBER_CHARS + ")");
}
return normalized.toBigInteger();
}
Expand Down
14 changes: 8 additions & 6 deletions src/main/java/org/apache/xmlbeans/impl/validator/Validator.java
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,10 @@ public final class Validator
public Validator(
SchemaType type, SchemaField field, SchemaTypeLoader globalLoader,
XmlOptions options, Collection<XmlError> defaultErrorListener) {
options = XmlOptions.maskNull(options);
_errorListener = options.getErrorListener();
_treatLaxAsSkip = options.isValidateTreatLaxAsSkip();
_strict = options.isValidateStrict();
_options = XmlOptions.maskNull(options);
_errorListener = _options.getErrorListener();
_treatLaxAsSkip = _options.isValidateTreatLaxAsSkip();
_strict = _options.isValidateStrict();

if (_errorListener == null) {
_errorListener = defaultErrorListener;
Expand Down Expand Up @@ -1099,7 +1099,8 @@ private void validateAtomicType(
break;
}
case SchemaType.BTC_DECIMAL: {
JavaDecimalHolderEx.validateLexical(value, type, _vc);
JavaDecimalHolderEx.validateLexical(value, type, _vc,
false, _options.getMaxNumberOfCharsForNumbers());

// An additional rule states that if the type is xs:integer or derived from it,
// then the decimal dot is not allowed.
Expand All @@ -1109,7 +1110,7 @@ private void validateAtomicType(
}

if (errorState == _errorState) {
_decimalValue = MathUtil.parseAsBigDecimal(value);
_decimalValue = MathUtil.parseAsBigDecimal(value, _options.getMaxNumberOfCharsForNumbers());
JavaDecimalHolderEx.validateValue(_decimalValue, type, _vc);
}

Expand Down Expand Up @@ -1498,6 +1499,7 @@ private void addToList(SchemaType type) {
// Members of the validator class
//

private final XmlOptions _options;
private boolean _invalid;
private final SchemaType _rootType;
private final SchemaField _rootField;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.ValidationContext;
import org.apache.xmlbeans.impl.schema.BuiltinSchemaTypeSystem;
import org.apache.xmlbeans.impl.util.MathUtil;
Expand Down Expand Up @@ -67,11 +68,16 @@ public static void validateLexical(String v, ValidationContext context) {
}

public static void validateLexical(String v, ValidationContext context, boolean allowExponent) {
validateLexical(v, context, allowExponent, XmlOptions.DEFAULT_MAX_NUMBER_CHARS);
}

public static void validateLexical(String v, ValidationContext context, boolean allowExponent,
int maxNumberOfChars) {
if (allowExponent) {
// long-standing lenient behaviour: accept whatever BigDecimal accepts,
// which includes scientific/exponent notation such as "1E5".
try {
MathUtil.parseAsBigDecimal(v);
MathUtil.parseAsBigDecimal(v, maxNumberOfChars);
} catch (Exception e) {
context.invalid(XmlErrorCodes.DECIMAL, new Object[]{v});
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -69,7 +70,12 @@ public static void validateLexical(String v, SchemaType sType, ValidationContext
}

public static void validateLexical(String v, SchemaType sType, ValidationContext context, boolean allowExponent) {
JavaDecimalHolder.validateLexical(v, context, allowExponent);
validateLexical(v, sType, context, allowExponent, XmlOptions.DEFAULT_MAX_NUMBER_CHARS);
}

public static void validateLexical(String v, SchemaType sType, ValidationContext context,
boolean allowExponent, int maxNumberOfChars) {
JavaDecimalHolder.validateLexical(v, context, allowExponent, maxNumberOfChars);

// check pattern
if (sType.hasPatternFacet()) {
Expand Down
15 changes: 15 additions & 0 deletions src/test/java/misc/checkin/XmlOptionsTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import org.apache.xmlbeans.XmlOptions;
import org.junit.jupiter.api.Test;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;

Expand Down Expand Up @@ -60,4 +61,18 @@ void testSaveNoAttributeWhitespaceEscapeFlag() {
xmlOptions.setSaveNoAttributeWhitespaceEscape(false);
assertFalse(xmlOptions.isSaveNoAttributeWhitespaceEscape());
}

@Test
void testMaxNumberOfCharsForNumbers() {
XmlOptions xmlOptions = new XmlOptions();
assertEquals(XmlOptions.DEFAULT_MAX_NUMBER_CHARS,
xmlOptions.getMaxNumberOfCharsForNumbers());
xmlOptions.setMaxNumberOfCharsForNumbers(2);
assertEquals(2, xmlOptions.getMaxNumberOfCharsForNumbers());
xmlOptions.setMaxNumberOfCharsForNumbers(Integer.valueOf(3));
assertEquals(3, xmlOptions.getMaxNumberOfCharsForNumbers());
xmlOptions.setMaxNumberOfCharsForNumbers(null);
assertEquals(XmlOptions.DEFAULT_MAX_NUMBER_CHARS, xmlOptions.getMaxNumberOfCharsForNumbers());
}

}
Loading