Skip to content
Open
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
46 changes: 34 additions & 12 deletions src/main/java/org/apache/xmlbeans/impl/util/XsTypeConverter.java
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,38 @@ public final class XsTypeConverter {
private static final String[] URI_CHARS_TO_BE_REPLACED = {" ", "{", "}", "|", "\\", "^", "[", "]", "`"};
private static final String[] URI_CHARS_REPLACED_WITH = {"%20", "%7b", "%7d", "%7c", "%5c", "%5e", "%5b", "%5d", "%60"};

// Float.parseFloat / Double.parseDouble accept lexical forms that are not
// in the XSD float/double value space: hexadecimal floats (0x1p4), the Java
// "Infinity" token, and a trailing type suffix (f/F/d/D). XSD only allows a
// decimal number with an optional exponent, or the special values INF, -INF
// and NaN. Reject the Java-only forms so they surface as invalid rather than
// being silently parsed.
private static void checkFloatingPointLexical(CharSequence cs) {
final int len = cs.length();
for (int i = 0; i < len; i++) {
switch (cs.charAt(i)) {
case 'x':
case 'X':
case 'p':
case 'P':
case 'i':
case 't':
case 'y':
throw new NumberFormatException("invalid char '" + cs.charAt(i) + "' in floating point value");
default:
break;
}
}
if (len > 0) {
final char last = cs.charAt(len - 1);
// a trailing 'F' is only valid as the last char of "INF"
if (last == 'd' || last == 'D' ||
((last == 'f' || last == 'F') && (len < 2 || cs.charAt(len - 2) != 'N'))) {
throw new NumberFormatException("invalid trailing char '" + last + "' in floating point value");
}
}
}

// ======================== float ========================
public static float lexFloat(CharSequence cs)
throws NumberFormatException {
Expand All @@ -48,12 +80,7 @@ public static float lexFloat(CharSequence cs)
//current jdk impl of parseFloat calls trim() on the string.
//Any other space is illegal anyway, whether there are one or more spaces.
//so no need to do a collapse pass through the string.
if (cs.length() > 1) {
char ch = cs.charAt(cs.length() - 1);
if ((ch == 'f' || ch == 'F') && cs.charAt(cs.length() - 2) != 'N') {
throw new NumberFormatException("Invalid char '" + ch + "' in float.");
}
}
checkFloatingPointLexical(cs);
return Float.parseFloat(v);
} catch (NumberFormatException e) {
if (v.equals(POS_INF_LEX)) {
Expand Down Expand Up @@ -103,12 +130,7 @@ public static double lexDouble(CharSequence cs)
//current jdk impl of parseDouble calls trim() on the string.
//Any other space is illegal anyway, whether there are one or more spaces.
//so no need to do a collapse pass through the string.
if (cs.length() > 0) {
char ch = cs.charAt(cs.length() - 1);
if (ch == 'd' || ch == 'D') {
throw new NumberFormatException("Invalid char '" + ch + "' in double.");
}
}
checkFloatingPointLexical(cs);
return Double.parseDouble(v);
} catch (NumberFormatException e) {
if (v.equals(POS_INF_LEX)) {
Expand Down
31 changes: 31 additions & 0 deletions src/test/java/misc/checkin/XsTypeConverterTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,37 @@ void lexFloatAcceptsValidValues() {
assertEquals(1.0f, XsTypeConverter.lexFloat("1.0"));
assertEquals(Float.POSITIVE_INFINITY, XsTypeConverter.lexFloat("INF"));
assertEquals(Float.NEGATIVE_INFINITY, XsTypeConverter.lexFloat("-INF"));
assertEquals(1500.0f, XsTypeConverter.lexFloat("1.5e3"));
}

@Test
void lexFloatRejectsNonXsdLexicalForms() {
// hex floats, the java "Infinity" spelling and the double suffix are
// accepted by Float.parseFloat but are outside the xsd:float lexical space
assertThrows(NumberFormatException.class, () -> XsTypeConverter.lexFloat("0x1p4"));
assertThrows(NumberFormatException.class, () -> XsTypeConverter.lexFloat("Infinity"));
assertThrows(NumberFormatException.class, () -> XsTypeConverter.lexFloat("-Infinity"));
assertThrows(NumberFormatException.class, () -> XsTypeConverter.lexFloat("1.0d"));
assertThrows(NumberFormatException.class, () -> XsTypeConverter.lexFloat("1D"));
}

@Test
void lexDoubleAcceptsValidValues() {
assertEquals(1.0, XsTypeConverter.lexDouble("1.0"));
assertEquals(Double.POSITIVE_INFINITY, XsTypeConverter.lexDouble("INF"));
assertEquals(Double.NEGATIVE_INFINITY, XsTypeConverter.lexDouble("-INF"));
assertEquals(1500.0, XsTypeConverter.lexDouble("1.5e3"));
}

@Test
void lexDoubleRejectsNonXsdLexicalForms() {
// hex floats, the java "Infinity" spelling and the float suffix are
// accepted by Double.parseDouble but are outside the xsd:double lexical space
assertThrows(NumberFormatException.class, () -> XsTypeConverter.lexDouble("0x1p4"));
assertThrows(NumberFormatException.class, () -> XsTypeConverter.lexDouble("Infinity"));
assertThrows(NumberFormatException.class, () -> XsTypeConverter.lexDouble("-Infinity"));
assertThrows(NumberFormatException.class, () -> XsTypeConverter.lexDouble("1.0f"));
assertThrows(NumberFormatException.class, () -> XsTypeConverter.lexDouble("1F"));
}

@Test
Expand Down