1 package net.minidev.json.parser;
2
3 /*
4  *    Copyright 2011 JSON-SMART authors
5  *
6  * Licensed under the Apache License, Version 2.0 (the "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  * http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  */

18 import static net.minidev.json.parser.ParseException.ERROR_UNEXPECTED_CHAR;
19 import static net.minidev.json.parser.ParseException.ERROR_UNEXPECTED_EOF;
20 import static net.minidev.json.parser.ParseException.ERROR_UNEXPECTED_LEADING_0;
21 import static net.minidev.json.parser.ParseException.ERROR_UNEXPECTED_TOKEN;
22 import static net.minidev.json.parser.ParseException.ERROR_UNEXPECTED_UNICODE;
23
24 import java.io.IOException;
25 import java.math.BigDecimal;
26 import java.math.BigInteger;
27
28 import net.minidev.json.writer.JsonReader;
29 import net.minidev.json.writer.JsonReaderI;
30
31 /**
32  * JSONParserBase is the common code between {@link JSONParserString} and
33  * {@link JSONParserReader}
34  * 
35  * @see JSONParserMemory
36  * @see JSONParserStream
37  * 
38  * @author Uriel Chemouni <uchemouni@gmail.com>
39  */

40 abstract class JSONParserBase {
41     protected char c;
42     JsonReader base;
43     public final static byte EOI = 0x1A;
44     protected static final char MAX_STOP = 126; // '}' -> 125
45     private String lastKey;
46
47     protected static boolean[] stopAll = new boolean[MAX_STOP];
48     protected static boolean[] stopArray = new boolean[MAX_STOP];
49     protected static boolean[] stopKey = new boolean[MAX_STOP];
50     protected static boolean[] stopValue = new boolean[MAX_STOP];
51     protected static boolean[] stopX = new boolean[MAX_STOP];
52
53     static {
54         stopKey[':'] = stopKey[EOI] = true;
55         stopValue[','] = stopValue['}'] = stopValue[EOI] = true;
56         stopArray[','] = stopArray[']'] = stopArray[EOI] = true;
57         stopX[EOI] = true;
58         stopAll[','] = stopAll[':'] = true;
59         stopAll[']'] = stopAll['}'] = stopAll[EOI] = true;
60     }
61
62     /*
63      * End of static declaration
64      */

65     //
66     //
67     protected final MSB sb = new MSB(15);
68     protected Object xo;
69     protected String xs;
70     protected int pos;
71
72     /*
73      * Parsing flags
74      */

75     protected final boolean acceptLeadinZero;
76     protected final boolean acceptNaN;
77     protected final boolean acceptNonQuote;
78     protected final boolean acceptSimpleQuote;
79     protected final boolean acceptUselessComma;
80     protected final boolean checkTaillingData;
81     protected final boolean checkTaillingSpace;
82     protected final boolean ignoreControlChar;
83     protected final boolean useHiPrecisionFloat;
84     protected final boolean useIntegerStorage;
85     protected final boolean reject127;
86
87     public JSONParserBase(int permissiveMode) {
88         this.acceptNaN = (permissiveMode & JSONParser.ACCEPT_NAN) > 0;
89         this.acceptNonQuote = (permissiveMode & JSONParser.ACCEPT_NON_QUOTE) > 0;
90         this.acceptSimpleQuote = (permissiveMode & JSONParser.ACCEPT_SIMPLE_QUOTE) > 0;
91         this.ignoreControlChar = (permissiveMode & JSONParser.IGNORE_CONTROL_CHAR) > 0;
92         this.useIntegerStorage = (permissiveMode & JSONParser.USE_INTEGER_STORAGE) > 0;
93         this.acceptLeadinZero = (permissiveMode & JSONParser.ACCEPT_LEADING_ZERO) > 0;
94         this.acceptUselessComma = (permissiveMode & JSONParser.ACCEPT_USELESS_COMMA) > 0;
95         this.useHiPrecisionFloat = (permissiveMode & JSONParser.USE_HI_PRECISION_FLOAT) > 0;
96         this.checkTaillingData = (permissiveMode & (JSONParser.ACCEPT_TAILLING_DATA | JSONParser.ACCEPT_TAILLING_SPACE)) != (JSONParser.ACCEPT_TAILLING_DATA | JSONParser.ACCEPT_TAILLING_SPACE);
97         this.checkTaillingSpace = (permissiveMode & JSONParser.ACCEPT_TAILLING_SPACE) == 0;
98         this.reject127 = (permissiveMode & JSONParser.REJECT_127_CHAR) > 0;
99     }
100
101     public void checkControleChar() throws ParseException {
102         if (ignoreControlChar)
103             return;
104         int l = xs.length();
105         for (int i = 0; i < l; i++) {
106             char c = xs.charAt(i);
107             if (c < 0)
108                 continue;
109             if (c <= 31)
110                 throw new ParseException(pos + i, ParseException.ERROR_UNEXPECTED_CHAR, c);
111             if (c == 127) {
112                 if (reject127)
113                     throw new ParseException(pos + i, ParseException.ERROR_UNEXPECTED_CHAR, c);
114             }
115         }
116     }
117
118     public void checkLeadinZero() throws ParseException {
119         int len = xs.length();
120         if (len == 1)
121             return;
122         if (len == 2) {
123             if (xs.equals("00"))
124                 throw new ParseException(pos, ERROR_UNEXPECTED_LEADING_0, xs);
125             return;
126         }
127         char c1 = xs.charAt(0);
128         char c2 = xs.charAt(1);
129         if (c1 == '-') {
130             char c3 = xs.charAt(2);
131             if (c2 == '0' && c3 >= '0' && c3 <= '9')
132                 throw new ParseException(pos, ERROR_UNEXPECTED_LEADING_0, xs);
133             return;
134         }
135         if (c1 == '0' && c2 >= '0' && c2 <= '9')
136             throw new ParseException(pos, ERROR_UNEXPECTED_LEADING_0, xs);
137     }
138
139     protected Number extractFloat() throws ParseException {
140         if (!acceptLeadinZero)
141             checkLeadinZero();
142         if (!useHiPrecisionFloat)
143             return Float.parseFloat(xs);
144         if (xs.length() > 18) // follow JSonIJ parsing method
145             return new BigDecimal(xs);
146         return Double.parseDouble(xs);
147     }
148
149     /**
150      * use to return Primitive Type, or String, Or JsonObject or JsonArray
151      * generated by a ContainerFactory
152      */

153     protected <T> T parse(JsonReaderI<T> mapper) throws ParseException {
154         this.pos = -1;
155         T result;
156         try {
157             read();
158             result = readFirst(mapper);
159             if (checkTaillingData) {
160                 if (!checkTaillingSpace)
161                     skipSpace();
162                 if (c != EOI)
163                     throw new ParseException(pos - 1, ERROR_UNEXPECTED_TOKEN, c);
164             }
165         } catch (IOException e) {
166             throw new ParseException(pos, e);
167         }
168         xs = null;
169         xo = null;
170         return result;
171     }
172
173     protected Number parseNumber(String s) throws ParseException {
174         // pos
175         int p = 0;
176         // len
177         int l = s.length();
178         // max pos long base 10 len
179         int max = 19;
180         boolean neg;
181
182         if (s.charAt(0) == '-') {
183             p++;
184             max++;
185             neg = true;
186             if (!acceptLeadinZero && l >= 3 && s.charAt(1) == '0')
187                 throw new ParseException(pos, ERROR_UNEXPECTED_LEADING_0, s);
188         } else {
189             neg = false;
190             if (!acceptLeadinZero && l >= 2 && s.charAt(0) == '0')
191                 throw new ParseException(pos, ERROR_UNEXPECTED_LEADING_0, s);
192         }
193
194         boolean mustCheck;
195         if (l < max) {
196             max = l;
197             mustCheck = false;
198         } else if (l > max) {
199             return new BigInteger(s, 10);
200         } else {
201             max = l - 1;
202             mustCheck = true;
203         }
204
205         long r = 0;
206         while (p < max) {
207             r = (r * 10L) + ('0' - s.charAt(p++));
208         }
209         if (mustCheck) {
210             boolean isBig;
211             if (r > -922337203685477580L) {
212                 isBig = false;
213             } else if (r < -922337203685477580L) {
214                 isBig = true;
215             } else {
216                 if (neg)
217                     isBig = (s.charAt(p) > '8');
218                 else
219                     isBig = (s.charAt(p) > '7');
220             }
221             if (isBig)
222                 return new BigInteger(s, 10);
223             r = r * 10L + ('0' - s.charAt(p));
224         }
225         if (neg) {
226             if (this.useIntegerStorage && r >= Integer.MIN_VALUE)
227                 return (int) r;
228             return r;
229         }
230         r = -r;
231         if (this.useIntegerStorage && r <= Integer.MAX_VALUE)
232             return (int) r;
233         return r;
234     }
235
236     abstract protected void read() throws IOException;
237
238     protected <T> T readArray(JsonReaderI<T> mapper) throws ParseException, IOException {
239         Object current = mapper.createArray();
240         if (c != '[')
241             throw new RuntimeException("Internal Error");
242         read();
243         boolean needData = false;
244         // special case needData is false and can close is true
245         if (c == ',' && !acceptUselessComma)
246             throw new ParseException(pos, ERROR_UNEXPECTED_CHAR, (char) c);            
247         for (;;) {
248             switch (c) {
249             case ' ':
250             case '\r':
251             case '\n':
252             case '\t':
253                 read();
254                 continue;
255             case ']':
256                 if (needData && !acceptUselessComma)
257                     throw new ParseException(pos, ERROR_UNEXPECTED_CHAR, (char) c);
258                 read(); /* unstack */
259                 //
260                 return mapper.convert(current);
261             case ':':
262             case '}':
263                 throw new ParseException(pos, ERROR_UNEXPECTED_CHAR, (char) c);
264             case ',':
265                 if (needData && !acceptUselessComma)
266                     throw new ParseException(pos, ERROR_UNEXPECTED_CHAR, (char) c);
267                 read();
268                 needData = true;
269                 continue;
270             case EOI:
271                 throw new ParseException(pos - 1, ERROR_UNEXPECTED_EOF, "EOF");
272             default:
273                 mapper.addValue(current, readMain(mapper, stopArray));
274                 needData = false;
275                 continue;
276             }
277         }
278     }
279
280     /**
281      * use to return Primitive Type, or String, Or JsonObject or JsonArray
282      * generated by a ContainerFactory
283      */

284     protected <T> T readFirst(JsonReaderI<T> mapper) throws ParseException, IOException {
285         for (;;) {
286             switch (c) {
287             // skip spaces
288             case ' ':
289             case '\r':
290             case '\n':
291             case '\t':
292                 read();
293                 continue;
294                 // invalid stats
295             case ':':
296             case '}':
297             case ']':
298                 throw new ParseException(pos, ERROR_UNEXPECTED_CHAR, c);
299                 // start object
300             case '{':
301                 return readObject(mapper);
302                 // start Array
303             case '[':
304                 return readArray(mapper);
305                 // start string
306             case '"':
307             case '\'':
308                 readString();
309                 //
310                 return mapper.convert(xs);
311                 // string or null
312             case 'n':
313                 readNQString(stopX);
314                 if ("null".equals(xs)) {
315                     //
316                     return null;
317                 }
318                 if (!acceptNonQuote)
319                     throw new ParseException(pos, ERROR_UNEXPECTED_TOKEN, xs);
320                 //
321                 return mapper.convert(xs);
322                 // string or false
323             case 'f':
324                 readNQString(stopX);
325                 if ("false".equals(xs)) {
326                     //
327                     return mapper.convert(Boolean.FALSE);
328                 }
329                 if (!acceptNonQuote)
330                     throw new ParseException(pos, ERROR_UNEXPECTED_TOKEN, xs);
331                 //
332                 return mapper.convert(xs);
333                 // string or true
334             case 't':
335                 readNQString(stopX);
336                 if ("true".equals(xs)) {
337                     //
338                     return mapper.convert(Boolean.TRUE);
339                 }
340                 if (!acceptNonQuote)
341                     throw new ParseException(pos, ERROR_UNEXPECTED_TOKEN, xs);
342                 //
343                 return mapper.convert(xs);
344                 // string or NaN
345             case 'N':
346                 readNQString(stopX);
347                 if (!acceptNaN)
348                     throw new ParseException(pos, ERROR_UNEXPECTED_TOKEN, xs);
349                 if ("NaN".equals(xs)) {
350                     //
351                     return mapper.convert(Float.valueOf(Float.NaN));
352                 }
353                 if (!acceptNonQuote)
354                     throw new ParseException(pos, ERROR_UNEXPECTED_TOKEN, xs);
355                 //
356                 return mapper.convert(xs);
357                 // digits
358             case '0':
359             case '1':
360             case '2':
361             case '3':
362             case '4':
363             case '5':
364             case '6':
365             case '7':
366             case '8':
367             case '9':
368             case '-':
369                 xo = readNumber(stopX);
370                 //
371                 return mapper.convert(xo);
372             default:
373                 readNQString(stopX);
374                 if (!acceptNonQuote)
375                     throw new ParseException(pos, ERROR_UNEXPECTED_TOKEN, xs);
376                 //
377                 return mapper.convert(xs);
378             }
379         }
380     }
381
382     /**
383      * use to return Primitive Type, or String, Or JsonObject or JsonArray
384      * generated by a ContainerFactory
385      */

386     protected Object readMain(JsonReaderI<?> mapper, boolean stop[]) throws ParseException, IOException {
387         for (;;) {
388             switch (c) {
389             // skip spaces
390             case ' ':
391             case '\r':
392             case '\n':
393             case '\t':
394                 read();
395                 continue;
396                 // invalid stats
397             case ':':
398             case '}':
399             case ']':
400                 throw new ParseException(pos, ERROR_UNEXPECTED_CHAR, c);
401                 // start object
402             case '{':
403                 return readObject(mapper.startObject(lastKey));
404                 // start Array
405             case '[':
406                 return readArray(mapper.startArray(lastKey));
407                 // start string
408             case '"':
409             case '\'':
410                 readString();
411                 //
412                 return xs;
413                 // string or null
414             case 'n':
415                 readNQString(stop);
416                 if ("null".equals(xs)) {
417                     //
418                     return null;
419                 }
420                 if (!acceptNonQuote)
421                     throw new ParseException(pos, ERROR_UNEXPECTED_TOKEN, xs);
422                 //
423                 return xs;
424                 // string or false
425             case 'f':
426                 readNQString(stop);
427                 if ("false".equals(xs)) {
428                     //
429                     return Boolean.FALSE;
430                 }
431                 if (!acceptNonQuote)
432                     throw new ParseException(pos, ERROR_UNEXPECTED_TOKEN, xs);
433                 //
434                 return xs;
435                 // string or true
436             case 't':
437                 readNQString(stop);
438                 if ("true".equals(xs)) {
439                     //
440                     return Boolean.TRUE;
441                 }
442                 if (!acceptNonQuote)
443                     throw new ParseException(pos, ERROR_UNEXPECTED_TOKEN, xs);
444                 //
445                 return xs;
446                 // string or NaN
447             case 'N':
448                 readNQString(stop);
449                 if (!acceptNaN)
450                     throw new ParseException(pos, ERROR_UNEXPECTED_TOKEN, xs);
451                 if ("NaN".equals(xs)) {
452                     //
453                     return Float.valueOf(Float.NaN);
454                 }
455                 if (!acceptNonQuote)
456                     throw new ParseException(pos, ERROR_UNEXPECTED_TOKEN, xs);
457                 //
458                 return xs;
459                 // digits
460             case '0':
461             case '1':
462             case '2':
463             case '3':
464             case '4':
465             case '5':
466             case '6':
467             case '7':
468             case '8':
469             case '9':
470             case '-':
471                 //
472                 //
473                 return readNumber(stop);
474             default:
475                 readNQString(stop);
476                 if (!acceptNonQuote)
477                     throw new ParseException(pos, ERROR_UNEXPECTED_TOKEN, xs);
478                 //
479                 return xs;
480             }
481         }
482     }
483
484     abstract protected void readNoEnd() throws ParseException, IOException;
485
486     abstract protected void readNQString(boolean[] stop) throws IOException;
487
488     abstract protected Object readNumber(boolean[] stop) throws ParseException, IOException;
489
490     protected <T> T readObject(JsonReaderI<T> mapper) throws ParseException, IOException {
491         //
492         if (c != '{')
493             throw new RuntimeException("Internal Error");
494         Object current = mapper.createObject();
495         boolean needData = false;
496         boolean acceptData = true;
497         for (;;) {
498             read();
499             switch (c) {
500             case ' ':
501             case '\r':
502             case '\t':
503             case '\n':
504                 continue;
505             case ':':
506             case ']':
507             case '[':
508             case '{':
509                 throw new ParseException(pos, ERROR_UNEXPECTED_CHAR, c);
510             case '}':
511                 if (needData && !acceptUselessComma)
512                     throw new ParseException(pos, ERROR_UNEXPECTED_CHAR, (char) c);
513                 read(); /* unstack */
514                 //
515                 return mapper.convert(current);
516             case ',':
517                 if (needData && !acceptUselessComma)
518                     throw new ParseException(pos, ERROR_UNEXPECTED_CHAR, (char) c);
519                 acceptData = needData = true;
520                 continue;
521             case '"':
522             case '\'':
523             default:
524                 // int keyStart = pos;
525                 if (c == '\"' || c == '\'') {
526                     readString();
527                 } else {
528                     readNQString(stopKey);
529                     if (!acceptNonQuote)
530                         throw new ParseException(pos, ERROR_UNEXPECTED_TOKEN, xs);
531                 }
532                 String key = xs;
533                 if (!acceptData)
534                     throw new ParseException(pos, ERROR_UNEXPECTED_TOKEN, key);
535
536                 // Skip spaces
537                 skipSpace();
538
539                 if (c != ':') {
540                     if (c == EOI)
541                         throw new ParseException(pos - 1, ERROR_UNEXPECTED_EOF, null);
542                     throw new ParseException(pos - 1, ERROR_UNEXPECTED_CHAR, c);
543                 }
544                 readNoEnd(); /* skip : */
545                 lastKey = key;
546                 Object value = readMain(mapper, stopValue);
547                 mapper.setValue(current, key, value);
548                 lastKey = null;
549
550                 // Object duplicate = obj.put(key, readMain(stopValue));
551                 // if (duplicate != null)
552                 // throw new ParseException(keyStart, ERROR_UNEXPECTED_DUPLICATE_KEY, key);
553                 // handler.endObjectEntry();
554                 // should loop skipping read step
555                 skipSpace();
556                 if (c == '}') {
557                     read(); /* unstack */
558                     //
559                     return mapper.convert(current);
560                 }
561                 if (c == EOI) // Fixed on 18/10/2011 reported by vladimir
562                     throw new ParseException(pos - 1, ERROR_UNEXPECTED_EOF, null);
563                 // if c==, continue
564                 if (c == ',')
565                     acceptData = needData = true;
566                 else
567                     throw new ParseException(pos - 1, ERROR_UNEXPECTED_TOKEN, c);
568                 // acceptData = needData = false;
569             }
570         }
571     }
572
573     /**
574      * store and read
575      */

576     abstract void readS() throws IOException;
577
578     abstract protected void readString() throws ParseException, IOException;
579
580     protected void readString2() throws ParseException, IOException {
581         /* assert (c == '\"' || c == '\'') */
582         char sep = c;
583         for (;;) {
584             read();
585             switch (c) {
586             case EOI:
587                 throw new ParseException(pos - 1, ERROR_UNEXPECTED_EOF, null);
588             case '"':
589             case '\'':
590                 if (sep == c) {
591                     read();
592                     xs = sb.toString();
593                     return;
594                 }
595                 sb.append(c);
596                 break;
597             case '\\':
598                 read();
599                 switch (c) {
600                 case 't':
601                     sb.append('\t');
602                     break;
603                 case 'n':
604                     sb.append('\n');
605                     break;
606                 case 'r':
607                     sb.append('\r');
608                     break;
609                 case 'f':
610                     sb.append('\f');
611                     break;
612                 case 'b':
613                     sb.append('\b');
614                     break;
615                 case '\\':
616                     sb.append('\\');
617                     break;
618                 case '/':
619                     sb.append('/');
620                     break;
621                 case '\'':
622                     sb.append('\'');
623                     break;
624                 case '"':
625                     sb.append('"');
626                     break;
627                 case 'u':
628                     sb.append(readUnicode(4));
629                     break;
630                 case 'x':
631                     sb.append(readUnicode(2));
632                     break;
633                 default:
634                     break;
635                 }
636                 break;
637             case '\0': // end of string
638             case (char) 1: // Start of heading
639             case (char) 2: // Start of text
640             case (char) 3: // End of text
641             case (char) 4: // End of transmission
642             case (char) 5: // Enquiry
643             case (char) 6: // Acknowledge
644             case (char) 7: // Bell
645             case '\b': // 8: backSpase
646             case '\t': // 9: horizontal tab
647             case '\n': // 10: new line
648             case (char) 11: // Vertical tab
649             case '\f': // 12: form feed
650             case '\r': // 13: return carriage
651             case (char) 14: // Shift Out, alternate character set
652             case (char) 15: // Shift In, resume defaultn character set
653             case (char) 16: // Data link escape
654             case (char) 17: // XON, with XOFF to pause listings;
655             case (char) 18: // Device control 2, block-mode flow control
656             case (char) 19: // XOFF, with XON is TERM=18 flow control
657             case (char) 20: // Device control 4
658             case (char) 21: // Negative acknowledge
659             case (char) 22: // Synchronous idle
660             case (char) 23: // End transmission block, not the same as EOT
661             case (char) 24: // Cancel line, MPE echoes !!!
662             case (char) 25: // End of medium, Control-Y interrupt
663             // case (char) 26: // Substitute == EOI
664             case (char) 27: // escape
665             case (char) 28: // File Separator
666             case (char) 29: // Group Separator
667             case (char) 30: // Record Separator
668             case (char) 31: // Unit Separator
669                 if (ignoreControlChar)
670                     continue;
671                 throw new ParseException(pos, ERROR_UNEXPECTED_CHAR, c);                
672             case (char) 127: // del
673                 if (ignoreControlChar)
674                     continue;
675                 if (reject127)
676                     throw new ParseException(pos, ERROR_UNEXPECTED_CHAR, c);
677             default:
678                 sb.append(c);
679             }
680         }
681     }
682
683     protected char readUnicode(int totalChars) throws ParseException, IOException {
684         int value = 0;
685         for (int i = 0; i < totalChars; i++) {
686             value = value * 16;
687             read();
688             if (c <= '9' && c >= '0')
689                 value += c - '0';
690             else if (c <= 'F' && c >= 'A')
691                 value += (c - 'A') + 10;
692             else if (c >= 'a' && c <= 'f')
693                 value += (c - 'a') + 10;
694             else if (c == EOI)
695                 throw new ParseException(pos, ERROR_UNEXPECTED_EOF, "EOF");
696             else
697                 throw new ParseException(pos, ERROR_UNEXPECTED_UNICODE, c);
698         }
699         return (char) value;
700     }
701
702     protected void skipDigits() throws IOException {
703         for (;;) {
704             if (c < '0' || c > '9')
705                 return;
706             readS();
707         }
708     }
709
710     protected void skipNQString(boolean[] stop) throws IOException {
711         for (;;) {
712             if ((c == EOI) || (c >= 0 && c < MAX_STOP && stop[c]))
713                 return;
714             readS();
715         }
716     }
717
718     protected void skipSpace() throws IOException {
719         for (;;) {
720             if (c > ' ' || c == EOI)
721                 return;
722             readS();
723         }
724     }
725
726     public static class MSB {
727         char b[];
728         int p;
729
730         public MSB(int size) {
731             b = new char[size];
732             p = -1;
733         }
734
735         public void append(char c) {
736             p++;
737             if (b.length <= p) {
738                 char[] t = new char[b.length * 2 + 1];
739                 System.arraycopy(b, 0, t, 0, b.length);
740                 b = t;
741             }
742             b[p] = c;
743         }
744
745         public void append(int c) {
746             p++;
747             if (b.length <= p) {
748                 char[] t = new char[b.length * 2 + 1];
749                 System.arraycopy(b, 0, t, 0, b.length);
750                 b = t;
751             }
752             b[p] = (char) c;
753         }
754
755         public String toString() {
756             return new String(b, 0, p + 1);
757         }
758
759         public void clear() {
760             p = -1;
761         }
762     }
763 }
764