diff --git a/src/main/java/com/fasterxml/jackson/core/io/BigDecimalParser.java b/src/main/java/com/fasterxml/jackson/core/io/BigDecimalParser.java index e512351d30..a90121ce01 100644 --- a/src/main/java/com/fasterxml/jackson/core/io/BigDecimalParser.java +++ b/src/main/java/com/fasterxml/jackson/core/io/BigDecimalParser.java @@ -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 @@ -37,6 +30,10 @@ private BigDecimalParser() {} /** * Internal Jackson method. Please do not use. + *

+ * 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 @@ -48,6 +45,10 @@ public static BigDecimal parse(String valueStr) { /** * Internal Jackson method. Please do not use. + *

+ * 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 @@ -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. + *

+ * 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 @@ -90,25 +82,62 @@ public static BigDecimal parse(char[] chars) { return parse(chars, 0, chars.length); } + /** + * Internal Jackson method. Please do not use. + *

+ * 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. + *

+ * 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); + } + } diff --git a/src/test/java/com/fasterxml/jackson/core/json/async/AsyncNaNHandlingTest.java b/src/test/java/com/fasterxml/jackson/core/json/async/AsyncNaNHandlingTest.java index 099744781d..bce551c11e 100644 --- a/src/test/java/com/fasterxml/jackson/core/json/async/AsyncNaNHandlingTest.java +++ b/src/test/java/com/fasterxml/jackson/core/json/async/AsyncNaNHandlingTest.java @@ -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()); diff --git a/src/test/java/com/fasterxml/jackson/core/read/NonStandardParserFeaturesTest.java b/src/test/java/com/fasterxml/jackson/core/read/NonStandardParserFeaturesTest.java index a8a79ba6ec..3a60543c5c 100644 --- a/src/test/java/com/fasterxml/jackson/core/read/NonStandardParserFeaturesTest.java +++ b/src/test/java/com/fasterxml/jackson/core/read/NonStandardParserFeaturesTest.java @@ -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());