2
2
3
3
import java .io .IOException ;
4
4
import java .io .InputStream ;
5
+ import java .math .BigDecimal ;
6
+ import java .math .BigInteger ;
5
7
6
8
import org .apache .avro .io .BinaryDecoder ;
7
9
8
10
import com .fasterxml .jackson .core .*;
9
11
import com .fasterxml .jackson .core .io .IOContext ;
12
+ import com .fasterxml .jackson .core .io .NumberInput ;
10
13
import com .fasterxml .jackson .dataformat .avro .AvroParser ;
11
14
import com .fasterxml .jackson .dataformat .avro .AvroSchema ;
12
15
import com .fasterxml .jackson .dataformat .avro .CodecRecycler ;
@@ -21,7 +24,7 @@ public class AvroParserImpl extends AvroParser
21
24
22
25
/*
23
26
/**********************************************************
24
- /* Input source config, state
27
+ /* Input source config
25
28
/**********************************************************
26
29
*/
27
30
@@ -55,16 +58,36 @@ public class AvroParserImpl extends AvroParser
55
58
*/
56
59
protected BinaryDecoder _decoder ;
57
60
61
+ /*
62
+ /**********************************************************
63
+ /* Other decoding state
64
+ /**********************************************************
65
+ */
66
+
58
67
/**
59
68
* Index of the union branch that was followed to reach the current token. This is cleared when the next token is read.
69
+ *
70
+ * @since 2.9
60
71
*/
61
72
protected int _branchIndex ;
62
73
63
74
/**
64
75
* Index of the enum that was read as the current token. This is cleared when the next token is read.
76
+ *
77
+ * @since 2.9
65
78
*/
66
79
protected int _enumIndex ;
67
80
81
+ /**
82
+ * Value if decoded directly as `float`.
83
+ *<p>
84
+ * NOTE: base class (`ParserBase`) has other value storage, but since JSON
85
+ * has no distinction between double, float, only includes `float`.
86
+ *
87
+ * @since 2.9
88
+ */
89
+ protected float _numberFloat ;
90
+
68
91
/*
69
92
/**********************************************************
70
93
/* Life-cycle
@@ -221,6 +244,283 @@ protected void _initSchema(AvroSchema schema) throws JsonProcessingException {
221
244
_avroContext = new RootReader (this , schema .getReader ());
222
245
}
223
246
247
+ /*
248
+ /**********************************************************
249
+ /* Numeric accessors of public API
250
+ /**********************************************************
251
+ */
252
+
253
+ @ Override // since 2.9
254
+ public boolean isNaN () {
255
+ if (_currToken == JsonToken .VALUE_NUMBER_FLOAT ) {
256
+ if ((_numTypesValid & NR_DOUBLE ) != 0 ) {
257
+ // 10-Mar-2017, tatu: Alas, `Double.isFinite(d)` only added in JDK 8
258
+ double d = _numberDouble ;
259
+ return Double .isNaN (d ) || Double .isInfinite (d );
260
+ }
261
+ if ((_numTypesValid & NR_FLOAT ) != 0 ) {
262
+ float f = _numberFloat ;
263
+ return Float .isNaN (f ) || Float .isInfinite (f );
264
+ }
265
+ }
266
+ return false ;
267
+ }
268
+
269
+ @ Override
270
+ public Number getNumberValue () throws IOException
271
+ {
272
+ if (_numTypesValid == NR_UNKNOWN ) {
273
+ _checkNumericValue (NR_UNKNOWN ); // will also check event type
274
+ }
275
+ // Separate types for int types
276
+ if (_currToken == JsonToken .VALUE_NUMBER_INT ) {
277
+ if ((_numTypesValid & NR_INT ) != 0 ) {
278
+ return _numberInt ;
279
+ }
280
+ if ((_numTypesValid & NR_LONG ) != 0 ) {
281
+ return _numberLong ;
282
+ }
283
+ if ((_numTypesValid & NR_BIGINT ) != 0 ) {
284
+ return _numberBigInt ;
285
+ }
286
+ // Shouldn't get this far but if we do
287
+ return _numberBigDecimal ;
288
+ }
289
+
290
+ // And then floating point types. But here optimal type
291
+ // needs to be big decimal, to avoid losing any data?
292
+ if ((_numTypesValid & NR_BIGDECIMAL ) != 0 ) {
293
+ return _numberBigDecimal ;
294
+ }
295
+ if ((_numTypesValid & NR_DOUBLE ) != 0 ) {
296
+ return _numberDouble ;
297
+ }
298
+ if ((_numTypesValid & NR_FLOAT ) == 0 ) { // sanity check
299
+ _throwInternal ();
300
+ }
301
+ return _numberFloat ;
302
+ }
303
+
304
+ @ Override
305
+ public NumberType getNumberType () throws IOException
306
+ {
307
+ if (_numTypesValid == NR_UNKNOWN ) {
308
+ _checkNumericValue (NR_UNKNOWN ); // will also check event type
309
+ }
310
+ if (_currToken == JsonToken .VALUE_NUMBER_INT ) {
311
+ if ((_numTypesValid & NR_INT ) != 0 ) {
312
+ return NumberType .INT ;
313
+ }
314
+ if ((_numTypesValid & NR_LONG ) != 0 ) {
315
+ return NumberType .LONG ;
316
+ }
317
+ return NumberType .BIG_INTEGER ;
318
+ }
319
+
320
+ // And then floating point types. Here optimal type should be big decimal,
321
+ // to avoid losing any data? However... using BD is slow, so let's allow returning
322
+ // double as type if no explicit call has been made to access data as BD?
323
+ if ((_numTypesValid & NR_BIGDECIMAL ) != 0 ) {
324
+ return NumberType .BIG_DECIMAL ;
325
+ }
326
+ if ((_numTypesValid & NR_DOUBLE ) != 0 ) {
327
+ return NumberType .DOUBLE ;
328
+ }
329
+ return NumberType .FLOAT ;
330
+ }
331
+
332
+ @ Override
333
+ public float getFloatValue () throws IOException
334
+ {
335
+ if ((_numTypesValid & NR_FLOAT ) == 0 ) {
336
+ if (_numTypesValid == NR_UNKNOWN ) {
337
+ _checkNumericValue (NR_FLOAT );
338
+ }
339
+ if ((_numTypesValid & NR_FLOAT ) == 0 ) {
340
+ convertNumberToFloat ();
341
+ }
342
+ }
343
+ // Bounds/range checks would be tricky here, so let's not bother even trying...
344
+ /*
345
+ if (value < -Float.MAX_VALUE || value > MAX_FLOAT_D) {
346
+ _reportError("Numeric value ("+getText()+") out of range of Java float");
347
+ }
348
+ */
349
+ return _numberFloat ;
350
+ }
351
+
352
+ /*
353
+ /**********************************************************
354
+ /* Numeric conversions
355
+ /**********************************************************
356
+ */
357
+
358
+ protected void _checkNumericValue (int expType ) throws IOException
359
+ {
360
+ // Int or float?
361
+ if (_currToken == JsonToken .VALUE_NUMBER_INT || _currToken == JsonToken .VALUE_NUMBER_FLOAT ) {
362
+ return ;
363
+ }
364
+ _reportError ("Current token (" +getCurrentToken ()+") not numeric, can not use numeric value accessors" );
365
+ }
366
+
367
+ @ Override
368
+ protected void convertNumberToInt () throws IOException
369
+ {
370
+ // First, converting from long ought to be easy
371
+ if ((_numTypesValid & NR_LONG ) != 0 ) {
372
+ // Let's verify it's lossless conversion by simple roundtrip
373
+ int result = (int ) _numberLong ;
374
+ if (((long ) result ) != _numberLong ) {
375
+ _reportError ("Numeric value (" +getText ()+") out of range of int" );
376
+ }
377
+ _numberInt = result ;
378
+ } else if ((_numTypesValid & NR_BIGINT ) != 0 ) {
379
+ if (BI_MIN_INT .compareTo (_numberBigInt ) > 0
380
+ || BI_MAX_INT .compareTo (_numberBigInt ) < 0 ) {
381
+ reportOverflowInt ();
382
+ }
383
+ _numberInt = _numberBigInt .intValue ();
384
+ } else if ((_numTypesValid & NR_DOUBLE ) != 0 ) {
385
+ // Need to check boundaries
386
+ if (_numberDouble < MIN_INT_D || _numberDouble > MAX_INT_D ) {
387
+ reportOverflowInt ();
388
+ }
389
+ _numberInt = (int ) _numberDouble ;
390
+ } else if ((_numTypesValid & NR_FLOAT ) != 0 ) {
391
+ if (_numberFloat < MIN_INT_D || _numberFloat > MAX_INT_D ) {
392
+ reportOverflowInt ();
393
+ }
394
+ _numberInt = (int ) _numberFloat ;
395
+ } else if ((_numTypesValid & NR_BIGDECIMAL ) != 0 ) {
396
+ if (BD_MIN_INT .compareTo (_numberBigDecimal ) > 0
397
+ || BD_MAX_INT .compareTo (_numberBigDecimal ) < 0 ) {
398
+ reportOverflowInt ();
399
+ }
400
+ _numberInt = _numberBigDecimal .intValue ();
401
+ } else {
402
+ _throwInternal ();
403
+ }
404
+ _numTypesValid |= NR_INT ;
405
+ }
406
+
407
+ @ Override
408
+ protected void convertNumberToLong () throws IOException
409
+ {
410
+ if ((_numTypesValid & NR_INT ) != 0 ) {
411
+ _numberLong = (long ) _numberInt ;
412
+ } else if ((_numTypesValid & NR_BIGINT ) != 0 ) {
413
+ if (BI_MIN_LONG .compareTo (_numberBigInt ) > 0
414
+ || BI_MAX_LONG .compareTo (_numberBigInt ) < 0 ) {
415
+ reportOverflowLong ();
416
+ }
417
+ _numberLong = _numberBigInt .longValue ();
418
+ } else if ((_numTypesValid & NR_DOUBLE ) != 0 ) {
419
+ if (_numberDouble < MIN_LONG_D || _numberDouble > MAX_LONG_D ) {
420
+ reportOverflowLong ();
421
+ }
422
+ _numberLong = (long ) _numberDouble ;
423
+ } else if ((_numTypesValid & NR_FLOAT ) != 0 ) {
424
+ if (_numberFloat < MIN_LONG_D || _numberFloat > MAX_LONG_D ) {
425
+ reportOverflowInt ();
426
+ }
427
+ _numberLong = (long ) _numberFloat ;
428
+ } else if ((_numTypesValid & NR_BIGDECIMAL ) != 0 ) {
429
+ if (BD_MIN_LONG .compareTo (_numberBigDecimal ) > 0
430
+ || BD_MAX_LONG .compareTo (_numberBigDecimal ) < 0 ) {
431
+ reportOverflowLong ();
432
+ }
433
+ _numberLong = _numberBigDecimal .longValue ();
434
+ } else {
435
+ _throwInternal ();
436
+ }
437
+ _numTypesValid |= NR_LONG ;
438
+ }
439
+
440
+ @ Override
441
+ protected void convertNumberToBigInteger () throws IOException
442
+ {
443
+ if ((_numTypesValid & NR_BIGDECIMAL ) != 0 ) {
444
+ // here it'll just get truncated, no exceptions thrown
445
+ _numberBigInt = _numberBigDecimal .toBigInteger ();
446
+ } else if ((_numTypesValid & NR_LONG ) != 0 ) {
447
+ _numberBigInt = BigInteger .valueOf (_numberLong );
448
+ } else if ((_numTypesValid & NR_INT ) != 0 ) {
449
+ _numberBigInt = BigInteger .valueOf (_numberInt );
450
+ } else if ((_numTypesValid & NR_DOUBLE ) != 0 ) {
451
+ _numberBigInt = BigDecimal .valueOf (_numberDouble ).toBigInteger ();
452
+ } else if ((_numTypesValid & NR_FLOAT ) != 0 ) {
453
+ _numberBigInt = BigDecimal .valueOf (_numberFloat ).toBigInteger ();
454
+ } else {
455
+ _throwInternal ();
456
+ }
457
+ _numTypesValid |= NR_BIGINT ;
458
+ }
459
+
460
+ protected void convertNumberToFloat () throws IOException
461
+ {
462
+ // Note: this MUST start with more accurate representations, since we don't know which
463
+ // value is the original one (others get generated when requested)
464
+ if ((_numTypesValid & NR_BIGDECIMAL ) != 0 ) {
465
+ _numberFloat = _numberBigDecimal .floatValue ();
466
+ } else if ((_numTypesValid & NR_BIGINT ) != 0 ) {
467
+ _numberFloat = _numberBigInt .floatValue ();
468
+ } else if ((_numTypesValid & NR_DOUBLE ) != 0 ) {
469
+ _numberFloat = (float ) _numberDouble ;
470
+ } else if ((_numTypesValid & NR_LONG ) != 0 ) {
471
+ _numberFloat = (float ) _numberLong ;
472
+ } else if ((_numTypesValid & NR_INT ) != 0 ) {
473
+ _numberFloat = (float ) _numberInt ;
474
+ } else {
475
+ _throwInternal ();
476
+ }
477
+ _numTypesValid |= NR_FLOAT ;
478
+ }
479
+
480
+ @ Override
481
+ protected void convertNumberToDouble () throws IOException
482
+ {
483
+ // Note: this MUST start with more accurate representations, since we don't know which
484
+ // value is the original one (others get generated when requested)
485
+ if ((_numTypesValid & NR_BIGDECIMAL ) != 0 ) {
486
+ _numberDouble = _numberBigDecimal .doubleValue ();
487
+ } else if ((_numTypesValid & NR_FLOAT ) != 0 ) {
488
+ _numberDouble = (double ) _numberFloat ;
489
+ } else if ((_numTypesValid & NR_BIGINT ) != 0 ) {
490
+ _numberDouble = _numberBigInt .doubleValue ();
491
+ } else if ((_numTypesValid & NR_LONG ) != 0 ) {
492
+ _numberDouble = (double ) _numberLong ;
493
+ } else if ((_numTypesValid & NR_INT ) != 0 ) {
494
+ _numberDouble = (double ) _numberInt ;
495
+ } else {
496
+ _throwInternal ();
497
+ }
498
+ _numTypesValid |= NR_DOUBLE ;
499
+ }
500
+
501
+ @ Override
502
+ protected void convertNumberToBigDecimal () throws IOException
503
+ {
504
+ // Note: this MUST start with more accurate representations, since we don't know which
505
+ // value is the original one (others get generated when requested)
506
+ if ((_numTypesValid & NR_DOUBLE ) != 0 ) {
507
+ // 05-Apt-2017, tatu: Unlike with textual formats, we never have textual
508
+ // representation to work with here
509
+ _numberBigDecimal = new BigDecimal (_numberDouble );
510
+ } else if ((_numTypesValid & NR_FLOAT ) != 0 ) {
511
+ _numberBigDecimal = new BigDecimal (_numberFloat );
512
+ } else if ((_numTypesValid & NR_BIGINT ) != 0 ) {
513
+ _numberBigDecimal = new BigDecimal (_numberBigInt );
514
+ } else if ((_numTypesValid & NR_LONG ) != 0 ) {
515
+ _numberBigDecimal = BigDecimal .valueOf (_numberLong );
516
+ } else if ((_numTypesValid & NR_INT ) != 0 ) {
517
+ _numberBigDecimal = BigDecimal .valueOf (_numberInt );
518
+ } else {
519
+ _throwInternal ();
520
+ }
521
+ _numTypesValid |= NR_BIGDECIMAL ;
522
+ }
523
+
224
524
/*
225
525
/**********************************************************
226
526
/* Methods for AvroReadContext implementations: decoding
@@ -415,8 +715,8 @@ protected JsonToken setNumber(long v) {
415
715
}
416
716
417
717
protected JsonToken setNumber (float v ) {
418
- _numberDouble = v ;
419
- _numTypesValid = NR_DOUBLE ;
718
+ _numberFloat = v ;
719
+ _numTypesValid = NR_FLOAT ;
420
720
return JsonToken .VALUE_NUMBER_FLOAT ;
421
721
}
422
722
0 commit comments