blob: 607e1a622b81f10b93e789a6c89d5e6ffcad84bd [file] [log] [blame]
Tatu Saloranta81fb43c2017-05-17 18:25:34 -07001package com.fasterxml.jackson.core.json.async;
2
3import java.io.IOException;
4import java.io.OutputStream;
5
6import com.fasterxml.jackson.core.JsonToken;
7import com.fasterxml.jackson.core.async.ByteArrayFeeder;
8import com.fasterxml.jackson.core.async.NonBlockingInputFeeder;
9import com.fasterxml.jackson.core.io.IOContext;
Tatu Saloranta8e8ed3e2017-05-19 17:07:48 -070010import com.fasterxml.jackson.core.json.ByteSourceJsonBootstrapper;
Tatu Saloranta81fb43c2017-05-17 18:25:34 -070011import com.fasterxml.jackson.core.sym.ByteQuadsCanonicalizer;
12import com.fasterxml.jackson.core.util.VersionUtil;
13
14public class NonBlockingJsonParser
15 extends NonBlockingJsonParserBase
16 implements ByteArrayFeeder
17{
18 /*
19 /**********************************************************************
20 /* Input source config
21 /**********************************************************************
22 */
23
24 /**
25 * This buffer is actually provided via {@link NonBlockingInputFeeder}
26 */
27 protected byte[] _inputBuffer = NO_BYTES;
28
29 /**
30 * In addition to current buffer pointer, and end pointer,
31 * we will also need to know number of bytes originally
32 * contained. This is needed to correctly update location
33 * information when the block has been completed.
34 */
35 protected int _origBufferLen;
36
37 // And from ParserBase:
38// protected int _inputPtr;
39// protected int _inputEnd;
40
41 /*
42 /**********************************************************************
Tatu Saloranta8e8ed3e2017-05-19 17:07:48 -070043 /* Location tracking, additional
44 /**********************************************************************
45 */
46
47 /**
48 * Alternate row tracker, used to keep track of position by `\r` marker
49 * (whereas <code>_currInputRow</code> tracks `\n`). Used to simplify
50 * tracking of linefeeds, assuming that input typically uses various
51 * linefeed combinations (`\r`, `\n` or `\r\n`) consistently, in which
52 * case we can simply choose max of two row candidates.
53 */
54 protected int _currInputRowAlt = 1;
55
56 /*
57 /**********************************************************************
58 /* Other state
59 /**********************************************************************
60 */
61
62 protected int _currentQuote;
63
64 /*
65 /**********************************************************************
Tatu Saloranta81fb43c2017-05-17 18:25:34 -070066 /* Life-cycle
67 /**********************************************************************
68 */
69
70 public NonBlockingJsonParser(IOContext ctxt, int parserFeatures,
71 ByteQuadsCanonicalizer sym)
72 {
73 super(ctxt, parserFeatures, sym);
74 }
75
76 /*
77 /**********************************************************************
78 /* AsyncInputFeeder impl
79 /**********************************************************************
80 */
81
82 @Override
83 public ByteArrayFeeder getNonBlockingInputFeeder() {
84 return this;
85 }
86
87 @Override
88 public final boolean needMoreInput() {
89 return (_inputPtr >=_inputEnd) && !_endOfInput;
90 }
91
92 @Override
93 public void feedInput(byte[] buf, int start, int end) throws IOException
94 {
95 // Must not have remaining input
96 if (_inputPtr < _inputEnd) {
97 _reportError("Still have %d undecoded bytes, should not call 'feedInput'", _inputEnd - _inputPtr);
98 }
99 if (end < start) {
100 _reportError("Input end (%d) may not be before start (%d)", end, start);
101 }
102 // and shouldn't have been marked as end-of-input
103 if (_endOfInput) {
104 _reportError("Already closed, can not feed more input");
105 }
106 // Time to update pointers first
107 _currInputProcessed += _origBufferLen;
108
109 // And then update buffer settings
110 _inputBuffer = buf;
111 _inputPtr = start;
112 _inputEnd = end;
113 _origBufferLen = end - start;
114 }
115
116 @Override
117 public void endOfInput() {
118 _endOfInput = true;
119 }
120
121 /*
122 /**********************************************************************
123 /* Abstract methods/overrides from JsonParser
124 /**********************************************************************
125 */
126
127 /* Implementing these methods efficiently for non-blocking cases would
128 * be complicated; so for now let's just use the default non-optimized
129 * implementation
130 */
131
132// public boolean nextFieldName(SerializableString str) throws IOException
133// public String nextTextValue() throws IOException
134// public int nextIntValue(int defaultValue) throws IOException
135// public long nextLongValue(long defaultValue) throws IOException
136// public Boolean nextBooleanValue() throws IOException
137
138 @Override
139 public int releaseBuffered(OutputStream out) throws IOException {
140 int avail = _inputEnd - _inputPtr;
141 if (avail > 0) {
142 out.write(_inputBuffer, _inputPtr, avail);
143 }
144 return avail;
145 }
146
147 /*
148 /**********************************************************************
149 /* Main-level decoding
150 /**********************************************************************
151 */
152
153 @Override
154 public JsonToken nextToken() throws IOException
155 {
156 // First: regardless of where we really are, need at least one more byte;
157 // can simplify some of the checks by short-circuiting right away
158 if (_inputPtr >= _inputEnd) {
159 if (_closed) {
160 return null;
161 }
162 // note: if so, do not even bother changing state
163 if (_endOfInput) { // except for this special case
164 return _eofAsNextToken();
165 }
166 return JsonToken.NOT_AVAILABLE;
167 }
168 // in the middle of tokenization?
169 if (_currToken == JsonToken.NOT_AVAILABLE) {
170 return _finishToken();
171 }
Tatu Saloranta81fb43c2017-05-17 18:25:34 -0700172 // No: fresh new token; may or may not have existing one
173 _numTypesValid = NR_UNKNOWN;
Tatu Saloranta8e8ed3e2017-05-19 17:07:48 -0700174 _tokenInputTotal = _currInputProcessed + _inputPtr;
Tatu Saloranta81fb43c2017-05-17 18:25:34 -0700175 // also: clear any data retained so far
176 _binaryValue = null;
Tatu Saloranta0728d422017-05-23 23:11:36 -0700177 int ch = _inputBuffer[_inputPtr++] & 0xFF;
Tatu Saloranta81fb43c2017-05-17 18:25:34 -0700178
179 switch (_majorState) {
180 case MAJOR_INITIAL:
Tatu Saloranta8e8ed3e2017-05-19 17:07:48 -0700181 return _startDocument(ch);
Tatu Saloranta81fb43c2017-05-17 18:25:34 -0700182
183 case MAJOR_ROOT:
184 return _startValue(ch);
185
Tatu Saloranta0728d422017-05-23 23:11:36 -0700186 case MAJOR_OBJECT_FIELD_FIRST: // field or end-object
Tatu Saloranta81fb43c2017-05-17 18:25:34 -0700187 // expect name
188 return _startFieldName(ch);
Tatu Saloranta0728d422017-05-23 23:11:36 -0700189 case MAJOR_OBJECT_FIELD_NEXT: // comma
190 return _startFieldNameAfterComma(ch);
191
192 case MAJOR_OBJECT_VALUE: // require semicolon first
193 return _startValueAfterColon(ch);
194
195 case MAJOR_ARRAY_ELEMENT_FIRST: // value without leading comma
Tatu Saloranta81fb43c2017-05-17 18:25:34 -0700196 return _startValue(ch);
197
Tatu Saloranta0728d422017-05-23 23:11:36 -0700198 case MAJOR_ARRAY_ELEMENT_NEXT: // require leading comma
199 return _startValueAfterComma(ch);
200
Tatu Saloranta81fb43c2017-05-17 18:25:34 -0700201 default:
202 }
203 VersionUtil.throwInternal();
204 return null;
205 }
206
207 /**
208 * Method called when a (scalar) value type has been detected, but not all of
209 * contents have been decoded due to incomplete input available.
210 */
211 protected final JsonToken _finishToken() throws IOException
212 {
213 // NOTE: caller ensures availability of at least one byte
214
215 switch (_minorState) {
Tatu Saloranta0728d422017-05-23 23:11:36 -0700216 case MINOR_VALUE_LEADING_WS:
217 return _startValue(_inputBuffer[_inputPtr++] & 0xFF);
218 case MINOR_VALUE_LEADING_COMMA:
219 return _startValueAfterComma(_inputBuffer[_inputPtr++] & 0xFF);
220 case MINOR_VALUE_LEADING_COLON:
221 return _startValueAfterColon(_inputBuffer[_inputPtr++] & 0xFF);
222
Tatu Saloranta955e5c22017-05-23 17:02:30 -0700223 case MINOR_VALUE_TOKEN_NULL:
224 return _finishKeywordToken("null", _pending32, JsonToken.VALUE_NULL);
225 case MINOR_VALUE_TOKEN_TRUE:
Tatu Saloranta0728d422017-05-23 23:11:36 -0700226 return _finishKeywordToken("true", _pending32, JsonToken.VALUE_TRUE);
Tatu Saloranta955e5c22017-05-23 17:02:30 -0700227 case MINOR_VALUE_TOKEN_FALSE:
Tatu Saloranta0728d422017-05-23 23:11:36 -0700228 return _finishKeywordToken("false", _pending32, JsonToken.VALUE_FALSE);
Tatu Saloranta955e5c22017-05-23 17:02:30 -0700229 case MINOR_VALUE_TOKEN_ERROR: // case of "almost token", just need tokenize for error
230 return _finishErrorToken();
Tatu Saloranta81fb43c2017-05-17 18:25:34 -0700231 }
232 return null;
233 }
234
235 /*
236 /**********************************************************************
237 /* Second-level decoding, root level
238 /**********************************************************************
239 */
240
Tatu Saloranta8e8ed3e2017-05-19 17:07:48 -0700241 private final JsonToken _startDocument(int ch) throws IOException
242 {
243 ch &= 0xFF;
244
245 // Very first byte: could be BOM
246 if (ch == ByteSourceJsonBootstrapper.UTF8_BOM_1) {
247 // !!! TODO
248 }
249
250 // If not BOM (or we got past it), could be whitespace or comment to skip
251 while (ch <= 0x020) {
252 if (ch != INT_SPACE) {
253 if (ch == INT_LF) {
254 ++_currInputRow;
255 _currInputRowStart = _inputPtr;
256 } else if (ch == INT_CR) {
257 ++_currInputRowAlt;
258 _currInputRowStart = _inputPtr;
259 } else if (ch != INT_TAB) {
260 _throwInvalidSpace(ch);
261 }
262 }
263 if (_inputPtr >= _inputEnd) {
Tatu Saloranta0728d422017-05-23 23:11:36 -0700264 _minorState = MINOR_ROOT_GOT_SEPARATOR;
Tatu Saloranta8e8ed3e2017-05-19 17:07:48 -0700265 if (_closed) {
266 return null;
267 }
268 // note: if so, do not even bother changing state
269 if (_endOfInput) { // except for this special case
270 return _eofAsNextToken();
271 }
272 return JsonToken.NOT_AVAILABLE;
273 }
274 ch = _inputBuffer[_inputPtr++] & 0xFF;
275 }
276 return _startValue(ch);
277 }
278
279 /*
280 /**********************************************************************
281 /* Second-level decoding, value parsing
282 /**********************************************************************
283 */
284
Tatu Saloranta81fb43c2017-05-17 18:25:34 -0700285 /**
286 * Helper method called to detect type of a value token (at any level), and possibly
287 * decode it if contained in input buffer.
Tatu Saloranta0728d422017-05-23 23:11:36 -0700288 * Value may be preceded by leading white-space, but no separator (comma).
Tatu Saloranta81fb43c2017-05-17 18:25:34 -0700289 */
290 private final JsonToken _startValue(int ch) throws IOException
291 {
Tatu Saloranta0728d422017-05-23 23:11:36 -0700292 // First: any leading white space?
293 if (ch <= 0x0020) {
294 ch = _skipWS(ch);
295 if (ch <= 0) {
296 _minorState = MINOR_VALUE_LEADING_WS;
297 return _currToken;
298 }
299 }
300
Tatu Saloranta8e8ed3e2017-05-19 17:07:48 -0700301 if (ch == INT_QUOTE) {
302 return _startString(ch);
303 }
304 switch (ch) {
305 case '-':
306 return _startNegativeNumber();
307
308 // Should we have separate handling for plus? Although
309 // it is not allowed per se, it may be erroneously used,
310 // and could be indicate by a more specific error message.
311 case '0':
312 case '1':
313 case '2':
314 case '3':
315 case '4':
316 case '5':
317 case '6':
318 case '7':
319 case '8':
320 case '9':
321 return _startPositiveNumber(ch);
322 case 'f':
323 return _startFalseToken();
324 case 'n':
325 return _startNullToken();
326 case 't':
327 return _startTrueToken();
328 case '[':
329 return _startArrayScope();
330 case ']':
331 return _closeArrayScope();
332 case '{':
333 return _startObjectScope();
334 case '}':
335 return _closeObjectScope();
336 default:
337 }
338 return _startUnexpectedValue(ch);
339 }
340
Tatu Saloranta0728d422017-05-23 23:11:36 -0700341 /**
342 * Helper method called to parse token that is either a value token in array
343 * or end-array marker
344 */
345 private final JsonToken _startValueAfterComma(int ch) throws IOException
346 {
347 // First: any leading white space?
348 if (ch <= 0x0020) {
349 ch = _skipWS(ch);
350 if (ch <= 0) {
351 _minorState = MINOR_VALUE_LEADING_COMMA;
352 return _currToken;
353 }
354 }
355 if (ch != INT_COMMA) {
356 if (ch == INT_RBRACKET) {
357 return _closeArrayScope();
358 }
359 _reportUnexpectedChar(ch, "was expecting comma to separate "+_parsingContext.typeDesc()+" entries");
360 }
361 int ptr = _inputPtr;
362 if (ptr >= _inputEnd) {
363 _minorState = MINOR_VALUE_LEADING_WS;
364 return (_currToken = JsonToken.NOT_AVAILABLE);
365 }
366 ch = _inputBuffer[ptr];
367 _inputPtr = ptr+1;
368 if (ch <= 0x0020) {
369 ch = _skipWS(ch);
370 if (ch <= 0) {
371 _minorState = MINOR_VALUE_LEADING_WS;
372 return _currToken;
373 }
374 }
375 if (ch == INT_QUOTE) {
376 return _startString(ch);
377 }
378 switch (ch) {
379 case '-':
380 return _startNegativeNumber();
381
382 // Should we have separate handling for plus? Although
383 // it is not allowed per se, it may be erroneously used,
384 // and could be indicate by a more specific error message.
385 case '0': case '1':
386 case '2': case '3':
387 case '4': case '5':
388 case '6': case '7':
389 case '8': case '9':
390 return _startPositiveNumber(ch);
391 case 'f':
392 return _startFalseToken();
393 case 'n':
394 return _startNullToken();
395 case 't':
396 return _startTrueToken();
397 case '[':
398 return _startArrayScope();
399 case ']':
400 return _closeArrayScope();
401 case '{':
402 return _startObjectScope();
403 case '}':
404 return _closeObjectScope();
405 default:
406 }
407 return _startUnexpectedValue(ch);
408 }
409
410 /**
411 * Helper method called to detect type of a value token (at any level), and possibly
412 * decode it if contained in input buffer.
413 * Value MUST be preceded by a semi-colon (which may be surrounded by white-space)
414 */
415 private final JsonToken _startValueAfterColon(int ch) throws IOException
416 {
417 // First: any leading white space?
418 if (ch <= 0x0020) {
419 ch = _skipWS(ch);
420 if (ch <= 0) {
421 _minorState = MINOR_VALUE_LEADING_COLON;
422 return _currToken;
423 }
424 }
425 if (ch != INT_COLON) {
426 _reportUnexpectedChar(ch, "was expecting a colon to separate field name and value");
427 }
428 int ptr = _inputPtr;
429 if (ptr >= _inputEnd) {
430 _minorState = MINOR_VALUE_LEADING_WS;
431 return (_currToken = JsonToken.NOT_AVAILABLE);
432 }
433 ch = _inputBuffer[ptr];
434 _inputPtr = ptr+1;
435 if (ch <= 0x0020) {
436 ch = _skipWS(ch);
437 if (ch <= 0) {
438 _minorState = MINOR_VALUE_LEADING_WS;
439 return _currToken;
440 }
441 }
442 if (ch == INT_QUOTE) {
443 return _startString(ch);
444 }
445 switch (ch) {
446 case '-':
447 return _startNegativeNumber();
448
449 // Should we have separate handling for plus? Although
450 // it is not allowed per se, it may be erroneously used,
451 // and could be indicate by a more specific error message.
452 case '0': case '1':
453 case '2': case '3':
454 case '4': case '5':
455 case '6': case '7':
456 case '8': case '9':
457 return _startPositiveNumber(ch);
458 case 'f':
459 return _startFalseToken();
460 case 'n':
461 return _startNullToken();
462 case 't':
463 return _startTrueToken();
464 case '[':
465 return _startArrayScope();
466 case ']':
467 return _closeArrayScope();
468 case '{':
469 return _startObjectScope();
470 case '}':
471 return _closeObjectScope();
472 default:
473 }
474 return _startUnexpectedValue(ch);
475 }
476
477
Tatu Saloranta8e8ed3e2017-05-19 17:07:48 -0700478 protected JsonToken _startUnexpectedValue(int ch) throws IOException
479 {
480 // TODO: Maybe support non-standard tokens that streaming parser does:
481 //
482 // * NaN
483 // * Infinity
484 // * Plus-prefix for numbers
485 // * Apostrophe for Strings
486
487 switch (ch) {
488 case '\'':
489 return _startString(ch);
490
491 case ',':
492 // If Feature.ALLOW_MISSING_VALUES is enabled we may allow "missing values",
493 // that is, encountering a trailing comma or closing marker where value would be expected
494 if (!_parsingContext.inObject() && isEnabled(Feature.ALLOW_MISSING_VALUES)) {
495 // Important to "push back" separator, to be consumed before next value;
496 // does not lead to infinite loop
497 --_inputPtr;
498 return _valueComplete(JsonToken.VALUE_NULL);
499 }
500 break;
501 }
502 // !!! TODO: maybe try to collect more information for better diagnostics
503 _reportUnexpectedChar(ch, "expected a valid value (number, String, array, object, 'true', 'false' or 'null')");
504 return null;
505 }
506
Tatu Saloranta0728d422017-05-23 23:11:36 -0700507 private final int _skipWS(int ch) throws IOException
508 {
509 do {
510 if (ch != INT_SPACE) {
511 if (ch == INT_LF) {
512 ++_currInputRow;
513 _currInputRowStart = _inputPtr;
514 } else if (ch == INT_CR) {
515 ++_currInputRowAlt;
516 _currInputRowStart = _inputPtr;
517 } else if (ch != INT_TAB) {
518 _throwInvalidSpace(ch);
519 }
520 }
521 if (_inputPtr >= _inputEnd) {
522 if (_endOfInput) { // except for this special case
523 _eofAsNextToken();
524 } else {
525 _currToken = JsonToken.NOT_AVAILABLE;
526 }
527 return 0;
528 }
529 ch = _inputBuffer[_inputPtr++] & 0xFF;
530 } while (ch <= 0x0020);
531 return ch;
532 }
533
Tatu Saloranta8e8ed3e2017-05-19 17:07:48 -0700534 /*
535 /**********************************************************************
536 /* Second-level decoding, simple tokens
537 /**********************************************************************
538 */
539
540 protected JsonToken _startFalseToken() throws IOException
541 {
Tatu Saloranta955e5c22017-05-23 17:02:30 -0700542 int ptr = _inputPtr;
543 if ((ptr + 4) < _inputEnd) { // yes, can determine efficiently
544 byte[] buf = _inputBuffer;
545 if ((buf[ptr++] == 'a')
546 && (buf[ptr++] == 'l')
547 && (buf[ptr++] == 's')
548 && (buf[ptr++] == 'e')) {
549 int ch = buf[ptr] & 0xFF;
550 if (ch < INT_0 || (ch == INT_RBRACKET) || (ch == INT_RCURLY)) { // expected/allowed chars
551 _inputPtr = ptr;
552 return _valueComplete(JsonToken.VALUE_FALSE);
553 }
554 }
555 }
556 _minorState = MINOR_VALUE_TOKEN_FALSE;
557 return _finishKeywordToken("false", 1, JsonToken.VALUE_FALSE);
Tatu Saloranta8e8ed3e2017-05-19 17:07:48 -0700558 }
559
560 protected JsonToken _startTrueToken() throws IOException
561 {
Tatu Saloranta955e5c22017-05-23 17:02:30 -0700562 int ptr = _inputPtr;
563 if ((ptr + 3) < _inputEnd) { // yes, can determine efficiently
564 byte[] buf = _inputBuffer;
565 if ((buf[ptr++] == 'r')
566 && (buf[ptr++] == 'u')
567 && (buf[ptr++] == 'e')) {
568 int ch = buf[ptr] & 0xFF;
569 if (ch < INT_0 || (ch == INT_RBRACKET) || (ch == INT_RCURLY)) { // expected/allowed chars
570 _inputPtr = ptr;
571 return _valueComplete(JsonToken.VALUE_TRUE);
572 }
573 }
574 }
575 _minorState = MINOR_VALUE_TOKEN_TRUE;
576 return _finishKeywordToken("true", 1, JsonToken.VALUE_TRUE);
Tatu Saloranta8e8ed3e2017-05-19 17:07:48 -0700577 }
578
579 protected JsonToken _startNullToken() throws IOException
580 {
Tatu Saloranta955e5c22017-05-23 17:02:30 -0700581 int ptr = _inputPtr;
582 if ((ptr + 3) < _inputEnd) { // yes, can determine efficiently
583 byte[] buf = _inputBuffer;
584 if ((buf[ptr++] == 'u')
585 && (buf[ptr++] == 'l')
586 && (buf[ptr++] == 'l')) {
587 int ch = buf[ptr] & 0xFF;
588 if (ch < INT_0 || (ch == INT_RBRACKET) || (ch == INT_RCURLY)) { // expected/allowed chars
589 _inputPtr = ptr;
590 return _valueComplete(JsonToken.VALUE_NULL);
591 }
592 }
593 }
Tatu Saloranta0728d422017-05-23 23:11:36 -0700594 _minorState = MINOR_VALUE_TOKEN_NULL;
Tatu Saloranta955e5c22017-05-23 17:02:30 -0700595 return _finishKeywordToken("null", 1, JsonToken.VALUE_NULL);
596 }
597
598 protected JsonToken _finishKeywordToken(String expToken, int matched,
599 JsonToken result) throws IOException
600 {
601 final int end = expToken.length();
602
603 while (true) {
604 if (_inputPtr >= _inputEnd) {
605 _pending32 = matched;
606 return (_currToken = JsonToken.NOT_AVAILABLE);
607 }
608 int ch = _inputBuffer[_inputPtr] & 0xFF;
609 if (matched == end) { // need to verify trailing separator
610 if (ch < INT_0 || (ch == INT_RBRACKET) || (ch == INT_RCURLY)) { // expected/allowed chars
Tatu Saloranta0728d422017-05-23 23:11:36 -0700611 return _valueComplete(result);
Tatu Saloranta955e5c22017-05-23 17:02:30 -0700612 }
Tatu Saloranta0728d422017-05-23 23:11:36 -0700613 break;
Tatu Saloranta955e5c22017-05-23 17:02:30 -0700614 }
615 if (ch != expToken.charAt(matched)) {
616 break;
617 }
Tatu Saloranta0728d422017-05-23 23:11:36 -0700618 ++matched;
Tatu Saloranta955e5c22017-05-23 17:02:30 -0700619 ++_inputPtr;
620 }
621 _minorState = MINOR_VALUE_TOKEN_ERROR;
Tatu Saloranta0728d422017-05-23 23:11:36 -0700622 _textBuffer.resetWithCopy(expToken, 0, matched);
Tatu Saloranta955e5c22017-05-23 17:02:30 -0700623 return _finishErrorToken();
624 }
625
626 protected JsonToken _finishErrorToken() throws IOException
627 {
628 while (_inputPtr < _inputEnd) {
629 int i = (int) _inputBuffer[_inputPtr++];
630
631// !!! TODO: Decode UTF-8 characters properly...
632// char c = (char) _decodeCharForError(i);
633
634 char ch = (char) i;
635 if (Character.isJavaIdentifierPart(ch)) {
636 // 11-Jan-2016, tatu: note: we will fully consume the character,
637 // included or not, so if recovery was possible, it'd be off-by-one...
638 _textBuffer.append(ch);
639 if (_textBuffer.size() < MAX_ERROR_TOKEN_LENGTH) {
640 continue;
641 }
642 }
643 _reportError("Unrecognized token '%s': was expecting %s", _textBuffer.contentsAsString(),
644 "'null', 'true' or 'false'");
645 }
646 return (_currToken = JsonToken.NOT_AVAILABLE);
Tatu Saloranta8e8ed3e2017-05-19 17:07:48 -0700647 }
648
649 /*
650 /**********************************************************************
651 /* Second-level decoding, String decoding
652 /**********************************************************************
653 */
654
655 protected JsonToken _startString(int q) throws IOException
656 {
657 _currentQuote = q;
658 return null;
659 }
660
661 /*
662 /**********************************************************************
663 /* Second-level decoding, String decoding
664 /**********************************************************************
665 */
666
667 protected JsonToken _startPositiveNumber(int ch) throws IOException
668 {
669 return null;
670 }
671
672 protected JsonToken _startNegativeNumber() throws IOException
673 {
Tatu Saloranta81fb43c2017-05-17 18:25:34 -0700674 return null;
675 }
676
677 /*
678 /**********************************************************************
679 /* Second-level decoding, Name decoding
680 /**********************************************************************
681 */
682
683 /**
684 * Method that handles initial token type recognition for token
685 * that has to be either FIELD_NAME or END_OBJECT.
686 */
687 protected final JsonToken _startFieldName(int ch) throws IOException
688 {
689 return null;
690 }
Tatu Saloranta0728d422017-05-23 23:11:36 -0700691
692 protected final JsonToken _startFieldNameAfterComma(int ch) throws IOException
693 {
694 return null;
695 }
Tatu Saloranta81fb43c2017-05-17 18:25:34 -0700696}