Subversion Repositories bacoAlunos

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
1878 jmachado 1
package com.owlike.genson.stream;
2
 
3
import java.io.IOException;
4
import java.io.Reader;
5
import java.io.StringReader;
6
import java.util.ArrayDeque;
7
import java.util.Arrays;
8
import java.util.Deque;
9
import java.util.HashMap;
10
import java.util.Map;
11
 
12
import static com.owlike.genson.stream.ValueType.*;
13
 
14
public class JsonReader implements ObjectReader {
15
 
16
  protected final static int[] SKIPPED_TOKENS;
17
 
18
  static {
19
    SKIPPED_TOKENS = new int[128];
20
    SKIPPED_TOKENS['\t'] = 1;
21
    SKIPPED_TOKENS['\b'] = 1;
22
    SKIPPED_TOKENS['\n'] = 1;
23
    SKIPPED_TOKENS['\r'] = 1;
24
    SKIPPED_TOKENS['\f'] = 1;
25
    SKIPPED_TOKENS[' '] = 1;
26
  }
27
 
28
  private final static boolean[] _NEXT_TOKEN = new boolean[128];
29
 
30
  static {
31
    _NEXT_TOKEN[','] = true;
32
    _NEXT_TOKEN['"'] = true;
33
    _NEXT_TOKEN['{'] = true;
34
    _NEXT_TOKEN['['] = true;
35
    _NEXT_TOKEN['n'] = true;
36
    _NEXT_TOKEN['N'] = true;
37
    _NEXT_TOKEN['-'] = true;
38
    _NEXT_TOKEN['t'] = true;
39
    _NEXT_TOKEN['f'] = true;
40
    _NEXT_TOKEN['T'] = true;
41
    _NEXT_TOKEN['F'] = true;
42
 
43
    for (int i = 48; i < 58; i++)
44
      _NEXT_TOKEN[i] = true;
45
  }
46
 
47
  private final static char[] _END_OF_LINE = new char[]{'\n'};
48
  private final static char[] _END_OF_BLOCK_COMMENT = new char[]{'*', '/'};
49
 
50
  /*
51
   * Recupere dans Jackson
52
   */
53
  private final static int[] sHexValues = new int[128];
54
 
55
  static {
56
    1.5.0/docs/api/java/util/Arrays.html">Arrays.fill(sHexValues, -1);
57
    for (int i = 0; i < 10; ++i) {
58
      sHexValues['0' + i] = i;
59
    }
60
    for (int i = 0; i < 6; ++i) {
61
      sHexValues['a' + i] = 10 + i;
62
      sHexValues['A' + i] = 10 + i;
63
    }
64
  }
65
 
66
  private final static double[] _POWS = new double[309];
67
 
68
  static {
69
    for (int i = 0; i < _POWS.length; i++)
70
      _POWS[i] = 1.5.0/docs/api/java/lang/Math.html">Math.pow(10, i);
71
  }
72
 
73
  private final 1.5.0/docs/api/java/io/Reader.html">Reader reader;
74
  private final boolean strictDoubleParse;
75
  private final boolean readMetadata;
76
  private final char[] _buffer = new char[2048];
77
  private int _col;
78
  private int _row;
79
  private int _cursor;
80
  private int _buflen;
81
 
82
  private char[] _stringBuffer = new char[16];
83
  private int _stringBufferTail = 0;
84
  private int _stringBufferLength = _stringBuffer.length;
85
 
86
  private 1.5.0/docs/api/java/lang/String.html">String currentName;
87
  private 1.5.0/docs/api/java/lang/String.html">String _stringValue;
88
  protected long _intValue;
89
  protected double _doubleValue;
90
  private int _numberLen = 0;
91
  private 1.5.0/docs/api/java/lang/Boolean.html">Boolean _booleanValue;
92
  private ValueType valueType;
93
  private boolean _first = true;
94
  private boolean _metadata_readen = false;
95
  private Map<String, String> _metadata = new HashMap<String, String>(5);
96
 
97
  private final Deque<JsonType> _ctx = new ArrayDeque<JsonType>(10);
98
 
99
  {
100
    _ctx.push(JsonType.EMPTY);
101
  }
102
 
103
  public JsonReader(1.5.0/docs/api/java/lang/String.html">String source) {
104
    this(new 1.5.0/docs/api/java/io/StringReader.html">StringReader(source), false, false);
105
  }
106
 
107
  public JsonReader(1.5.0/docs/api/java/io/Reader.html">Reader reader, boolean strictDoubleParse, boolean readMetadata) {
108
    this.reader = reader;
109
    this.strictDoubleParse = strictDoubleParse;
110
    this.readMetadata = readMetadata;
111
 
112
    char token = (char) readNextToken(false);
113
    if ('[' == token) valueType = ARRAY;
114
    else if ('{' == token) valueType = OBJECT;
115
    else {
116
      // ok lets try to read next
117
      if (_buflen > 0) {
118
        try {
119
          valueType = consumeValue();
120
        } catch (JsonStreamException jse) {
121
          try {
122
            // we must cheat because consumeString attends the current token to be "
123
            // and will increment the cursor
124
            _cursor = -1;
125
            _col = -1;
126
            _stringValue = consumeString('"');
127
            valueType = STRING;
128
          } catch (1.5.0/docs/api/java/lang/RuntimeException.html">RuntimeException re) {
129
            throw re;
130
          }
131
        }
132
        if (valueOf(valueType.name()) == null)
133
          throw new JsonStreamException(
134
            "Failed to instanciate reader, first character was " + (char) token
135
              + " when possible characters are [ and {");
136
      } else valueType = NULL;
137
    }
138
  }
139
 
140
  public void close() {
141
    try {
142
      reader.close();
143
    } catch (1.5.0/docs/api/java/io/IOException.html">IOException e) {
144
      throw new JsonStreamException(e);
145
    }
146
  }
147
 
148
  public ObjectReader beginArray() {
149
    begin('[', JsonType.ARRAY);
150
    valueType = ARRAY;
151
    if (_metadata_readen) _metadata.clear();
152
    return this;
153
  }
154
 
155
  public ObjectReader beginObject() {
156
    if (!_metadata_readen) {
157
      begin('{', JsonType.OBJECT);
158
      valueType = OBJECT;
159
      if (readMetadata) {
160
        _metadata.clear();
161
        readMetadata();
162
      }
163
    }
164
    return this;
165
  }
166
 
167
  public ObjectReader nextObjectMetadata() {
168
    return beginObject();
169
  }
170
 
171
  public ObjectReader endArray() {
172
    end(']', JsonType.ARRAY);
173
    return this;
174
  }
175
 
176
  public ObjectReader endObject() {
177
    end('}', JsonType.OBJECT);
178
    _metadata.clear();
179
    _metadata_readen = false;
180
    return this;
181
  }
182
 
183
  public 1.5.0/docs/api/java/lang/String.html">String name() {
184
    if (enclosingType() != JsonType.OBJECT)
185
      throw new JsonStreamException("Only json objects have names, actual type is "
186
        + valueType);
187
    return currentName;
188
  }
189
 
190
  public 1.5.0/docs/api/java/lang/String.html">String valueAsString() {
191
    if (STRING == valueType) return _stringValue;
192
    if (INTEGER == valueType) return "" + _intValue;
193
    if (DOUBLE == valueType) return "" + _doubleValue;
194
    if (NULL == valueType) return null;
195
    if (BOOLEAN == valueType) {
196
      return _booleanValue.toString();
197
    }
198
    throw new JsonStreamException("Readen value can not be converted to String");
199
  }
