Skip to content

Commit

Permalink
Follow-up to #1157: unify exception handling (#1159)
Browse files Browse the repository at this point in the history
  • Loading branch information
cowtowncoder authored Dec 9, 2023
1 parent 45bb3b1 commit 8aa164a
Show file tree
Hide file tree
Showing 3 changed files with 63 additions and 34 deletions.
93 changes: 61 additions & 32 deletions src/main/java/com/fasterxml/jackson/core/io/BigDecimalParser.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,6 @@
import ch.randelshofer.fastdoubleparser.JavaBigDecimalParser;

import java.math.BigDecimal;
import java.util.Arrays;

// Based on a great idea of Eric Obermühlner to use a tree of smaller BigDecimals for parsing
// really big numbers with O(n^1.5) complexity instead of O(n^2) when using the constructor
// for a decimal representation from JDK 8/11:
//
// https://github.com/eobermuhlner/big-math/commit/7a5419aac8b2adba2aa700ccf00197f97b2ad89f

/**
* Internal Jackson Helper class used to implement more optimized parsing of {@link BigDecimal} for REALLY
Expand Down Expand Up @@ -37,6 +30,10 @@ private BigDecimalParser() {}

/**
* Internal Jackson method. Please do not use.
*<p>
* Note: Caller MUST pre-validate that given String represents a valid representation
* of {@link BigDecimal} value: parsers in {@code jackson-core} do that; other
* code must do the same.
*
* @param valueStr
* @return BigDecimal value
Expand All @@ -48,6 +45,10 @@ public static BigDecimal parse(String valueStr) {

/**
* Internal Jackson method. Please do not use.
*<p>
* Note: Caller MUST pre-validate that given String represents a valid representation
* of {@link BigDecimal} value: parsers in {@code jackson-core} do that; other
* code must do the same.
*
* @return BigDecimal value
* @throws NumberFormatException
Expand All @@ -62,25 +63,16 @@ public static BigDecimal parse(final char[] chars, final int off, final int len)
// 20-Aug-2022, tatu: Although "new BigDecimal(...)" only throws NumberFormatException
// operations by "parseBigDecimal()" can throw "ArithmeticException", so handle both:
} catch (ArithmeticException | NumberFormatException e) {
String desc = e.getMessage();
// 05-Feb-2021, tatu: Alas, JDK mostly has null message so:
if (desc == null) {
desc = "Not a valid number representation";
}
String stringToReport;
if (len <= MAX_CHARS_TO_REPORT) {
stringToReport = new String(chars, off, len);
} else {
stringToReport = new String(Arrays.copyOfRange(chars, off, MAX_CHARS_TO_REPORT))
+ "(truncated, full length is " + chars.length + " chars)";
}
throw new NumberFormatException("Value \"" + stringToReport
+ "\" can not be represented as `java.math.BigDecimal`, reason: " + desc);
throw _parseFailure(e, new String(chars, off, len));
}
}

/**
* Internal Jackson method. Please do not use.
*<p>
* Note: Caller MUST pre-validate that given String represents a valid representation
* of {@link BigDecimal} value: parsers in {@code jackson-core} do that; other
* code must do the same.
*
* @param chars
* @return BigDecimal value
Expand All @@ -90,25 +82,62 @@ public static BigDecimal parse(char[] chars) {
return parse(chars, 0, chars.length);
}

/**
* Internal Jackson method. Please do not use.
*<p>
* Note: Caller MUST pre-validate that given String represents a valid representation
* of {@link BigDecimal} value: parsers in {@code jackson-core} do that; other
* code must do the same.
*
* @param valueStr
* @return BigDecimal value
* @throws NumberFormatException
*/
public static BigDecimal parseWithFastParser(final String valueStr) {
try {
return JavaBigDecimalParser.parseBigDecimal(valueStr);
} catch (NumberFormatException nfe) {
final String reportNum = valueStr.length() <= MAX_CHARS_TO_REPORT ?
valueStr : valueStr.substring(0, MAX_CHARS_TO_REPORT) + " [truncated]";
throw new NumberFormatException("Value \"" + reportNum
+ "\" can not be represented as `java.math.BigDecimal`, reason: " + nfe.getMessage());
} catch (ArithmeticException | NumberFormatException e) {
throw _parseFailure(e, valueStr);
}
}

/**
* Internal Jackson method. Please do not use.
*<p>
* Note: Caller MUST pre-validate that given String represents a valid representation
* of {@link BigDecimal} value: parsers in {@code jackson-core} do that; other
* code must do the same.
*
* @return BigDecimal value
* @throws NumberFormatException
*/
public static BigDecimal parseWithFastParser(final char[] ch, final int off, final int len) {
try {
return JavaBigDecimalParser.parseBigDecimal(ch, off, len);
} catch (NumberFormatException nfe) {
final String reportNum = len <= MAX_CHARS_TO_REPORT ?
new String(ch, off, len) : new String(ch, off, MAX_CHARS_TO_REPORT) + " [truncated]";
throw new NumberFormatException("Value \"" + reportNum
+ "\" can not be represented as `java.math.BigDecimal`, reason: " + nfe.getMessage());
} catch (ArithmeticException | NumberFormatException e) {
throw _parseFailure(e, new String(ch, off, len));
}
}

private static NumberFormatException _parseFailure(Exception e, String fullValue) {
String desc = e.getMessage();
// 05-Feb-2021, tatu: Alas, JDK mostly has null message so:
if (desc == null) {
desc = "Not a valid number representation";
}
String valueToReport = _getValueDesc(fullValue);
return new NumberFormatException("Value " + valueToReport
+ " can not be deserialized as `java.math.BigDecimal`, reason: " + desc);
}

private static String _getValueDesc(String fullValue) {
final int len = fullValue.length();
if (len <= MAX_CHARS_TO_REPORT) {
return String.format("\"%s\"", fullValue);
}
return String.format("\"%s\" (truncated to %d chars (from %d))",
fullValue.substring(0, MAX_CHARS_TO_REPORT),
MAX_CHARS_TO_REPORT, len);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ private void _testAllowNaN(JsonFactory f, String doc, int readBytes) throws Exce
/*BigDecimal dec =*/ p.getDecimalValue();
fail("Should fail when trying to access NaN as BigDecimal");
} catch (NumberFormatException e) {
verifyException(e, "can not be represented as `java.math.BigDecimal`");
verifyException(e, "can not be deserialized as `java.math.BigDecimal`");
}

assertToken(JsonToken.END_ARRAY, p.nextToken());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@ private void _testAllowNaN(int mode) throws Exception
/*BigDecimal dec =*/ p.getDecimalValue();
fail("Should fail when trying to access NaN as BigDecimal");
} catch (NumberFormatException e) {
verifyException(e, "can not be represented as `java.math.BigDecimal`");
verifyException(e, "can not be deserialized as `java.math.BigDecimal`");
}

assertToken(JsonToken.END_ARRAY, p.nextToken());
Expand Down

0 comments on commit 8aa164a

Please sign in to comment.