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
86 changes: 47 additions & 39 deletions src/main/java/org/apache/xmlbeans/impl/util/XsTypeConverter.java
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ public static float lexFloat(CharSequence cs)
*/
public static float lexFloat(CharSequence cs, boolean strict)
throws NumberFormatException {
rejectInvalidNumber(cs);
final String v = cs.toString();
switch (v) {
case POS_INF_LEX:
Expand Down Expand Up @@ -162,6 +163,7 @@ public static double lexDouble(CharSequence cs)
*/
public static double lexDouble(CharSequence cs, boolean strict)
throws NumberFormatException {
rejectInvalidNumber(cs);
final String v = cs.toString();
switch (v) {
case POS_INF_LEX:
Expand Down Expand Up @@ -212,6 +214,7 @@ public static String printDouble(double value) {
// ======================== decimal ========================
public static BigDecimal lexDecimal(CharSequence cs)
throws NumberFormatException {
rejectInvalidNumber(cs);
final String v = cs.toString();

//TODO: review this
Expand Down Expand Up @@ -300,6 +303,7 @@ public static String printInteger(BigInteger value) {
// ======================== long ========================
public static long lexLong(CharSequence cs)
throws NumberFormatException {
rejectInvalidNumber(cs);
rejectSignAfterPlus(cs);
final String v = cs.toString();
return Long.parseLong(trimInitialPlus(v));
Expand Down Expand Up @@ -358,7 +362,7 @@ public static String printShort(short value) {
// ======================== int ========================
public static int lexInt(CharSequence cs)
throws NumberFormatException {
return parseInt(cs);
return parseIntXsdNumber(cs, Integer.MIN_VALUE, Integer.MAX_VALUE);
}

public static int lexInt(CharSequence cs, Collection<XmlError> errors) {
Expand Down Expand Up @@ -642,10 +646,6 @@ private static String trimTrailingZeros(String xsd_decimal) {
return xsd_decimal;
}

private static int parseInt(CharSequence cs) {
return parseIntXsdNumber(cs, Integer.MIN_VALUE, Integer.MAX_VALUE);
}

private static short parseShort(CharSequence cs) {
return (short) parseIntXsdNumber(cs, Short.MIN_VALUE, Short.MAX_VALUE);
}
Expand All @@ -655,57 +655,59 @@ private static byte parseByte(CharSequence cs) {
}

private static int parseIntXsdNumber(CharSequence ch, int min_value, int max_value) {
// int parser on a CharSequence
int length = ch.length();
if (length < 1) {
throw new NumberFormatException("For input string: \"" + ch + "\"");
}

int sign = 1;
int result = 0;
int start = 0;
int limit;
int limit2;

char c = ch.charAt(0);
if (c == '-') {
start++;
limit = (min_value / 10);
limit2 = -(min_value % 10);
} else if (c == '+') {
start++;
sign = -1;
limit = -(max_value / 10);
limit2 = (max_value % 10);
} else {
sign = -1;
limit = -(max_value / 10);
limit2 = (max_value % 10);
rejectInvalidNumber(ch);

final int len = ch.length();
int i = 0;
boolean negative = false;

// Sign
char first = ch.charAt(0);
if (first == '-') {
negative = true;
i = 1;
} else if (first == '+') {
i = 1;
}

for (int i = 0; i < length - start; i++) {
c = ch.charAt(i + start);
int v = (c >= '0' && c <= '9') ? c - '0' : -1;
if (i == len) {
throw new NumberFormatException("For input string: \"" + ch + "\""); // just "+" or "-"
}

if (v < 0) {
long result = 0; // Use long to avoid intermediate overflow

while (i < len) {
char c = ch.charAt(i++);
int digit = c - '0';
if (digit < 0 || digit > 9) {
throw new NumberFormatException("For input string: \"" + ch + "\"");
}

if (result < limit || (result == limit && v > limit2)) {
// Early overflow detection
if (result > (Long.MAX_VALUE / 10)) {
throw new NumberFormatException("For input string: \"" + ch + "\"");
}
result = result * 10 + digit;
}

if (negative) {
result = -result;
}

result = Math.toIntExact(result * 10L - v);
if (result < min_value || result > max_value) {
throw new NumberFormatException(String.format(
"For input string: \"%s\"; min-allowed=%d, max-allowed=%d",
ch, min_value, max_value));
}

return Math.multiplyExact(sign, result);
return Math.toIntExact(result);
}

// ======================== anyURI ========================

/**
* Checks the regular expression of URI, defined by RFC2369 http://www.ietf.org/rfc/rfc2396.txt Appendix B.
* Note: The whitespace normalization rule collapse must be applied priot to calling this method.
* Note: The whitespace normalization rule collapse must be applied prior to calling this method.
*
* @param lexical_value the lexical value
* @return same input value if input value is in the lexical space
Expand Down Expand Up @@ -744,4 +746,10 @@ public static CharSequence lexAnyURI(CharSequence lexical_value) {

return lexical_value;
}

private static void rejectInvalidNumber(CharSequence cs) {
if (cs == null || cs.length() == 0) {
throw new NumberFormatException("For input string: \"" + cs + "\"");
}
}
}
40 changes: 39 additions & 1 deletion src/test/java/misc/checkin/XsTypeConverterTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,31 @@ public class XsTypeConverterTest {
@Test
void lexIntAcceptsAscii() {
assertEquals(123, XsTypeConverter.lexInt("123"));
assertEquals(123, XsTypeConverter.lexInt("00123"));
assertEquals(-123, XsTypeConverter.lexInt("-123"));
assertEquals(123, XsTypeConverter.lexInt("+123"));
assertEquals(Integer.MAX_VALUE, XsTypeConverter.lexInt(Integer.toString(Integer.MAX_VALUE)));
assertEquals(Integer.MIN_VALUE, XsTypeConverter.lexInt(Integer.toString(Integer.MIN_VALUE)));
}

@Test
void lexLongAcceptsAscii() {
assertEquals(123L, XsTypeConverter.lexLong("123"));
assertEquals(123L, XsTypeConverter.lexLong("00123"));
assertEquals(-123L, XsTypeConverter.lexLong("-123"));
assertEquals(123L, XsTypeConverter.lexLong("+123"));
assertEquals(Long.MAX_VALUE, XsTypeConverter.lexLong(Long.toString(Long.MAX_VALUE)));
assertEquals(Long.MIN_VALUE, XsTypeConverter.lexLong(Long.toString(Long.MIN_VALUE)));
}

@Test
void lexShortAcceptsAscii() {
assertEquals(123, XsTypeConverter.lexShort("123"));
assertEquals(123, XsTypeConverter.lexShort("00123"));
assertEquals(-123, XsTypeConverter.lexShort("-123"));
assertEquals(123, XsTypeConverter.lexShort("+123"));
assertEquals(Short.MAX_VALUE, XsTypeConverter.lexShort(Short.toString(Short.MAX_VALUE)));
assertEquals(Short.MIN_VALUE, XsTypeConverter.lexShort(Short.toString(Short.MIN_VALUE)));
}

@Test
Expand All @@ -46,9 +69,14 @@ void lexIntRejectsNonAsciiDigits() {
assertThrows(NumberFormatException.class, () -> XsTypeConverter.lexInt(DEVANAGARI_123));
}

@Test
void lexIntRejectsEmptyOrNull() {
assertThrows(NumberFormatException.class, () -> XsTypeConverter.lexInt(null));
assertThrows(NumberFormatException.class, () -> XsTypeConverter.lexInt(""));
}

@Test
void lexShortRejectsNonAsciiDigits() {
assertEquals(123, XsTypeConverter.lexShort("123"));
assertThrows(NumberFormatException.class, () -> XsTypeConverter.lexShort(FULLWIDTH_123));
assertThrows(NumberFormatException.class, () -> XsTypeConverter.lexShort(ARABIC_123));
}
Expand Down Expand Up @@ -105,6 +133,7 @@ void lexFloatStrictRejectsNonXsdLexicalForms() {
@Test
void lexFloatStrictAcceptsValidValues() {
assertEquals(1.0f, XsTypeConverter.lexFloat("1.0", true));
assertEquals(1.0f, XsTypeConverter.lexFloat("01.0", true));
assertEquals(1500.0f, XsTypeConverter.lexFloat("1.5e3", true));
assertEquals(Float.POSITIVE_INFINITY, XsTypeConverter.lexFloat("INF", true));
assertEquals(Float.NEGATIVE_INFINITY, XsTypeConverter.lexFloat("-INF", true));
Expand All @@ -114,6 +143,7 @@ void lexFloatStrictAcceptsValidValues() {
@Test
void lexDoubleAcceptsValidValues() {
assertEquals(1.0, XsTypeConverter.lexDouble("1.0"));
assertEquals(1.0, XsTypeConverter.lexDouble("01.0"));
assertEquals(Double.POSITIVE_INFINITY, XsTypeConverter.lexDouble("INF"));
assertEquals(Double.NEGATIVE_INFINITY, XsTypeConverter.lexDouble("-INF"));
assertEquals(1500.0, XsTypeConverter.lexDouble("1.5e3"));
Expand All @@ -140,6 +170,7 @@ void lexDoubleStrictRejectsNonXsdLexicalForms() {
@Test
void lexDoubleStrictAcceptsValidValues() {
assertEquals(1.0, XsTypeConverter.lexDouble("1.0", true));
assertEquals(1.0, XsTypeConverter.lexDouble("01.0", true));
assertEquals(1500.0, XsTypeConverter.lexDouble("1.5e3", true));
assertEquals(Double.POSITIVE_INFINITY, XsTypeConverter.lexDouble("INF", true));
assertEquals(Double.NEGATIVE_INFINITY, XsTypeConverter.lexDouble("-INF", true));
Expand Down Expand Up @@ -176,6 +207,12 @@ void lexLongRejectsDoubleSign() {
assertThrows(NumberFormatException.class, () -> XsTypeConverter.lexLong("+-5"));
}

@Test
void lexLongRejectsEmptyOrNull() {
assertThrows(NumberFormatException.class, () -> XsTypeConverter.lexLong(null));
assertThrows(NumberFormatException.class, () -> XsTypeConverter.lexLong(""));
}

@Test
void lexIntegerRejectsDoubleSign() {
// "+-5" was already caught; "++5" leaked through as 5.
Expand All @@ -190,6 +227,7 @@ void lexDecimalRejectsEmptyString() {
// an empty value used to read charAt(-1) in trimTrailingZeros and
// throw StringIndexOutOfBoundsException instead of NumberFormatException.
assertThrows(NumberFormatException.class, () -> XsTypeConverter.lexDecimal(""));
assertThrows(NumberFormatException.class, () -> XsTypeConverter.lexDecimal(null));
}

@Test
Expand Down
Loading