200
 
201
  public int valueAsInt() {
202
    if (INTEGER == valueType) {
203
      int value = (int) _intValue;
204
      if (value != _intValue) throwNumberFormatException("an int", "overflowing long value " + _intValue);
205
      return value;
206
    } else if (DOUBLE == valueType) {
207
      int value = (int) _doubleValue;
208
      long longValue = (long) _doubleValue;
209
      // lets accept only if the integer part is the same and ignore the decimals
210
      if (value != longValue) {
211
        throwNumberFormatException("an int", "overflowing double value " + _doubleValue);
212
      }
213
      return value;
214
    } else if (STRING == valueType) return 1.5.0/docs/api/java/lang/Integer.html">Integer.parseInt(_stringValue);
215
 
216
    throw new JsonStreamException("Expected a int but value is of type " + valueType);
217
  }
218
 
219
  public long valueAsLong() {
220
    if (INTEGER == valueType) {
221
      return _intValue;
222
    } else if (DOUBLE == valueType) {
223
      if (1.5.0/docs/api/java/lang/Long.html">Long.MIN_VALUE > _doubleValue || _doubleValue > 1.5.0/docs/api/java/lang/Long.html">Long.MAX_VALUE) {
224
        throwNumberFormatException("a long", "overflowing double value " + _doubleValue);
225
      }
226
      return (long) _doubleValue;
227
    } else if (STRING == valueType) return 1.5.0/docs/api/java/lang/Long.html">Long.parseLong(_stringValue);
228
 
229
    throw new JsonStreamException("Expected a long but value is of type " + valueType);
230
  }
231
 
232
  public double valueAsDouble() {
233
    if (DOUBLE == valueType) {
234
      return _doubleValue;
235
    } else if (INTEGER == valueType) {
236
      // for the moment lets do that even if there is some precision loss...
237
      return 1.5.0/docs/api/java/lang/Long.html">Long.valueOf(_intValue).doubleValue();
238
    } else if (STRING == valueType) return 1.5.0/docs/api/java/lang/Double.html">Double.parseDouble(_stringValue);
239
 
240
    throw new JsonStreamException("Expected a double but value is of type " + valueType);
241
  }
