1
16
17 package com.google.gson.stream;
18
19 import com.google.gson.internal.JsonReaderInternalAccess;
20 import com.google.gson.internal.bind.JsonTreeReader;
21 import java.io.Closeable;
22 import java.io.EOFException;
23 import java.io.IOException;
24 import java.io.Reader;
25 import java.util.Arrays;
26
27
191 public class JsonReader implements Closeable {
192
193 private static final char[] NON_EXECUTE_PREFIX = ")]}'\n".toCharArray();
194 private static final long MIN_INCOMPLETE_INTEGER = Long.MIN_VALUE / 10;
195
196 private static final int PEEKED_NONE = 0;
197 private static final int PEEKED_BEGIN_OBJECT = 1;
198 private static final int PEEKED_END_OBJECT = 2;
199 private static final int PEEKED_BEGIN_ARRAY = 3;
200 private static final int PEEKED_END_ARRAY = 4;
201 private static final int PEEKED_TRUE = 5;
202 private static final int PEEKED_FALSE = 6;
203 private static final int PEEKED_NULL = 7;
204 private static final int PEEKED_SINGLE_QUOTED = 8;
205 private static final int PEEKED_DOUBLE_QUOTED = 9;
206 private static final int PEEKED_UNQUOTED = 10;
207
208 private static final int PEEKED_BUFFERED = 11;
209 private static final int PEEKED_SINGLE_QUOTED_NAME = 12;
210 private static final int PEEKED_DOUBLE_QUOTED_NAME = 13;
211 private static final int PEEKED_UNQUOTED_NAME = 14;
212
213 private static final int PEEKED_LONG = 15;
214 private static final int PEEKED_NUMBER = 16;
215 private static final int PEEKED_EOF = 17;
216
217
218 private static final int NUMBER_CHAR_NONE = 0;
219 private static final int NUMBER_CHAR_SIGN = 1;
220 private static final int NUMBER_CHAR_DIGIT = 2;
221 private static final int NUMBER_CHAR_DECIMAL = 3;
222 private static final int NUMBER_CHAR_FRACTION_DIGIT = 4;
223 private static final int NUMBER_CHAR_EXP_E = 5;
224 private static final int NUMBER_CHAR_EXP_SIGN = 6;
225 private static final int NUMBER_CHAR_EXP_DIGIT = 7;
226
227
228 private final Reader in;
229
230
231 private boolean lenient = false;
232
233
239 private final char[] buffer = new char[1024];
240 private int pos = 0;
241 private int limit = 0;
242
243 private int lineNumber = 0;
244 private int lineStart = 0;
245
246 int peeked = PEEKED_NONE;
247
248
252 private long peekedLong;
253
254
258 private int peekedNumberLength;
259
260
265 private String peekedString;
266
267
270 private int[] stack = new int[32];
271 private int stackSize = 0;
272 {
273 stack[stackSize++] = JsonScope.EMPTY_DOCUMENT;
274 }
275
276
284 private String[] pathNames = new String[32];
285 private int[] pathIndices = new int[32];
286
287
290 public JsonReader(Reader in) {
291 if (in == null) {
292 throw new NullPointerException("in == null");
293 }
294 this.in = in;
295 }
296
297
326 public final void setLenient(boolean lenient) {
327 this.lenient = lenient;
328 }
329
330
333 public final boolean isLenient() {
334 return lenient;
335 }
336
337
341 public void beginArray() throws IOException {
342 int p = peeked;
343 if (p == PEEKED_NONE) {
344 p = doPeek();
345 }
346 if (p == PEEKED_BEGIN_ARRAY) {
347 push(JsonScope.EMPTY_ARRAY);
348 pathIndices[stackSize - 1] = 0;
349 peeked = PEEKED_NONE;
350 } else {
351 throw new IllegalStateException("Expected BEGIN_ARRAY but was " + peek() + locationString());
352 }
353 }
354
355
359 public void endArray() throws IOException {
360 int p = peeked;
361 if (p == PEEKED_NONE) {
362 p = doPeek();
363 }
364 if (p == PEEKED_END_ARRAY) {
365 stackSize--;
366 pathIndices[stackSize - 1]++;
367 peeked = PEEKED_NONE;
368 } else {
369 throw new IllegalStateException("Expected END_ARRAY but was " + peek() + locationString());
370 }
371 }
372
373
377 public void beginObject() throws IOException {
378 int p = peeked;
379 if (p == PEEKED_NONE) {
380 p = doPeek();
381 }
382 if (p == PEEKED_BEGIN_OBJECT) {
383 push(JsonScope.EMPTY_OBJECT);
384 peeked = PEEKED_NONE;
385 } else {
386 throw new IllegalStateException("Expected BEGIN_OBJECT but was " + peek() + locationString());
387 }
388 }
389
390
394 public void endObject() throws IOException {
395 int p = peeked;
396 if (p == PEEKED_NONE) {
397 p = doPeek();
398 }
399 if (p == PEEKED_END_OBJECT) {
400 stackSize--;
401 pathNames[stackSize] = null;
402 pathIndices[stackSize - 1]++;
403 peeked = PEEKED_NONE;
404 } else {
405 throw new IllegalStateException("Expected END_OBJECT but was " + peek() + locationString());
406 }
407 }
408
409
412 public boolean hasNext() throws IOException {
413 int p = peeked;
414 if (p == PEEKED_NONE) {
415 p = doPeek();
416 }
417 return p != PEEKED_END_OBJECT && p != PEEKED_END_ARRAY;
418 }
419
420
423 public JsonToken peek() throws IOException {
424 int p = peeked;
425 if (p == PEEKED_NONE) {
426 p = doPeek();
427 }
428
429 switch (p) {
430 case PEEKED_BEGIN_OBJECT:
431 return JsonToken.BEGIN_OBJECT;
432 case PEEKED_END_OBJECT:
433 return JsonToken.END_OBJECT;
434 case PEEKED_BEGIN_ARRAY:
435 return JsonToken.BEGIN_ARRAY;
436 case PEEKED_END_ARRAY:
437 return JsonToken.END_ARRAY;
438 case PEEKED_SINGLE_QUOTED_NAME:
439 case PEEKED_DOUBLE_QUOTED_NAME:
440 case PEEKED_UNQUOTED_NAME:
441 return JsonToken.NAME;
442 case PEEKED_TRUE:
443 case PEEKED_FALSE:
444 return JsonToken.BOOLEAN;
445 case PEEKED_NULL:
446 return JsonToken.NULL;
447 case PEEKED_SINGLE_QUOTED:
448 case PEEKED_DOUBLE_QUOTED:
449 case PEEKED_UNQUOTED:
450 case PEEKED_BUFFERED:
451 return JsonToken.STRING;
452 case PEEKED_LONG:
453 case PEEKED_NUMBER:
454 return JsonToken.NUMBER;
455 case PEEKED_EOF:
456 return JsonToken.END_DOCUMENT;
457 default:
458 throw new AssertionError();
459 }
460 }
461
462 int doPeek() throws IOException {
463 int peekStack = stack[stackSize - 1];
464 if (peekStack == JsonScope.EMPTY_ARRAY) {
465 stack[stackSize - 1] = JsonScope.NONEMPTY_ARRAY;
466 } else if (peekStack == JsonScope.NONEMPTY_ARRAY) {
467
468 int c = nextNonWhitespace(true);
469 switch (c) {
470 case ']':
471 return peeked = PEEKED_END_ARRAY;
472 case ';':
473 checkLenient();
474 case ',':
475 break;
476 default:
477 throw syntaxError("Unterminated array");
478 }
479 } else if (peekStack == JsonScope.EMPTY_OBJECT || peekStack == JsonScope.NONEMPTY_OBJECT) {
480 stack[stackSize - 1] = JsonScope.DANGLING_NAME;
481
482 if (peekStack == JsonScope.NONEMPTY_OBJECT) {
483 int c = nextNonWhitespace(true);
484 switch (c) {
485 case '}':
486 return peeked = PEEKED_END_OBJECT;
487 case ';':
488 checkLenient();
489 case ',':
490 break;
491 default:
492 throw syntaxError("Unterminated object");
493 }
494 }
495 int c = nextNonWhitespace(true);
496 switch (c) {
497 case '"':
498 return peeked = PEEKED_DOUBLE_QUOTED_NAME;
499 case '\'':
500 checkLenient();
501 return peeked = PEEKED_SINGLE_QUOTED_NAME;
502 case '}':
503 if (peekStack != JsonScope.NONEMPTY_OBJECT) {
504 return peeked = PEEKED_END_OBJECT;
505 } else {
506 throw syntaxError("Expected name");
507 }
508 default:
509 checkLenient();
510 pos--;
511 if (isLiteral((char) c)) {
512 return peeked = PEEKED_UNQUOTED_NAME;
513 } else {
514 throw syntaxError("Expected name");
515 }
516 }
517 } else if (peekStack == JsonScope.DANGLING_NAME) {
518 stack[stackSize - 1] = JsonScope.NONEMPTY_OBJECT;
519
520 int c = nextNonWhitespace(true);
521 switch (c) {
522 case ':':
523 break;
524 case '=':
525 checkLenient();
526 if ((pos < limit || fillBuffer(1)) && buffer[pos] == '>') {
527 pos++;
528 }
529 break;
530 default:
531 throw syntaxError("Expected ':'");
532 }
533 } else if (peekStack == JsonScope.EMPTY_DOCUMENT) {
534 if (lenient) {
535 consumeNonExecutePrefix();
536 }
537 stack[stackSize - 1] = JsonScope.NONEMPTY_DOCUMENT;
538 } else if (peekStack == JsonScope.NONEMPTY_DOCUMENT) {
539 int c = nextNonWhitespace(false);
540 if (c == -1) {
541 return peeked = PEEKED_EOF;
542 } else {
543 checkLenient();
544 pos--;
545 }
546 } else if (peekStack == JsonScope.CLOSED) {
547 throw new IllegalStateException("JsonReader is closed");
548 }
549
550 int c = nextNonWhitespace(true);
551 switch (c) {
552 case ']':
553 if (peekStack == JsonScope.EMPTY_ARRAY) {
554 return peeked = PEEKED_END_ARRAY;
555 }
556
557 case ';':
558 case ',':
559
560 if (peekStack == JsonScope.EMPTY_ARRAY || peekStack == JsonScope.NONEMPTY_ARRAY) {
561 checkLenient();
562 pos--;
563 return peeked = PEEKED_NULL;
564 } else {
565 throw syntaxError("Unexpected value");
566 }
567 case '\'':
568 checkLenient();
569 return peeked = PEEKED_SINGLE_QUOTED;
570 case '"':
571 return peeked = PEEKED_DOUBLE_QUOTED;
572 case '[':
573 return peeked = PEEKED_BEGIN_ARRAY;
574 case '{':
575 return peeked = PEEKED_BEGIN_OBJECT;
576 default:
577 pos--;
578 }
579
580 int result = peekKeyword();
581 if (result != PEEKED_NONE) {
582 return result;
583 }
584
585 result = peekNumber();
586 if (result != PEEKED_NONE) {
587 return result;
588 }
589
590 if (!isLiteral(buffer[pos])) {
591 throw syntaxError("Expected value");
592 }
593
594 checkLenient();
595 return peeked = PEEKED_UNQUOTED;
596 }
597
598 private int peekKeyword() throws IOException {
599
600 char c = buffer[pos];
601 String keyword;
602 String keywordUpper;
603 int peeking;
604 if (c == 't' || c == 'T') {
605 keyword = "true";
606 keywordUpper = "TRUE";
607 peeking = PEEKED_TRUE;
608 } else if (c == 'f' || c == 'F') {
609 keyword = "false";
610 keywordUpper = "FALSE";
611 peeking = PEEKED_FALSE;
612 } else if (c == 'n' || c == 'N') {
613 keyword = "null";
614 keywordUpper = "NULL";
615 peeking = PEEKED_NULL;
616 } else {
617 return PEEKED_NONE;
618 }
619
620
621 int length = keyword.length();
622 for (int i = 1; i < length; i++) {
623 if (pos + i >= limit && !fillBuffer(i + 1)) {
624 return PEEKED_NONE;
625 }
626 c = buffer[pos + i];
627 if (c != keyword.charAt(i) && c != keywordUpper.charAt(i)) {
628 return PEEKED_NONE;
629 }
630 }
631
632 if ((pos + length < limit || fillBuffer(length + 1))
633 && isLiteral(buffer[pos + length])) {
634 return PEEKED_NONE;
635 }
636
637
638 pos += length;
639 return peeked = peeking;
640 }
641
642 private int peekNumber() throws IOException {
643
644 char[] buffer = this.buffer;
645 int p = pos;
646 int l = limit;
647
648 long value = 0;
649 boolean negative = false;
650 boolean fitsInLong = true;
651 int last = NUMBER_CHAR_NONE;
652
653 int i = 0;
654
655 charactersOfNumber:
656 for (; true; i++) {
657 if (p + i == l) {
658 if (i == buffer.length) {
659
660
661 return PEEKED_NONE;
662 }
663 if (!fillBuffer(i + 1)) {
664 break;
665 }
666 p = pos;
667 l = limit;
668 }
669
670 char c = buffer[p + i];
671 switch (c) {
672 case '-':
673 if (last == NUMBER_CHAR_NONE) {
674 negative = true;
675 last = NUMBER_CHAR_SIGN;
676 continue;
677 } else if (last == NUMBER_CHAR_EXP_E) {
678 last = NUMBER_CHAR_EXP_SIGN;
679 continue;
680 }
681 return PEEKED_NONE;
682
683 case '+':
684 if (last == NUMBER_CHAR_EXP_E) {
685 last = NUMBER_CHAR_EXP_SIGN;
686 continue;
687 }
688 return PEEKED_NONE;
689
690 case 'e':
691 case 'E':
692 if (last == NUMBER_CHAR_DIGIT || last == NUMBER_CHAR_FRACTION_DIGIT) {
693 last = NUMBER_CHAR_EXP_E;
694 continue;
695 }
696 return PEEKED_NONE;
697
698 case '.':
699 if (last == NUMBER_CHAR_DIGIT) {
700 last = NUMBER_CHAR_DECIMAL;
701 continue;
702 }
703 return PEEKED_NONE;
704
705 default:
706 if (c < '0' || c > '9') {
707 if (!isLiteral(c)) {
708 break charactersOfNumber;
709 }
710 return PEEKED_NONE;
711 }
712 if (last == NUMBER_CHAR_SIGN || last == NUMBER_CHAR_NONE) {
713 value = -(c - '0');
714 last = NUMBER_CHAR_DIGIT;
715 } else if (last == NUMBER_CHAR_DIGIT) {
716 if (value == 0) {
717 return PEEKED_NONE;
718 }
719 long newValue = value * 10 - (c - '0');
720 fitsInLong &= value > MIN_INCOMPLETE_INTEGER
721 || (value == MIN_INCOMPLETE_INTEGER && newValue < value);
722 value = newValue;
723 } else if (last == NUMBER_CHAR_DECIMAL) {
724 last = NUMBER_CHAR_FRACTION_DIGIT;
725 } else if (last == NUMBER_CHAR_EXP_E || last == NUMBER_CHAR_EXP_SIGN) {
726 last = NUMBER_CHAR_EXP_DIGIT;
727 }
728 }
729 }
730
731
732 if (last == NUMBER_CHAR_DIGIT && fitsInLong && (value != Long.MIN_VALUE || negative) && (value!=0 || false==negative)) {
733 peekedLong = negative ? value : -value;
734 pos += i;
735 return peeked = PEEKED_LONG;
736 } else if (last == NUMBER_CHAR_DIGIT || last == NUMBER_CHAR_FRACTION_DIGIT
737 || last == NUMBER_CHAR_EXP_DIGIT) {
738 peekedNumberLength = i;
739 return peeked = PEEKED_NUMBER;
740 } else {
741 return PEEKED_NONE;
742 }
743 }
744
745 private boolean isLiteral(char c) throws IOException {
746 switch (c) {
747 case '/':
748 case '\\':
749 case ';':
750 case '#':
751 case '=':
752 checkLenient();
753 case '{':
754 case '}':
755 case '[':
756 case ']':
757 case ':':
758 case ',':
759 case ' ':
760 case '\t':
761 case '\f':
762 case '\r':
763 case '\n':
764 return false;
765 default:
766 return true;
767 }
768 }
769
770
777 public String nextName() throws IOException {
778 int p = peeked;
779 if (p == PEEKED_NONE) {
780 p = doPeek();
781 }
782 String result;
783 if (p == PEEKED_UNQUOTED_NAME) {
784 result = nextUnquotedValue();
785 } else if (p == PEEKED_SINGLE_QUOTED_NAME) {
786 result = nextQuotedValue('\'');
787 } else if (p == PEEKED_DOUBLE_QUOTED_NAME) {
788 result = nextQuotedValue('"');
789 } else {
790 throw new IllegalStateException("Expected a name but was " + peek() + locationString());
791 }
792 peeked = PEEKED_NONE;
793 pathNames[stackSize - 1] = result;
794 return result;
795 }
796
797
805 public String nextString() throws IOException {
806 int p = peeked;
807 if (p == PEEKED_NONE) {
808 p = doPeek();
809 }
810 String result;
811 if (p == PEEKED_UNQUOTED) {
812 result = nextUnquotedValue();
813 } else if (p == PEEKED_SINGLE_QUOTED) {
814 result = nextQuotedValue('\'');
815 } else if (p == PEEKED_DOUBLE_QUOTED) {
816 result = nextQuotedValue('"');
817 } else if (p == PEEKED_BUFFERED) {
818 result = peekedString;
819 peekedString = null;
820 } else if (p == PEEKED_LONG) {
821 result = Long.toString(peekedLong);
822 } else if (p == PEEKED_NUMBER) {
823 result = new String(buffer, pos, peekedNumberLength);
824 pos += peekedNumberLength;
825 } else {
826 throw new IllegalStateException("Expected a string but was " + peek() + locationString());
827 }
828 peeked = PEEKED_NONE;
829 pathIndices[stackSize - 1]++;
830 return result;
831 }
832
833
840 public boolean nextBoolean() throws IOException {
841 int p = peeked;
842 if (p == PEEKED_NONE) {
843 p = doPeek();
844 }
845 if (p == PEEKED_TRUE) {
846 peeked = PEEKED_NONE;
847 pathIndices[stackSize - 1]++;
848 return true;
849 } else if (p == PEEKED_FALSE) {
850 peeked = PEEKED_NONE;
851 pathIndices[stackSize - 1]++;
852 return false;
853 }
854 throw new IllegalStateException("Expected a boolean but was " + peek() + locationString());
855 }
856
857
864 public void nextNull() throws IOException {
865 int p = peeked;
866 if (p == PEEKED_NONE) {
867 p = doPeek();
868 }
869 if (p == PEEKED_NULL) {
870 peeked = PEEKED_NONE;
871 pathIndices[stackSize - 1]++;
872 } else {
873 throw new IllegalStateException("Expected null but was " + peek() + locationString());
874 }
875 }
876
877
886 public double nextDouble() throws IOException {
887 int p = peeked;
888 if (p == PEEKED_NONE) {
889 p = doPeek();
890 }
891
892 if (p == PEEKED_LONG) {
893 peeked = PEEKED_NONE;
894 pathIndices[stackSize - 1]++;
895 return (double) peekedLong;
896 }
897
898 if (p == PEEKED_NUMBER) {
899 peekedString = new String(buffer, pos, peekedNumberLength);
900 pos += peekedNumberLength;
901 } else if (p == PEEKED_SINGLE_QUOTED || p == PEEKED_DOUBLE_QUOTED) {
902 peekedString = nextQuotedValue(p == PEEKED_SINGLE_QUOTED ? '\'' : '"');
903 } else if (p == PEEKED_UNQUOTED) {
904 peekedString = nextUnquotedValue();
905 } else if (p != PEEKED_BUFFERED) {
906 throw new IllegalStateException("Expected a double but was " + peek() + locationString());
907 }
908
909 peeked = PEEKED_BUFFERED;
910 double result = Double.parseDouble(peekedString);
911 if (!lenient && (Double.isNaN(result) || Double.isInfinite(result))) {
912 throw new MalformedJsonException(
913 "JSON forbids NaN and infinities: " + result + locationString());
914 }
915 peekedString = null;
916 peeked = PEEKED_NONE;
917 pathIndices[stackSize - 1]++;
918 return result;
919 }
920
921
931 public long nextLong() throws IOException {
932 int p = peeked;
933 if (p == PEEKED_NONE) {
934 p = doPeek();
935 }
936
937 if (p == PEEKED_LONG) {
938 peeked = PEEKED_NONE;
939 pathIndices[stackSize - 1]++;
940 return peekedLong;
941 }
942
943 if (p == PEEKED_NUMBER) {
944 peekedString = new String(buffer, pos, peekedNumberLength);
945 pos += peekedNumberLength;
946 } else if (p == PEEKED_SINGLE_QUOTED || p == PEEKED_DOUBLE_QUOTED || p == PEEKED_UNQUOTED) {
947 if (p == PEEKED_UNQUOTED) {
948 peekedString = nextUnquotedValue();
949 } else {
950 peekedString = nextQuotedValue(p == PEEKED_SINGLE_QUOTED ? '\'' : '"');
951 }
952 try {
953 long result = Long.parseLong(peekedString);
954 peeked = PEEKED_NONE;
955 pathIndices[stackSize - 1]++;
956 return result;
957 } catch (NumberFormatException ignored) {
958
959 }
960 } else {
961 throw new IllegalStateException("Expected a long but was " + peek() + locationString());
962 }
963
964 peeked = PEEKED_BUFFERED;
965 double asDouble = Double.parseDouble(peekedString);
966 long result = (long) asDouble;
967 if (result != asDouble) {
968 throw new NumberFormatException("Expected a long but was " + peekedString + locationString());
969 }
970 peekedString = null;
971 peeked = PEEKED_NONE;
972 pathIndices[stackSize - 1]++;
973 return result;
974 }
975
976
986 private String nextQuotedValue(char quote) throws IOException {
987
988 char[] buffer = this.buffer;
989 StringBuilder builder = null;
990 while (true) {
991 int p = pos;
992 int l = limit;
993
994 int start = p;
995 while (p < l) {
996 int c = buffer[p++];
997
998 if (c == quote) {
999 pos = p;
1000 int len = p - start - 1;
1001 if (builder == null) {
1002 return new String(buffer, start, len);
1003 } else {
1004 builder.append(buffer, start, len);
1005 return builder.toString();
1006 }
1007 } else if (c == '\\') {
1008 pos = p;
1009 int len = p - start - 1;
1010 if (builder == null) {
1011 int estimatedLength = (len + 1) * 2;
1012 builder = new StringBuilder(Math.max(estimatedLength, 16));
1013 }
1014 builder.append(buffer, start, len);
1015 builder.append(readEscapeCharacter());
1016 p = pos;
1017 l = limit;
1018 start = p;
1019 } else if (c == '\n') {
1020 lineNumber++;
1021 lineStart = p;
1022 }
1023 }
1024
1025 if (builder == null) {
1026 int estimatedLength = (p - start) * 2;
1027 builder = new StringBuilder(Math.max(estimatedLength, 16));
1028 }
1029 builder.append(buffer, start, p - start);
1030 pos = p;
1031 if (!fillBuffer(1)) {
1032 throw syntaxError("Unterminated string");
1033 }
1034 }
1035 }
1036
1037
1040 @SuppressWarnings("fallthrough")
1041 private String nextUnquotedValue() throws IOException {
1042 StringBuilder builder = null;
1043 int i = 0;
1044
1045 findNonLiteralCharacter:
1046 while (true) {
1047 for (; pos + i < limit; i++) {
1048 switch (buffer[pos + i]) {
1049 case '/':
1050 case '\\':
1051 case ';':
1052 case '#':
1053 case '=':
1054 checkLenient();
1055 case '{':
1056 case '}':
1057 case '[':
1058 case ']':
1059 case ':':
1060 case ',':
1061 case ' ':
1062 case '\t':
1063 case '\f':
1064 case '\r':
1065 case '\n':
1066 break findNonLiteralCharacter;
1067 }
1068 }
1069
1070
1071 if (i < buffer.length) {
1072 if (fillBuffer(i + 1)) {
1073 continue;
1074 } else {
1075 break;
1076 }
1077 }
1078
1079
1080 if (builder == null) {
1081 builder = new StringBuilder(Math.max(i,16));
1082 }
1083 builder.append(buffer, pos, i);
1084 pos += i;
1085 i = 0;
1086 if (!fillBuffer(1)) {
1087 break;
1088 }
1089 }
1090
1091 String result = (null == builder) ? new String(buffer, pos, i) : builder.append(buffer, pos, i).toString();
1092 pos += i;
1093 return result;
1094 }
1095
1096 private void skipQuotedValue(char quote) throws IOException {
1097
1098 char[] buffer = this.buffer;
1099 do {
1100 int p = pos;
1101 int l = limit;
1102
1103 while (p < l) {
1104 int c = buffer[p++];
1105 if (c == quote) {
1106 pos = p;
1107 return;
1108 } else if (c == '\\') {
1109 pos = p;
1110 readEscapeCharacter();
1111 p = pos;
1112 l = limit;
1113 } else if (c == '\n') {
1114 lineNumber++;
1115 lineStart = p;
1116 }
1117 }
1118 pos = p;
1119 } while (fillBuffer(1));
1120 throw syntaxError("Unterminated string");
1121 }
1122
1123 private void skipUnquotedValue() throws IOException {
1124 do {
1125 int i = 0;
1126 for (; pos + i < limit; i++) {
1127 switch (buffer[pos + i]) {
1128 case '/':
1129 case '\\':
1130 case ';':
1131 case '#':
1132 case '=':
1133 checkLenient();
1134 case '{':
1135 case '}':
1136 case '[':
1137 case ']':
1138 case ':':
1139 case ',':
1140 case ' ':
1141 case '\t':
1142 case '\f':
1143 case '\r':
1144 case '\n':
1145 pos += i;
1146 return;
1147 }
1148 }
1149 pos += i;
1150 } while (fillBuffer(1));
1151 }
1152
1153
1163 public int nextInt() throws IOException {
1164 int p = peeked;
1165 if (p == PEEKED_NONE) {
1166 p = doPeek();
1167 }
1168
1169 int result;
1170 if (p == PEEKED_LONG) {
1171 result = (int) peekedLong;
1172 if (peekedLong != result) {
1173 throw new NumberFormatException("Expected an int but was " + peekedLong + locationString());
1174 }
1175 peeked = PEEKED_NONE;
1176 pathIndices[stackSize - 1]++;
1177 return result;
1178 }
1179
1180 if (p == PEEKED_NUMBER) {
1181 peekedString = new String(buffer, pos, peekedNumberLength);
1182 pos += peekedNumberLength;
1183 } else if (p == PEEKED_SINGLE_QUOTED || p == PEEKED_DOUBLE_QUOTED || p == PEEKED_UNQUOTED) {
1184 if (p == PEEKED_UNQUOTED) {
1185 peekedString = nextUnquotedValue();
1186 } else {
1187 peekedString = nextQuotedValue(p == PEEKED_SINGLE_QUOTED ? '\'' : '"');
1188 }
1189 try {
1190 result = Integer.parseInt(peekedString);
1191 peeked = PEEKED_NONE;
1192 pathIndices[stackSize - 1]++;
1193 return result;
1194 } catch (NumberFormatException ignored) {
1195
1196 }
1197 } else {
1198 throw new IllegalStateException("Expected an int but was " + peek() + locationString());
1199 }
1200
1201 peeked = PEEKED_BUFFERED;
1202 double asDouble = Double.parseDouble(peekedString);
1203 result = (int) asDouble;
1204 if (result != asDouble) {
1205 throw new NumberFormatException("Expected an int but was " + peekedString + locationString());
1206 }
1207 peekedString = null;
1208 peeked = PEEKED_NONE;
1209 pathIndices[stackSize - 1]++;
1210 return result;
1211 }
1212
1213
1216 public void close() throws IOException {
1217 peeked = PEEKED_NONE;
1218 stack[0] = JsonScope.CLOSED;
1219 stackSize = 1;
1220 in.close();
1221 }
1222
1223
1228 public void skipValue() throws IOException {
1229 int count = 0;
1230 do {
1231 int p = peeked;
1232 if (p == PEEKED_NONE) {
1233 p = doPeek();
1234 }
1235
1236 if (p == PEEKED_BEGIN_ARRAY) {
1237 push(JsonScope.EMPTY_ARRAY);
1238 count++;
1239 } else if (p == PEEKED_BEGIN_OBJECT) {
1240 push(JsonScope.EMPTY_OBJECT);
1241 count++;
1242 } else if (p == PEEKED_END_ARRAY) {
1243 stackSize--;
1244 count--;
1245 } else if (p == PEEKED_END_OBJECT) {
1246 stackSize--;
1247 count--;
1248 } else if (p == PEEKED_UNQUOTED_NAME || p == PEEKED_UNQUOTED) {
1249 skipUnquotedValue();
1250 } else if (p == PEEKED_SINGLE_QUOTED || p == PEEKED_SINGLE_QUOTED_NAME) {
1251 skipQuotedValue('\'');
1252 } else if (p == PEEKED_DOUBLE_QUOTED || p == PEEKED_DOUBLE_QUOTED_NAME) {
1253 skipQuotedValue('"');
1254 } else if (p == PEEKED_NUMBER) {
1255 pos += peekedNumberLength;
1256 }
1257 peeked = PEEKED_NONE;
1258 } while (count != 0);
1259
1260 pathIndices[stackSize - 1]++;
1261 pathNames[stackSize - 1] = "null";
1262 }
1263
1264 private void push(int newTop) {
1265 if (stackSize == stack.length) {
1266 int newLength = stackSize * 2;
1267 stack = Arrays.copyOf(stack, newLength);
1268 pathIndices = Arrays.copyOf(pathIndices, newLength);
1269 pathNames = Arrays.copyOf(pathNames, newLength);
1270 }
1271 stack[stackSize++] = newTop;
1272 }
1273
1274
1279 private boolean fillBuffer(int minimum) throws IOException {
1280 char[] buffer = this.buffer;
1281 lineStart -= pos;
1282 if (limit != pos) {
1283 limit -= pos;
1284 System.arraycopy(buffer, pos, buffer, 0, limit);
1285 } else {
1286 limit = 0;
1287 }
1288
1289 pos = 0;
1290 int total;
1291 while ((total = in.read(buffer, limit, buffer.length - limit)) != -1) {
1292 limit += total;
1293
1294
1295 if (lineNumber == 0 && lineStart == 0 && limit > 0 && buffer[0] == '\ufeff') {
1296 pos++;
1297 lineStart++;
1298 minimum++;
1299 }
1300
1301 if (limit >= minimum) {
1302 return true;
1303 }
1304 }
1305 return false;
1306 }
1307
1308
1314 private int nextNonWhitespace(boolean throwOnEof) throws IOException {
1315
1323 char[] buffer = this.buffer;
1324 int p = pos;
1325 int l = limit;
1326 while (true) {
1327 if (p == l) {
1328 pos = p;
1329 if (!fillBuffer(1)) {
1330 break;
1331 }
1332 p = pos;
1333 l = limit;
1334 }
1335
1336 int c = buffer[p++];
1337 if (c == '\n') {
1338 lineNumber++;
1339 lineStart = p;
1340 continue;
1341 } else if (c == ' ' || c == '\r' || c == '\t') {
1342 continue;
1343 }
1344
1345 if (c == '/') {
1346 pos = p;
1347 if (p == l) {
1348 pos--;
1349 boolean charsLoaded = fillBuffer(2);
1350 pos++;
1351 if (!charsLoaded) {
1352 return c;
1353 }
1354 }
1355
1356 checkLenient();
1357 char peek = buffer[pos];
1358 switch (peek) {
1359 case '*':
1360
1361 pos++;
1362 if (!skipTo("*/")) {
1363 throw syntaxError("Unterminated comment");
1364 }
1365 p = pos + 2;
1366 l = limit;
1367 continue;
1368
1369 case '/':
1370
1371 pos++;
1372 skipToEndOfLine();
1373 p = pos;
1374 l = limit;
1375 continue;
1376
1377 default:
1378 return c;
1379 }
1380 } else if (c == '#') {
1381 pos = p;
1382
1387 checkLenient();
1388 skipToEndOfLine();
1389 p = pos;
1390 l = limit;
1391 } else {
1392 pos = p;
1393 return c;
1394 }
1395 }
1396 if (throwOnEof) {
1397 throw new EOFException("End of input" + locationString());
1398 } else {
1399 return -1;
1400 }
1401 }
1402
1403 private void checkLenient() throws IOException {
1404 if (!lenient) {
1405 throw syntaxError("Use JsonReader.setLenient(true) to accept malformed JSON");
1406 }
1407 }
1408
1409
1414 private void skipToEndOfLine() throws IOException {
1415 while (pos < limit || fillBuffer(1)) {
1416 char c = buffer[pos++];
1417 if (c == '\n') {
1418 lineNumber++;
1419 lineStart = pos;
1420 break;
1421 } else if (c == '\r') {
1422 break;
1423 }
1424 }
1425 }
1426
1427
1430 private boolean skipTo(String toFind) throws IOException {
1431 int length = toFind.length();
1432 outer:
1433 for (; pos + length <= limit || fillBuffer(length); pos++) {
1434 if (buffer[pos] == '\n') {
1435 lineNumber++;
1436 lineStart = pos + 1;
1437 continue;
1438 }
1439 for (int c = 0; c < length; c++) {
1440 if (buffer[pos + c] != toFind.charAt(c)) {
1441 continue outer;
1442 }
1443 }
1444 return true;
1445 }
1446 return false;
1447 }
1448
1449 @Override public String toString() {
1450 return getClass().getSimpleName() + locationString();
1451 }
1452
1453 String locationString() {
1454 int line = lineNumber + 1;
1455 int column = pos - lineStart + 1;
1456 return " at line " + line + " column " + column + " path " + getPath();
1457 }
1458
1459
1463 public String getPath() {
1464 StringBuilder result = new StringBuilder().append('$');
1465 for (int i = 0, size = stackSize; i < size; i++) {
1466 switch (stack[i]) {
1467 case JsonScope.EMPTY_ARRAY:
1468 case JsonScope.NONEMPTY_ARRAY:
1469 result.append('[').append(pathIndices[i]).append(']');
1470 break;
1471
1472 case JsonScope.EMPTY_OBJECT:
1473 case JsonScope.DANGLING_NAME:
1474 case JsonScope.NONEMPTY_OBJECT:
1475 result.append('.');
1476 if (pathNames[i] != null) {
1477 result.append(pathNames[i]);
1478 }
1479 break;
1480
1481 case JsonScope.NONEMPTY_DOCUMENT:
1482 case JsonScope.EMPTY_DOCUMENT:
1483 case JsonScope.CLOSED:
1484 break;
1485 }
1486 }
1487 return result.toString();
1488 }
1489
1490
1499 private char readEscapeCharacter() throws IOException {
1500 if (pos == limit && !fillBuffer(1)) {
1501 throw syntaxError("Unterminated escape sequence");
1502 }
1503
1504 char escaped = buffer[pos++];
1505 switch (escaped) {
1506 case 'u':
1507 if (pos + 4 > limit && !fillBuffer(4)) {
1508 throw syntaxError("Unterminated escape sequence");
1509 }
1510
1511 char result = 0;
1512 for (int i = pos, end = i + 4; i < end; i++) {
1513 char c = buffer[i];
1514 result <<= 4;
1515 if (c >= '0' && c <= '9') {
1516 result += (c - '0');
1517 } else if (c >= 'a' && c <= 'f') {
1518 result += (c - 'a' + 10);
1519 } else if (c >= 'A' && c <= 'F') {
1520 result += (c - 'A' + 10);
1521 } else {
1522 throw new NumberFormatException("\\u" + new String(buffer, pos, 4));
1523 }
1524 }
1525 pos += 4;
1526 return result;
1527
1528 case 't':
1529 return '\t';
1530
1531 case 'b':
1532 return '\b';
1533
1534 case 'n':
1535 return '\n';
1536
1537 case 'r':
1538 return '\r';
1539
1540 case 'f':
1541 return '\f';
1542
1543 case '\n':
1544 lineNumber++;
1545 lineStart = pos;
1546
1547
1548 case '\'':
1549 case '"':
1550 case '\\':
1551 case '/':
1552 return escaped;
1553 default:
1554
1555 throw syntaxError("Invalid escape sequence");
1556 }
1557 }
1558
1559
1563 private IOException syntaxError(String message) throws IOException {
1564 throw new MalformedJsonException(message + locationString());
1565 }
1566
1567
1570 private void consumeNonExecutePrefix() throws IOException {
1571
1572 nextNonWhitespace(true);
1573 pos--;
1574
1575 if (pos + NON_EXECUTE_PREFIX.length > limit && !fillBuffer(NON_EXECUTE_PREFIX.length)) {
1576 return;
1577 }
1578
1579 for (int i = 0; i < NON_EXECUTE_PREFIX.length; i++) {
1580 if (buffer[pos + i] != NON_EXECUTE_PREFIX[i]) {
1581 return;
1582 }
1583 }
1584
1585
1586 pos += NON_EXECUTE_PREFIX.length;
1587 }
1588
1589 static {
1590 JsonReaderInternalAccess.INSTANCE = new JsonReaderInternalAccess() {
1591 @Override public void promoteNameToValue(JsonReader reader) throws IOException {
1592 if (reader instanceof JsonTreeReader) {
1593 ((JsonTreeReader)reader).promoteNameToValue();
1594 return;
1595 }
1596 int p = reader.peeked;
1597 if (p == PEEKED_NONE) {
1598 p = reader.doPeek();
1599 }
1600 if (p == PEEKED_DOUBLE_QUOTED_NAME) {
1601 reader.peeked = PEEKED_DOUBLE_QUOTED;
1602 } else if (p == PEEKED_SINGLE_QUOTED_NAME) {
1603 reader.peeked = PEEKED_SINGLE_QUOTED;
1604 } else if (p == PEEKED_UNQUOTED_NAME) {
1605 reader.peeked = PEEKED_UNQUOTED;
1606 } else {
1607 throw new IllegalStateException(
1608 "Expected a name but was " + reader.peek() + reader.locationString());
1609 }
1610 }
1611 };
1612 }
1613 }
1614