diff --git a/avro/src/main/java/tools/jackson/dataformat/avro/deser/AvroParserImpl.java b/avro/src/main/java/tools/jackson/dataformat/avro/deser/AvroParserImpl.java index e93c31580..62441d1ed 100644 --- a/avro/src/main/java/tools/jackson/dataformat/avro/deser/AvroParserImpl.java +++ b/avro/src/main/java/tools/jackson/dataformat/avro/deser/AvroParserImpl.java @@ -210,8 +210,9 @@ public final boolean isNaN() { public final Number getNumberValue() throws JacksonException { if (_numTypesValid == NR_UNKNOWN) { - _checkNumericValue(NR_UNKNOWN); // will also check event type + _checkNumericValue(); // will also check event type } + // Separate types for int types if (_currToken == JsonToken.VALUE_NUMBER_INT) { if ((_numTypesValid & NR_INT) != 0) { @@ -249,9 +250,6 @@ public final Number getNumberValueExact() throws JacksonException { @Override public final NumberType getNumberType() throws JacksonException { - if (_numTypesValid == NR_UNKNOWN) { - _checkNumericValue(NR_UNKNOWN); // will also check event type - } if (_currToken == JsonToken.VALUE_NUMBER_INT) { if ((_numTypesValid & NR_INT) != 0) { return NumberType.INT; @@ -265,13 +263,17 @@ public final NumberType getNumberType() throws JacksonException // And then floating point types. Here optimal type should be big decimal, // to avoid losing any data? However... using BD is slow, so let's allow returning // double as type if no explicit call has been made to access data as BD? - if ((_numTypesValid & NR_BIGDECIMAL) != 0) { - return NumberType.BIG_DECIMAL; - } - if ((_numTypesValid & NR_DOUBLE) != 0) { - return NumberType.DOUBLE; + if (_currToken == JsonToken.VALUE_NUMBER_FLOAT) { + if ((_numTypesValid & NR_BIGDECIMAL) != 0) { + return NumberType.BIG_DECIMAL; + } + if ((_numTypesValid & NR_DOUBLE) != 0) { + return NumberType.DOUBLE; + } + return NumberType.FLOAT; } - return NumberType.FLOAT; + + return null; } @Override // since 2.17 @@ -295,7 +297,7 @@ public final float getFloatValue() throws JacksonException { if ((_numTypesValid & NR_FLOAT) == 0) { if (_numTypesValid == NR_UNKNOWN) { - _checkNumericValue(NR_FLOAT); + _checkNumericValue(); } if ((_numTypesValid & NR_FLOAT) == 0) { convertNumberToFloat(); @@ -316,13 +318,12 @@ public final float getFloatValue() throws JacksonException /********************************************************************** */ - protected final void _checkNumericValue(int expType) throws JacksonException + protected final void _checkNumericValue() throws JacksonException { // Int or float? - if (_currToken == JsonToken.VALUE_NUMBER_INT || _currToken == JsonToken.VALUE_NUMBER_FLOAT) { - return; + if (_currToken != JsonToken.VALUE_NUMBER_INT && _currToken != JsonToken.VALUE_NUMBER_FLOAT) { + throw _constructReadException("Current token (%s) not numeric, cannot use numeric value accessors", _currToken); } - _reportError("Current token ("+currentToken()+") not numeric, can not use numeric value accessors"); } @Override @@ -333,7 +334,7 @@ protected final void convertNumberToInt() throws JacksonException // Let's verify it's lossless conversion by simple roundtrip int result = (int) _numberLong; if (((long) result) != _numberLong) { - _reportError("Numeric value ("+getString()+") out of range of int"); + _reportError("Numeric value ("+getString()+") out of range of `int`"); } _numberInt = result; } else if ((_numTypesValid & NR_BIGINT) != 0) { diff --git a/cbor/src/main/java/tools/jackson/dataformat/cbor/CBORParser.java b/cbor/src/main/java/tools/jackson/dataformat/cbor/CBORParser.java index 5bdb0ebfc..e7c357218 100644 --- a/cbor/src/main/java/tools/jackson/dataformat/cbor/CBORParser.java +++ b/cbor/src/main/java/tools/jackson/dataformat/cbor/CBORParser.java @@ -2011,7 +2011,7 @@ public boolean isNaN() { public Number getNumberValue() throws JacksonException { if (_numTypesValid == NR_UNKNOWN) { - _checkNumericValue(NR_UNKNOWN); // will also check event type + _checkNumericValue(); // will also check event type } // Separate types for int types if (_currToken == JsonToken.VALUE_NUMBER_INT) { @@ -2050,9 +2050,6 @@ public final Number getNumberValueExact() throws JacksonException { @Override public NumberType getNumberType() throws JacksonException { - if (_numTypesValid == NR_UNKNOWN) { - _checkNumericValue(NR_UNKNOWN); // will also check event type - } if (_currToken == JsonToken.VALUE_NUMBER_INT) { if ((_numTypesValid & NR_INT) != 0) { return NumberType.INT; @@ -2062,23 +2059,25 @@ public NumberType getNumberType() throws JacksonException } return NumberType.BIG_INTEGER; } - /* And then floating point types. Here optimal type * needs to be big decimal, to avoid losing any data? * However... using BD is slow, so let's allow returning * double as type if no explicit call has been made to access * data as BD? */ - if ((_numTypesValid & NR_BIGDECIMAL) != 0) { - return NumberType.BIG_DECIMAL; - } - if ((_numTypesValid & NR_DOUBLE) != 0) { - return NumberType.DOUBLE; + if (_currToken == JsonToken.VALUE_NUMBER_FLOAT) { + if ((_numTypesValid & NR_BIGDECIMAL) != 0) { + return NumberType.BIG_DECIMAL; + } + if ((_numTypesValid & NR_DOUBLE) != 0) { + return NumberType.DOUBLE; + } + return NumberType.FLOAT; } - return NumberType.FLOAT; + return null; } - @Override // since 2.17 + @Override public NumberTypeFP getNumberTypeFP() throws JacksonException { if (_currToken == JsonToken.VALUE_NUMBER_FLOAT) { @@ -2106,11 +2105,9 @@ public float getFloatValue() throws JacksonException { if ((_numTypesValid & NR_FLOAT) == 0) { if (_numTypesValid == NR_UNKNOWN) { - _checkNumericValue(NR_FLOAT); - } - if ((_numTypesValid & NR_FLOAT) == 0) { - convertNumberToFloat(); + _checkNumericValue(); } + convertNumberToFloat(); } // Bounds/range checks would be tricky here, so let's not bother even trying... /* @@ -2144,13 +2141,13 @@ protected int _parseIntValue() throws JacksonException { /********************************************************************** */ - protected void _checkNumericValue(int expType) throws JacksonException + protected void _checkNumericValue() throws JacksonException { // Int or float? - if (_currToken == JsonToken.VALUE_NUMBER_INT || _currToken == JsonToken.VALUE_NUMBER_FLOAT) { - return; + if (_currToken != JsonToken.VALUE_NUMBER_INT && _currToken != JsonToken.VALUE_NUMBER_FLOAT) { + throw _constructReadException("Current token (%s) not numeric, cannot use numeric value accessors", + _currToken); } - _reportError("Current token ("+currentToken()+") not numeric, can not use numeric value accessors"); } @Override // due to addition of Float as type diff --git a/cbor/src/test/java/tools/jackson/dataformat/cbor/parse/CBORNumberParsingGetType1433Test.java b/cbor/src/test/java/tools/jackson/dataformat/cbor/parse/CBORNumberParsingGetType1433Test.java index 9c2e9522b..a1b01328c 100644 --- a/cbor/src/test/java/tools/jackson/dataformat/cbor/parse/CBORNumberParsingGetType1433Test.java +++ b/cbor/src/test/java/tools/jackson/dataformat/cbor/parse/CBORNumberParsingGetType1433Test.java @@ -3,13 +3,11 @@ import org.junit.jupiter.api.Test; import tools.jackson.core.*; -import tools.jackson.core.exc.StreamReadException; import tools.jackson.dataformat.cbor.CBORFactory; import tools.jackson.dataformat.cbor.CBORTestBase; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.fail; public class CBORNumberParsingGetType1433Test extends CBORTestBase @@ -59,12 +57,8 @@ void getNumberType() throws Exception private void _verifyGetNumberTypeFail(JsonParser p, String token) throws Exception { - try { - p.getNumberType(); - fail("Should not pass"); - } catch (StreamReadException e) { - verifyException(e, "Current token ("+token+") not numeric, can not use numeric"); - } + // In 2.x got exception; in 3.x null + assertNull(p.getNumberType()); } private CBORFactory jsonFactory() { diff --git a/protobuf/src/main/java/tools/jackson/dataformat/protobuf/ProtobufParser.java b/protobuf/src/main/java/tools/jackson/dataformat/protobuf/ProtobufParser.java index 44fd79123..750382b33 100644 --- a/protobuf/src/main/java/tools/jackson/dataformat/protobuf/ProtobufParser.java +++ b/protobuf/src/main/java/tools/jackson/dataformat/protobuf/ProtobufParser.java @@ -1597,7 +1597,7 @@ public boolean isNaN() { public Number getNumberValue() throws JacksonException { if (_numTypesValid == NR_UNKNOWN) { - _checkNumericValue(NR_UNKNOWN); // will also check event type + _checkNumericValue(); // will also check event type } // Separate types for int types if (_currToken == JsonToken.VALUE_NUMBER_INT) { @@ -1636,9 +1636,6 @@ public final Number getNumberValueExact() throws JacksonException { @Override public NumberType getNumberType() throws JacksonException { - if (_numTypesValid == NR_UNKNOWN) { - _checkNumericValue(NR_UNKNOWN); // will also check event type - } if (_currToken == JsonToken.VALUE_NUMBER_INT) { if ((_numTypesValid & NR_LONG) != 0) { return NumberType.LONG; @@ -1655,13 +1652,17 @@ public NumberType getNumberType() throws JacksonException * double as type if no explicit call has been made to access * data as BD? */ - if ((_numTypesValid & NR_BIGDECIMAL) != 0) { - return NumberType.BIG_DECIMAL; - } - if ((_numTypesValid & NR_DOUBLE) != 0) { - return NumberType.DOUBLE; + if (_currToken == JsonToken.VALUE_NUMBER_FLOAT) { + if ((_numTypesValid & NR_BIGDECIMAL) != 0) { + return NumberType.BIG_DECIMAL; + } + if ((_numTypesValid & NR_DOUBLE) != 0) { + return NumberType.DOUBLE; + } + return NumberType.FLOAT; } - return NumberType.FLOAT; + + return null; } @Override // since 2.17 @@ -1686,11 +1687,9 @@ public int getIntValue() throws JacksonException { if ((_numTypesValid & NR_INT) == 0) { if (_numTypesValid == NR_UNKNOWN) { // not parsed at all - _checkNumericValue(NR_INT); // will also check event type - } - if ((_numTypesValid & NR_INT) == 0) { // wasn't an int natively? - convertNumberToInt(); // let's make it so, if possible + _checkNumericValue(); // will also check event type } + convertNumberToInt(); // let's make it so, if possible } return _numberInt; } @@ -1700,11 +1699,9 @@ public long getLongValue() throws JacksonException { if ((_numTypesValid & NR_LONG) == 0) { if (_numTypesValid == NR_UNKNOWN) { - _checkNumericValue(NR_LONG); - } - if ((_numTypesValid & NR_LONG) == 0) { - convertNumberToLong(); + _checkNumericValue(); } + convertNumberToLong(); } return _numberLong; } @@ -1714,11 +1711,9 @@ public BigInteger getBigIntegerValue() throws JacksonException { if ((_numTypesValid & NR_BIGINT) == 0) { if (_numTypesValid == NR_UNKNOWN) { - _checkNumericValue(NR_BIGINT); - } - if ((_numTypesValid & NR_BIGINT) == 0) { - convertNumberToBigInteger(); + _checkNumericValue(); } + convertNumberToBigInteger(); } return _numberBigInt; } @@ -1728,11 +1723,9 @@ public float getFloatValue() throws JacksonException { if ((_numTypesValid & NR_FLOAT) == 0) { if (_numTypesValid == NR_UNKNOWN) { - _checkNumericValue(NR_FLOAT); - } - if ((_numTypesValid & NR_FLOAT) == 0) { - convertNumberToFloat(); + _checkNumericValue(); } + convertNumberToFloat(); } // Bounds/range checks would be tricky here, so let's not bother even trying... /* @@ -1748,11 +1741,9 @@ public double getDoubleValue() throws JacksonException { if ((_numTypesValid & NR_DOUBLE) == 0) { if (_numTypesValid == NR_UNKNOWN) { - _checkNumericValue(NR_DOUBLE); - } - if ((_numTypesValid & NR_DOUBLE) == 0) { - convertNumberToDouble(); + _checkNumericValue(); } + convertNumberToDouble(); } return _numberDouble; } @@ -1762,11 +1753,9 @@ public BigDecimal getDecimalValue() throws JacksonException { if ((_numTypesValid & NR_BIGDECIMAL) == 0) { if (_numTypesValid == NR_UNKNOWN) { - _checkNumericValue(NR_BIGDECIMAL); - } - if ((_numTypesValid & NR_BIGDECIMAL) == 0) { - convertNumberToBigDecimal(); + _checkNumericValue(); } + convertNumberToBigDecimal(); } return _numberBigDecimal; } @@ -1777,13 +1766,12 @@ public BigDecimal getDecimalValue() throws JacksonException /********************************************************************** */ - protected void _checkNumericValue(int expType) throws JacksonException + protected void _checkNumericValue() throws JacksonException { // Int or float? - if (_currToken == JsonToken.VALUE_NUMBER_INT || _currToken == JsonToken.VALUE_NUMBER_FLOAT) { - return; + if (_currToken != JsonToken.VALUE_NUMBER_INT && _currToken != JsonToken.VALUE_NUMBER_FLOAT) { + throw _constructReadException("Current token (%s) not numeric, cannot use numeric value accessors", _currToken); } - _reportError("Current token ("+_currToken+") not numeric, can not use numeric value accessors"); } protected void convertNumberToInt() throws JacksonException diff --git a/smile/src/main/java/tools/jackson/dataformat/smile/SmileParser.java b/smile/src/main/java/tools/jackson/dataformat/smile/SmileParser.java index 7aea285d1..a5f5929e3 100644 --- a/smile/src/main/java/tools/jackson/dataformat/smile/SmileParser.java +++ b/smile/src/main/java/tools/jackson/dataformat/smile/SmileParser.java @@ -2264,6 +2264,21 @@ protected void _parseNumericValue() throws JacksonException throw _constructReadException("Current token (%s) not numeric, cannot use numeric value accessors", _currToken); } + @Override + protected boolean _parseNumericValueIfNumber() throws JacksonException + { + if (_tokenIncomplete) { + _tokenIncomplete = false; + int tb = _typeAsInt; + // ensure we got a numeric type with value that is lazily parsed + if ((tb >> 5) == 1) { + _finishNumberToken(tb); + return true; + } + } + return false; + } + /* @Override // since 2.6 protected int _parseIntValue() throws JacksonException diff --git a/smile/src/main/java/tools/jackson/dataformat/smile/SmileParserBase.java b/smile/src/main/java/tools/jackson/dataformat/smile/SmileParserBase.java index 51006bef5..5daef7805 100644 --- a/smile/src/main/java/tools/jackson/dataformat/smile/SmileParserBase.java +++ b/smile/src/main/java/tools/jackson/dataformat/smile/SmileParserBase.java @@ -253,8 +253,22 @@ public final boolean mayContainRawBinary() { @Override protected abstract void _closeInput() throws JacksonException; + /** + * Method called to complete parsing of a numeric value: will throw + * {@link StreamReadException} if current token is not numeric. + * + * @throws StreamReadException if current token is not numeric + */ protected abstract void _parseNumericValue() throws JacksonException; + /** + * Similar to {@link #_parseNumericValue()}, but will not throw exception + * if the current token is not a numeric value. + * + * @return {@code true} if current token is numeric; {@code false} otherwise + */ + protected abstract boolean _parseNumericValueIfNumber() throws JacksonException; + // public abstract int releaseBuffered(OutputStream out) throws JacksonException; // public abstract Object getInputSource(); @@ -391,7 +405,9 @@ public final Number getNumberValueExact() throws JacksonException { public final NumberType getNumberType() throws JacksonException { if (_numTypesValid == NR_UNKNOWN) { - _parseNumericValue(); // will also check event type + if (!_parseNumericValueIfNumber()) { + return null; + } } return _numberType; } diff --git a/smile/src/main/java/tools/jackson/dataformat/smile/async/NonBlockingParserBase.java b/smile/src/main/java/tools/jackson/dataformat/smile/async/NonBlockingParserBase.java index 941c47726..5a56367d8 100644 --- a/smile/src/main/java/tools/jackson/dataformat/smile/async/NonBlockingParserBase.java +++ b/smile/src/main/java/tools/jackson/dataformat/smile/async/NonBlockingParserBase.java @@ -210,10 +210,16 @@ protected void _closeInput() { // in future @Override protected void _parseNumericValue() throws JacksonException { - if (_currToken == JsonToken.VALUE_NUMBER_INT || _currToken == JsonToken.VALUE_NUMBER_FLOAT) { - return; + if (_currToken != JsonToken.VALUE_NUMBER_INT && _currToken != JsonToken.VALUE_NUMBER_FLOAT) { + _reportError("Current token (%s) not numeric, can not use numeric value accessors", _currToken); } - _reportError("Current token (%s) not numeric, can not use numeric value accessors", _currToken); + } + + @Override + protected boolean _parseNumericValueIfNumber() throws JacksonException + { + // No incomplete values so just return + return false; } /* diff --git a/smile/src/test/java/tools/jackson/dataformat/smile/parse/SmileNumberParsingGetType1433Test.java b/smile/src/test/java/tools/jackson/dataformat/smile/parse/SmileNumberParsingGetType1433Test.java index caa7c5ab6..07a07cc51 100644 --- a/smile/src/test/java/tools/jackson/dataformat/smile/parse/SmileNumberParsingGetType1433Test.java +++ b/smile/src/test/java/tools/jackson/dataformat/smile/parse/SmileNumberParsingGetType1433Test.java @@ -3,13 +3,11 @@ import org.junit.jupiter.api.Test; import tools.jackson.core.*; -import tools.jackson.core.exc.StreamReadException; import tools.jackson.dataformat.smile.SmileFactory; import tools.jackson.dataformat.smile.BaseTestForSmile; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.fail; public class SmileNumberParsingGetType1433Test extends BaseTestForSmile @@ -59,12 +57,8 @@ void getNumberType() throws Exception private void _verifyGetNumberTypeFail(JsonParser p, String token) throws Exception { - try { - p.getNumberType(); - fail("Should not pass"); - } catch (StreamReadException e) { - verifyException(e, "Current token ("+token+") not numeric, cannot use numeric"); - } + // In 2.x got exception; in 3.x null + assertNull(p.getNumberType()); } private SmileFactory jsonFactory() {