242
 
243
  public short valueAsShort() {
244
    if (INTEGER == valueType) {
245
      short value = (short) _intValue;
246
      if (value != _intValue) throwNumberFormatException("a short", "overflowing long value " + _intValue);
247
      return value;
248
    } else if (DOUBLE == valueType) {
249
      short value = (short) _doubleValue;
250
      long longValue = (long) _doubleValue;
251
      // lets accept only if the integer part is the same and ignore the decimals
252
      if (value != longValue) {
253
        throwNumberFormatException("a short", "overflowing double value " + _doubleValue);
254
      }
255
      return value;
256
    } else if (STRING == valueType) return 1.5.0/docs/api/java/lang/Short.html">Short.parseShort(_stringValue);
257
 
258
    throw new JsonStreamException("Expected a short but value is of type " + valueType);
259
  }
260
 
261
  public float valueAsFloat() {
262
    if (DOUBLE == valueType) {
263
      return (float) _doubleValue;
264
    } else if (INTEGER == valueType) {
265
      // same as for doubles, for the moment lets do that even if there is some precision
266
      // loss...
267
      return 1.5.0/docs/api/java/lang/Long.html">Long.valueOf(_intValue).floatValue();
268
    } else if (STRING == valueType) return 1.5.0/docs/api/java/lang/Float.html">Float.parseFloat(_stringValue);
269
 
270
    throw new JsonStreamException("Expected a float but value is of type " + valueType);
271
  }
272
 
273
  public boolean valueAsBoolean() {
274
    if (BOOLEAN == valueType) {
275
      return _booleanValue;
276
    }
277
    if (STRING == valueType) return 1.5.0/docs/api/java/lang/Boolean.html">Boolean.parseBoolean(_stringValue);
278
 
279
    throw new JsonStreamException("Readen value is not of type boolean");
280
  }
281
 
282
  public byte[] valueAsByteArray() {
283
    if (STRING == valueType) return Base64.decodeFast(_stringValue);
284
    if (NULL == valueType) return null;
285
    throw new JsonStreamException("Expected a String to convert to byte array found "
286
      + valueType);
287
  }
288
 
289
  public 1.5.0/docs/api/java/lang/String.html">String metadata(1.5.0/docs/api/java/lang/String.html">String name) {
290
    if (!_metadata_readen) nextObjectMetadata();
291
    return _metadata.get(name);
292
  }
293
 
294
  public ValueType getValueType() {
295
    return valueType;
296
  }
297
 
298
  public ObjectReader skipValue() {
299
 
300
    if (ARRAY == valueType || OBJECT == valueType) {
301
      int balance = 0;
302
      do {
303
        if (ARRAY == valueType) {
304
          beginArray();
305
          balance++;
306
        } else if (OBJECT == valueType) {
307
          beginObject();
308
          balance++;
309
        }
310
 
311
        while (hasNext()) {
312
          next();
313
          skipValue();
314
        }
315
 
316
        JsonType type = _ctx.peek();
317
        if (JsonType.ARRAY == type) {
318
          endArray();
319
          balance--;
320
        } else if (JsonType.OBJECT == type) {
321
          endObject();
322
          balance--;
323
        }
324
      } while (balance > 0);
325
    }
326
 
327
    return this;
328
  }
329
 
330
  public boolean hasNext() {
331
    int token = readNextToken(false);
332
    if (token == -1) return false;
333
    if (token < 128) {
334
      if (_first || _ctx.size() == 1) return _NEXT_TOKEN[token];
335
      else if (token == ',') return true;
336
    }
337
 
338
    return false;
339
  }
340
 
341
  public ValueType next() {
342
    _metadata_readen = false;
343
    _first = false;
344
 
345
    char ctoken = (char) readNextToken(false);
346
 
347
    if (ctoken == ',') {
348
      _cursor++;
349
      ctoken = (char) readNextToken(false);
350
    } else if (JsonType.ARRAY == _ctx.peek()) {
351
      if (ctoken == '[') {
352
        valueType = ARRAY;
353
        return valueType;
354
      }
355
      if (ctoken == '{') {
356
        valueType = OBJECT;
357
        return valueType;
358
      }
359
    }
360
 
361
    if (JsonType.OBJECT == _ctx.peek()) {
362
      currentName = consumeString(ctoken);
363
      if (readNextToken(true) != ':') newWrongTokenException(":", _cursor - 1);
364
    }
365
 
366
    valueType = consumeValue();
367
    return valueType;
368
  }
369
 
370
  @1.5.0/docs/api/java/lang/Override.html">Override
371
  public JsonType enclosingType() {
372
    return _ctx.peek();
373
  }
374
 
375
  protected final ValueType consumeValue() {
376
    char ctoken = (char) readNextToken(false);
377
    if (ctoken == '"') {
378
      _stringValue = consumeString(ctoken);
379
      return STRING;
380
    } else if (ctoken == '[') return ARRAY;
381
    else if (ctoken == '{') return OBJECT;
382
    else return consumeLiteral();
383
  }
384
 
385
  protected final void readMetadata() {
386
    _metadata_readen = true;
387
    while (true) {
388
      char ctoken = (char) readNextToken(false);
389
      if ('"' != ctoken) return;
390
      ensureBufferHas(2, true);
391
 
392
      if ('@' == _buffer[_cursor + 1]) {
393
        _cursor++;
394
        // we cheat here...
395
        1.5.0/docs/api/java/lang/String.html">String key = consumeString(ctoken);
396
 
397
        if (readNextToken(true) != ':') newWrongTokenException(":", _cursor - 1);
398
 
399
        1.5.0/docs/api/java/lang/String.html">String value = consumeString((char) readNextToken(false));
400
        _metadata.put(key, value);
401
        if (readNextToken(false) == ',') {
402
          _cursor++;
403
        }
404
      } else return;
405
    }
406
  }
407
 
408
  protected final void begin(int character, JsonType type) {
409
    int token = readNextToken(true);
410
    // seems unecessary as it is done in readNextToken
411
    // checkIllegalEnd(token);
412
    if (character == token) {
413
      _ctx.push(type);
414
    } else newWrongTokenException("" + (char) character, _cursor - 1);
415
    _first = true;
416
  }
417
 
418
  protected final void end(int character, JsonType type) {
419
    int token = readNextToken(true);
420
    // seems unecessary as it is done in readNextToken
421
    // checkIllegalEnd(token);
422
    if (character == token && type == _ctx.peek()) {
423
      _ctx.pop();
424
    } else newWrongTokenException("" + (char) character, _cursor - 1);
425
    _first = false;
426
  }
427
 
428
  protected final 1.5.0/docs/api/java/lang/String.html">String consumeString(int token) {
429
    if (token != '"') newMisplacedTokenException(_cursor);
430
    _cursor++;
431
    boolean buffered = false;
432
    while (true) {
433
      if (fillBuffer(true) < 0) {
434
        // TODO ugly to copy, by the way ensure we don't have the same problem elsewhere
435
        1.5.0/docs/api/java/lang/String.html">String name = new 1.5.0/docs/api/java/lang/String.html">String(_stringBuffer, 0, _stringBufferTail);
436
        _stringBufferTail = 0;
437
        return name;
438
      }
439
 
440
      int i = _cursor;
441
      for (; i < _buflen; ) {
442
        if (_buffer[i] == '"') {
443
          if (buffered) {
444
            writeToStringBuffer(_buffer, _cursor, i - _cursor);
445
            _cursor = i + 1;
446
            1.5.0/docs/api/java/lang/String.html">String name = new 1.5.0/docs/api/java/lang/String.html">String(_stringBuffer, 0, _stringBufferTail);
447
            _stringBufferTail = 0;
448
            return name;
449
          } else {
450
            1.5.0/docs/api/java/lang/String.html">String name = new 1.5.0/docs/api/java/lang/String.html">String(_buffer, _cursor, i - _cursor);
451
            _cursor = i + 1;
452
            return name;
453
          }
454
        } else if (_buffer[i] == '\\') {
455
          buffered = true;
456
          writeToStringBuffer(_buffer, _cursor, i - _cursor);
457
          _cursor = i + 1;
458
          if (_stringBufferLength <= (_stringBufferTail + 1)) expandStringBuffer(16);
459
          _stringBuffer[_stringBufferTail++] = readEscaped();
460
          i = _cursor;
461
        } else i++;
462
      }
463
 
464
      buffered = true;
465
      writeToStringBuffer(_buffer, _cursor, i - _cursor);
466
      _cursor = i + 1;
467
    }
468
  }
469
 
470
  /**
471
   * Reads the next literal value into _booleanValue, _doubleValue or _intValue and returns the
472
   * type of the readed literal, possible values are : INTEGER, DOUBLE, BOOLEAN, NULL. When
473
   * calling this method the _cursor must be positioned on the first character of the value in the
474
   * _buffer, you can ensure that by calling {@link #readNextToken(boolean)}.
475
   */
476
  protected final ValueType consumeLiteral() {
477
    int token = _buffer[_cursor];
478
 
479
    if ((token > 47 && token < 58) || token == 45) {
480
      return consumeNumber();
481
    } else {
482
      ensureBufferHas(4, true);
483
 
484
      if ((_buffer[_cursor] == 'N' || _buffer[_cursor] == 'n')
485
        && (_buffer[_cursor + 1] == 'U' || _buffer[_cursor + 1] == 'u')
486
        && (_buffer[_cursor + 2] == 'L' || _buffer[_cursor + 2] == 'l')
487
        && (_buffer[_cursor + 3] == 'L' || _buffer[_cursor + 3] == 'l')) {
488
        _cursor += 4;
489
        return NULL;
490
      }
491
 
492
      if ((_buffer[_cursor] == 'T' || _buffer[_cursor] == 't')
493
        && (_buffer[_cursor + 1] == 'R' || _buffer[_cursor + 1] == 'r')
494
        && (_buffer[_cursor + 2] == 'U' || _buffer[_cursor + 2] == 'u')
495
        && (_buffer[_cursor + 3] == 'E' || _buffer[_cursor + 3] == 'e')) {
496
        _booleanValue = true;
497
        _cursor += 4;
498
        return BOOLEAN;
499
      }
500
      ensureBufferHas(5, true);
501
 
502
      if ((_buffer[_cursor] == 'F' || _buffer[_cursor] == 'f')
503
        && (_buffer[_cursor + 1] == 'A' || _buffer[_cursor + 1] == 'a')
504
        && (_buffer[_cursor + 2] == 'L' || _buffer[_cursor + 2] == 'l')
505
        && (_buffer[_cursor + 3] == 'S' || _buffer[_cursor + 3] == 's')
506
        && (_buffer[_cursor + 4] == 'E' || _buffer[_cursor + 4] == 'e')) {
507
        _booleanValue = false;
508
        _cursor += 5;
509
        return BOOLEAN;
510
      } else {
511
        throw new JsonStreamException.Builder().message(
512
          "Illegal character around row " + _row + " and column " + (_cursor - _col)
513
            + " awaited for literal (number, boolean or null) but read '"
514
            + _buffer[_cursor] + "'!").create();
515
      }
516
    }
517
  }
518
 
519
  private ValueType consumeNumber() {
520
    // lets fill the buffer and handle differently overflowing values
521
    if ((_buflen - _cursor) < 378) ensureBufferHas(_buflen, false);
522
 
523
    int begin = _cursor;
524
    int cur;
525
    boolean negative;
526
    // check the sign
527
    if (_buffer[_cursor] == 45) {
528
      negative = true;
529
      _cursor++;
530
      cur = _cursor;
531
    } else {
532
      negative = false;
533
      cur = _cursor;
534
    }
535
    // just to handle invalid leading 0000
536
    for (; cur < _buflen && _buffer[cur] == 48; cur++) ;
537
    // Careful we consume the '-' here, but also all the leading 0, even if it is of form 0.xxx
538
    _cursor = cur;
539
 
540
    int len = 1.5.0/docs/api/java/lang/Math.html">Math.min(_buflen, cur + 18);
541
    int token;
542
 
543
    long longValue = 0;
544
    for (; cur < len; cur++) {
545
      token = _buffer[cur];
546
      if (token < 48 || token > 57) {
547
        break;
548
      }
549
      longValue = 10L * longValue + (token - 48);
550
    }
551
 
552
    if (cur < _buflen) {
553
      // read the maximum we can to fill the long capacity, at max we can read 1 additional
554
      // digit
555
      token = _buffer[cur];
556
      if (token > 47 && token < 58) {
557
        long newLongValue = 10L * longValue + (token - 48);
558
        if (newLongValue > longValue) {
559
          longValue = newLongValue;
560
          cur++;
561
        }
562
        /* TODO we parse here Long.MIN_VALUE as a double, we had also pb in the writer
563
        * the solution => instead of doing operations on positive numbers, do the inverse, use negative numbers
564
        * as the min negative long holds the - max positive long.
565
        */
566
        // else we exceed long capacity, just continue and parse it as a double
567
      }
568
 
569
      if (cur < _buflen
570
        && ((token = _buffer[cur]) == 46 || token == 101 || token == 69 || (token > 47 && token < 58))) {
571
 
572
        if (strictDoubleParse) {
573
          _cursor = begin;
574
          return consumeStrictNumber(cur);
575
        } else return consumeDouble(cur, longValue, negative);
576
      }
577
    }
578
 
579
    _intValue = negative ? -longValue : longValue;
580
    _numberLen = cur - _cursor;
581
    _cursor = cur;
582
    return INTEGER;
583
  }
584
 
585
  // a custom implementation based on fast path principles
586
  private ValueType consumeDouble(int cur, long longValue, boolean negative) {
587
    int token;
588
 
589
    // TODO a verifier : on ne veut compter dans les intDigit que ce qui n'est pas pris dans le
590
    // longValue, idem pour les decimales??
591
    int intDigits = cur - _cursor;
592
    int valueDigits = longValue > 0 ? cur - _cursor : 0;
593
 
594
    // ok we have readen as many characters as a long can contain
595
    // the next readen characters will serve for the digit count for large integer numbers
596
    if (intDigits > 17) {
597
      for (; cur < _buflen; cur++) {
598
        if (_buffer[cur] < 48 || _buffer[cur] > 57) {
599
          break;
600
        }
601
      }
602
      // only if we advanced
603
      if (intDigits != (cur - _cursor)) intDigits = (cur - _cursor) - intDigits;
604
    } else
605
      // TODO check ça m'a l'air bizarre comme cas...
606
      intDigits = 0;
607
 
608
    int decimalDigits = 0;
609
 
610
    // next possible case is a dot
611
    if (cur < _buflen && _buffer[cur] == 46) {
612
      cur++;
613
      int start = cur;
614
      // if integer part value is zero we could use scientific notation and win in precision
615
      if (longValue == 0) {
616
        // reset the counter as we don't care of the leading zeros
617
        intDigits = 0;
618
        // now lets try to read as many consecutive zeros as available
619
        for (; cur < _buflen && _buffer[cur] == 48; cur++) ;
620
      }
621
      // ok now we must read again into the longValue
622
      int len = 1.5.0/docs/api/java/lang/Math.html">Math.min(_buflen, cur + (18 - valueDigits));
623
 
624
      for (; cur < len; cur++) {
625
        token = _buffer[cur];
626
        if (token < 48 || token > 57) {
627
          break;
628
        }
629
        longValue = 10L * longValue + (token - 48);
630
      }
631
      decimalDigits = cur - start;
632
 
633
      start = cur;
634
      // no need to count digits after the precision we support for decimals
635
      // continue reading digits and just ignore the values, we will truncate
636
      for (; cur < _buflen; cur++) {
637
        if (_buffer[cur] < 48 || _buffer[cur] > 57) {
638
          break;
639
        }
640
      }
641
    }
642
 
643
    // now try to read exponent E/e
644
    if ((cur + 1) < _buflen && (_buffer[cur] == 101 || _buffer[cur] == 69)) {
645
      token = _buffer[++cur];
646
      boolean negativeExp;
647
      // check the sign
648
      if (token == 45) {
649
        negativeExp = true;
650
        cur++;
651
      } else {
652
        if (token == 43) cur++;
653
        negativeExp = false;
654
      }
655
      // read the power of ten
656
      int powValue = 0;
657
      for (; cur < _buflen; cur++) {
658
        token = _buffer[cur];
659
        if (token < 48 || token > 57) {
660
          break;
661
        }
662
        powValue = 10 * powValue + (token - 48);
663
      }
664
 
665
      // depending on the sign put it in the decimal digit counter or integer digit counter
666
      if (negativeExp) decimalDigits += powValue;
667
      else intDigits += powValue;
668
    }
669
 
670
    // and now make the difference so it balances well
671
    decimalDigits = intDigits - decimalDigits;
672
 
673
    if (decimalDigits < 0) {
674
            /*
675
             * This allows to handle also Double.MIN_VALUE and Double.MIN_NORMAL as
676
             * Double.MIN_NORMAL has 16 decimal digits, 308+16=324, but 10^324 overflows the double
677
             * capacity, its Infinity. So we use this little trick.
678
             */
679
      if (decimalDigits < -308) {
680
        if (decimalDigits < -325) {
681
          _doubleValue = 0;
682
        } else {
683
          _doubleValue = longValue / _POWS[-decimalDigits - 308];
684
          _doubleValue = _doubleValue / _POWS[308];
685
        }
686
      } else {
687
        // better precision than multiplication
688
        _doubleValue = longValue / _POWS[-decimalDigits];
689
      }
690
    } else {
691
      if (decimalDigits > 308) {
692
        _doubleValue = 1.5.0/docs/api/java/lang/Double.html">Double.POSITIVE_INFINITY;
693
      } else {
694
        // we don't need to apply same technique as before as
695
        // 308-16=292 so it's okay :)
696
        _doubleValue = longValue * _POWS[decimalDigits];
697
      }
698
    }
699
 
700
    _doubleValue = negative ? -_doubleValue : _doubleValue;
701
    _numberLen = cur - _cursor;
702
    _cursor = cur;
703
    return DOUBLE;
704
  }
705
 
706
  private final ValueType consumeStrictNumber(int localCursor) {
707
    if (localCursor < _buflen) {
708
      // consider all the remaining integer values as part of the double
709
      for (; localCursor < _buflen; localCursor++) {
710
        if (_buffer[localCursor] < 48 || _buffer[localCursor] > 57) {
711
          break;
712
        }
713
      }
714
    }
715
 
716
    if (localCursor < _buflen) {
717
      if (_buffer[localCursor] == '.') {
718
        localCursor = advanceWhileNumeric(++localCursor);
719
      }
720
    }
721
 
722
    if (localCursor + 1 < _buflen) {
723
      char ctoken = _buffer[localCursor];
724
      if (ctoken == 'e' || ctoken == 'E') {
725
        ctoken = _buffer[++localCursor];
726
        if (ctoken == '-' || ctoken == '+' || (ctoken > 47 && ctoken < 58)) {
727
          localCursor = advanceWhileNumeric(++localCursor);
728
        } else newWrongTokenException("'-' or '+' or '' (same as +)");
729
      }
730
    }
731
 
732
    // it may overflow of the max numeric value we accept to read, it should
733
    if (localCursor >= _buflen) {
734
 
735
    }
736
 
737
    _numberLen = localCursor - _cursor;
738
    _stringValue = new 1.5.0/docs/api/java/lang/String.html">String(_buffer, _cursor, _numberLen);
739
    _doubleValue = 1.5.0/docs/api/java/lang/Double.html">Double.parseDouble(_stringValue);
740
    _cursor = localCursor;
741
    return DOUBLE;
742
  }
743
 
744
  private int advanceWhileNumeric(int cursor) {
745
    for (; cursor < _buflen; cursor++) {
746
      if ((_buffer[cursor] < 48 || _buffer[cursor] > 57)) {
747
        return cursor;
748
      }
749
    }
750
    return cursor;
751
  }
752
 
753
  protected final int readNextToken(boolean consume) {
754
    while (true) {
755
      if (_cursor >= _buflen) fillBuffer(true);
756
 
757
      for (; _cursor < _buflen; _cursor++) {
758
        int token = _buffer[_cursor];
759
        if (token < 128 && SKIPPED_TOKENS[token] == 0) {
760
          if (token == '/') {
761
            ensureBufferHas(2, true);
762
            if (_buffer[_cursor + 1] == '*') {
763
              _cursor += 2;
764
              advanceAfter(_END_OF_BLOCK_COMMENT);
765
            } else if (_buffer[_cursor + 1] == '/') {
766
              _cursor += 2;
767
              advanceAfter(_END_OF_LINE);
768
              _row++;
769
              _col = _cursor;
770
            } else newWrongTokenException("start comment // or /*", _cursor);
771
            // don't consume the token
772
            _cursor--;
773
          } else if (consume) {
774
            return _buffer[_cursor++];
775
          } else return token;
776
        } else if (_buffer[_cursor] == '\n') {
777
          _row++;
778
          _col = _cursor;
779
        }
780
      }
781
 
782
      if (_buflen == -1) break;
783
    }
784
 
785
    return _cursor < _buflen ? _buffer[_cursor] : -1;
786
  }
787
 
788
  private final void advanceAfter(char[] str) {
789
    int strPos = 0;
790
    while (true) {
791
      if (_cursor >= _buflen) fillBuffer(true);
792
 
793
      for (; _cursor < _buflen && strPos < str.length; _cursor++) {
794
        if (_buffer[_cursor] == str[strPos]) {
795
          strPos++;
796
        } else strPos = 0;
797
      }
798
 
799
      if (strPos == str.length) {
800
        return;
801
      }
802
      if (_buflen == -1) break;
803
    }
804
  }
805
 
806
  protected final char readEscaped() {
807
    fillBuffer(true);
808
 
809
    char token = _buffer[_cursor++];
810
    switch (token) {
811
      case 'b':
812
        return '\b';
813
      case 't':
814
        return '\t';
815
      case 'n':
816
        return '\n';
817
      case 'f':
818
        return '\f';
819
      case 'r':
820
        return '\r';
821
      case '"':
822
      case '/':
823
      case '\\':
824
        return token;
825
 
826
      case 'u':
827
        break;
828
 
829
      default:
830
        newMisplacedTokenException(_cursor - 1);
831
    }
832
 
833
    int value = 0;
834
    if (ensureBufferHas(4, false) < 0) {
835
      throw new JsonStreamException("Expected 4 hex-digit for character escape sequence!");
836
      // System.arraycopy(buffer, cursor, buffer, 0, buflen-cursor);
837
      // buflen = buflen - cursor + reader.read(buffer, buflen-cursor, cursor);
838
      // cursor = 0;
839
    }
840
    for (int i = 0; i < 4; ++i) {
841
      int ch = _buffer[_cursor++];
842
      int digit = (ch > 127) ? -1 : sHexValues[ch];
843
      if (digit < 0) {
844
        throw new JsonStreamException("Wrong character '" + ch
845
          + "' expected a hex-digit for character escape sequence!");
846
      }
847
      value = (value << 4) | digit;
848
    }
849
 
850
    return (char) value;
851
  }
852
 
853
  private final void writeToStringBuffer(final char[] data, final int offset, final int length) {
854
    if (_stringBufferLength <= (_stringBufferTail + length)) {
855
      expandStringBuffer(length);
856
    }
857
    1.5.0/docs/api/java/lang/System.html">System.arraycopy(data, offset, _stringBuffer, _stringBufferTail, length);
858
    _stringBufferTail += length;
859
  }
860
 
861
  private final void expandStringBuffer(int length) {
862
    char[] extendedStringBuffer = new char[_stringBufferLength * 2 + length];
863
    1.5.0/docs/api/java/lang/System.html">System.arraycopy(_stringBuffer, 0, extendedStringBuffer, 0, _stringBufferTail);
864
    _stringBuffer = extendedStringBuffer;
865
    _stringBufferLength = extendedStringBuffer.length;
866
  }
867
 
868
  private final int fillBuffer(boolean doThrow) {
869
    if (_cursor < _buflen) return _buflen;
870
    try {
871
      _buflen = reader.read(_buffer);
872
    } catch (1.5.0/docs/api/java/io/IOException.html">IOException ioe) {
873
      throw new JsonStreamException(ioe);
874
    }
875
    checkIllegalEnd(_buflen);
876
    _cursor = 0;
877
    _col = 0;
878
    return _buflen;
879
  }
880
 
881
  private final int ensureBufferHas(int minLength, boolean doThrow) {
882
    try {
883
      int actualLen = _buflen - _cursor;
884
      if (actualLen >= minLength) {
885
        return actualLen;
886
      }
887
 
888
      1.5.0/docs/api/java/lang/System.html">System.arraycopy(_buffer, _cursor, _buffer, 0, actualLen);
889
      for (; actualLen < minLength; ) {
890
        int len = reader.read(_buffer, actualLen, _buffer.length - actualLen);
891
        if (len < 0) {
892
          if (doThrow) throw new JsonStreamException(
893
            "Encountered end of stream, incomplete json!");
894
          else {
895
            _buflen = actualLen;
896
            _col = 0;
897
            _cursor = 0;
898
            return len;
899
          }
900
        }
901
        actualLen += len;
902
      }
903
      _buflen = actualLen;
904
      _col = 0;
905
      _cursor = 0;
906
      return actualLen;
907
    } catch (1.5.0/docs/api/java/io/IOException.html">IOException ioe) {
908
      throw new JsonStreamException(ioe);
909
    }
910
  }
911
 
912
  protected final boolean isEOF() {
913
    return _buflen < 0 || fillBuffer(false) < 0;
914
  }
915
 
916
  private final void newWrongTokenException(1.5.0/docs/api/java/lang/String.html">String awaited) {
917
    newWrongTokenException(awaited, _cursor);
918
  }
919
 
920
  public int column() {
921
    int col = _cursor - _col;
922
    return col < 0 ? 0 : col;
923
  }
924
 
925
  public int row() {
926
    return _row;
927
  }
928
 
929
  private final void newWrongTokenException(1.5.0/docs/api/java/lang/String.html">String awaited, int cursor) {
930
    // otherwise it fails when an error occurs on first character
931
    if (cursor < 0) cursor = 0;
932
    int pos = cursor - _col;
933
    if (pos < 0) pos = 0;
934
 
935
    if (_buflen < 0) throw new JsonStreamException(
936
      "Incomplete data or malformed json : encoutered end of stream but expected "
937
        + awaited).niceTrace();
938
    else throw new JsonStreamException.Builder()
939
      .message(
940
        "Illegal character at row " + _row + " and column " + pos + " expected "
941
          + awaited + " but read '" + _buffer[cursor] + "' !")
942
      .locate(_row, pos).create().niceTrace();
943
  }
944
 
945
  private final void newMisplacedTokenException(int cursor) {
946
    if (_buflen < 0)
947
      throw JsonStreamException.niceTrace(new JsonStreamException(
948
        "Incomplete data or malformed json : encoutered end of stream."));
949
 
950
    if (cursor < 0) cursor = 0;
951
    int pos = cursor - _col;
952
    if (pos < 0) pos = 0;
953
 
954
    throw new JsonStreamException.Builder()
955
      .message(
956
        "Encountred misplaced character '" + _buffer[cursor] + "' around row "
957
          + _row + " and column " + pos).locate(_row, pos).create().niceTrace();
958
  }
959
 
960
  private final void checkIllegalEnd(int token) {
961
    if (token == -1 && JsonType.EMPTY != _ctx.peek())
962
      throw new JsonStreamException(
963
        "Incomplete data or malformed json : encoutered end of stream!").niceTrace();
964
  }
965
 
966
  private void throwNumberFormatException(1.5.0/docs/api/java/lang/String.html">String expected, 1.5.0/docs/api/java/lang/String.html">String encoutered) {
967
    int pos = _cursor - _col - _numberLen;
968
    throw JsonStreamException.niceTrace(new 1.5.0/docs/api/java/lang/NumberFormatException.html">NumberFormatException("Wrong numeric type at row " + _row + " and column " + pos
969
      + ", expected " + expected + " but encoutered " + encoutered));
970
  }
971
 
972
}