This project has retired. For details please refer to its Attic page.
JSONParser xref
View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  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,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.chemistry.opencmis.commons.impl.json.parser;
20  
21  import java.io.IOException;
22  import java.io.Reader;
23  import java.io.StringReader;
24  import java.util.ArrayDeque;
25  import java.util.Deque;
26  import java.util.List;
27  import java.util.Map;
28  
29  import org.apache.chemistry.opencmis.commons.impl.json.JSONArray;
30  import org.apache.chemistry.opencmis.commons.impl.json.JSONObject;
31  
32  /**
33   * Parser for JSON text. Please note that JSONParser is NOT thread-safe.
34   * 
35   * (Taken from JSON.simple <http://code.google.com/p/json-simple/> and modified
36   * for OpenCMIS.)
37   * 
38   * @author FangYidong<fangyidong@yahoo.com.cn>
39   */
40  public class JSONParser {
41      public static final int S_INIT = 0;
42      public static final int S_IN_FINISHED_VALUE = 1; // string, number, boolean,
43                                                       // null, object, array
44      public static final int S_IN_OBJECT = 2;
45      public static final int S_IN_ARRAY = 3;
46      public static final int S_PASSED_PAIR_KEY = 4;
47      public static final int S_IN_PAIR_VALUE = 5;
48      public static final int S_END = 6;
49      public static final int S_IN_ERROR = -1;
50  
51      private ArrayDeque<Integer> handlerStatusStack;
52      private Yylex lexer = new Yylex((Reader) null);
53      private Yytoken token = null;
54      private int status = S_INIT;
55  
56      private int peekStatus(Deque<Integer> statusStack) {
57          if (statusStack.isEmpty()) {
58              return -1;
59          }
60  
61          return statusStack.getFirst();
62      }
63  
64      /**
65       * Reset the parser to the initial state without resetting the underlying
66       * reader.
67       * 
68       */
69      public void reset() {
70          token = null;
71          status = S_INIT;
72          handlerStatusStack = null;
73      }
74  
75      /**
76       * Reset the parser to the initial state with a new character reader.
77       * 
78       * @param in
79       *            - The new character reader.
80       * @throws IOException
81       * @throws JSONParseException
82       */
83      public void reset(Reader in) {
84          lexer.yyreset(in);
85          reset();
86      }
87  
88      /**
89       * @return The position of the beginning of the current token.
90       */
91      public int getPosition() {
92          return lexer.getPosition();
93      }
94  
95      public Object parse(String s) throws JSONParseException {
96          return parse(s, (ContainerFactory) null);
97      }
98  
99      public Object parse(String s, ContainerFactory containerFactory) throws JSONParseException {
100         StringReader in = new StringReader(s);
101         try {
102             return parse(in, containerFactory);
103         } catch (IOException ie) {
104             /*
105              * Actually it will never happen.
106              */
107             throw new JSONParseException(-1, JSONParseException.ERROR_UNEXPECTED_EXCEPTION, ie);
108         }
109     }
110 
111     public Object parse(Reader in) throws IOException, JSONParseException {
112         return parse(in, (ContainerFactory) null);
113     }
114 
115     /**
116      * Parse JSON text into java object from the input source.
117      * 
118      * @param in
119      * @param containerFactory
120      *            - Use this factory to createyour own JSON object and JSON
121      *            array containers.
122      * @return Instance of the following: org.json.simple.JSONObject,
123      *         org.json.simple.JSONArray, java.lang.String, java.lang.Number,
124      *         java.lang.Boolean, null
125      * 
126      * @throws IOException
127      * @throws JSONParseException
128      */
129     @SuppressWarnings("unchecked")
130     public Object parse(Reader in, ContainerFactory containerFactory) throws IOException, JSONParseException {
131         reset(in);
132         Deque<Integer> statusStack = new ArrayDeque<Integer>();
133         Deque<Object> valueStack = new ArrayDeque<Object>();
134 
135         try {
136             do {
137                 nextToken();
138                 switch (status) {
139                 case S_INIT:
140                     switch (token.type) {
141                     case Yytoken.TYPE_VALUE:
142                         status = S_IN_FINISHED_VALUE;
143                         statusStack.addFirst(Integer.valueOf(status));
144                         valueStack.addFirst(token.value);
145                         break;
146                     case Yytoken.TYPE_LEFT_BRACE:
147                         status = S_IN_OBJECT;
148                         statusStack.addFirst(Integer.valueOf(status));
149                         valueStack.addFirst(createObjectContainer(containerFactory));
150                         break;
151                     case Yytoken.TYPE_LEFT_SQUARE:
152                         status = S_IN_ARRAY;
153                         statusStack.addFirst(Integer.valueOf(status));
154                         valueStack.addFirst(createArrayContainer(containerFactory));
155                         break;
156                     default:
157                         status = S_IN_ERROR;
158                     }// inner switch
159                     break;
160 
161                 case S_IN_FINISHED_VALUE:
162                     if (token.type == Yytoken.TYPE_EOF) {
163                         return valueStack.removeFirst();
164                     } else {
165                         throw new JSONParseException(getPosition(), JSONParseException.ERROR_UNEXPECTED_TOKEN, token);
166                     }
167 
168                 case S_IN_OBJECT:
169                     switch (token.type) {
170                     case Yytoken.TYPE_COMMA:
171                         break;
172                     case Yytoken.TYPE_VALUE:
173                         if (token.value instanceof String) {
174                             String key = (String) token.value;
175                             valueStack.addFirst(key);
176                             status = S_PASSED_PAIR_KEY;
177                             statusStack.addFirst(Integer.valueOf(status));
178                         } else {
179                             status = S_IN_ERROR;
180                         }
181                         break;
182                     case Yytoken.TYPE_RIGHT_BRACE:
183                         if (valueStack.size() > 1) {
184                             statusStack.removeFirst();
185                             valueStack.removeFirst();
186                             status = peekStatus(statusStack);
187                         } else {
188                             status = S_IN_FINISHED_VALUE;
189                         }
190                         break;
191                     default:
192                         status = S_IN_ERROR;
193                         break;
194                     }// inner switch
195                     break;
196 
197                 case S_PASSED_PAIR_KEY:
198                     switch (token.type) {
199                     case Yytoken.TYPE_COLON:
200                         break;
201                     case Yytoken.TYPE_VALUE:
202                         statusStack.removeFirst();
203                         String key = (String) valueStack.removeFirst();
204                         Map<String, Object> parent = (Map<String, Object>) valueStack.getFirst();
205                         parent.put(key, token.value);
206                         status = peekStatus(statusStack);
207                         break;
208                     case Yytoken.TYPE_LEFT_SQUARE:
209                         statusStack.removeFirst();
210                         key = (String) valueStack.removeFirst();
211                         parent = (Map<String, Object>) valueStack.getFirst();
212                         List<Object> newArray = createArrayContainer(containerFactory);
213                         parent.put(key, newArray);
214                         status = S_IN_ARRAY;
215                         statusStack.addFirst(Integer.valueOf(status));
216                         valueStack.addFirst(newArray);
217                         break;
218                     case Yytoken.TYPE_LEFT_BRACE:
219                         statusStack.removeFirst();
220                         key = (String) valueStack.removeFirst();
221                         parent = (Map<String, Object>) valueStack.getFirst();
222                         Map<String, Object> newObject = createObjectContainer(containerFactory);
223                         parent.put(key, newObject);
224                         status = S_IN_OBJECT;
225                         statusStack.addFirst(Integer.valueOf(status));
226                         valueStack.addFirst(newObject);
227                         break;
228                     default:
229                         status = S_IN_ERROR;
230                     }
231                     break;
232 
233                 case S_IN_ARRAY:
234                     switch (token.type) {
235                     case Yytoken.TYPE_COMMA:
236                         break;
237                     case Yytoken.TYPE_VALUE:
238                         List<Object> val = (List<Object>) valueStack.getFirst();
239                         val.add(token.value);
240                         break;
241                     case Yytoken.TYPE_RIGHT_SQUARE:
242                         if (valueStack.size() > 1) {
243                             statusStack.removeFirst();
244                             valueStack.removeFirst();
245                             status = peekStatus(statusStack);
246                         } else {
247                             status = S_IN_FINISHED_VALUE;
248                         }
249                         break;
250                     case Yytoken.TYPE_LEFT_BRACE:
251                         val = (List<Object>) valueStack.getFirst();
252                         Map<String, Object> newObject = createObjectContainer(containerFactory);
253                         val.add(newObject);
254                         status = S_IN_OBJECT;
255                         statusStack.addFirst(Integer.valueOf(status));
256                         valueStack.addFirst(newObject);
257                         break;
258                     case Yytoken.TYPE_LEFT_SQUARE:
259                         val = (List<Object>) valueStack.getFirst();
260                         List<Object> newArray = createArrayContainer(containerFactory);
261                         val.add(newArray);
262                         status = S_IN_ARRAY;
263                         statusStack.addFirst(Integer.valueOf(status));
264                         valueStack.addFirst(newArray);
265                         break;
266                     default:
267                         status = S_IN_ERROR;
268                     }// inner switch
269                     break;
270                 case S_IN_ERROR:
271                     throw new JSONParseException(getPosition(), JSONParseException.ERROR_UNEXPECTED_TOKEN, token);
272                 }// switch
273                 if (status == S_IN_ERROR) {
274                     throw new JSONParseException(getPosition(), JSONParseException.ERROR_UNEXPECTED_TOKEN, token);
275                 }
276             } while (token.type != Yytoken.TYPE_EOF);
277         } catch (IOException ie) {
278             throw ie;
279         }
280 
281         throw new JSONParseException(getPosition(), JSONParseException.ERROR_UNEXPECTED_TOKEN, token);
282     }
283 
284     private void nextToken() throws JSONParseException, IOException {
285         token = lexer.yylex();
286         if (token == null) {
287             token = new Yytoken(Yytoken.TYPE_EOF, null);
288         }
289     }
290 
291     private Map<String, Object> createObjectContainer(ContainerFactory containerFactory) {
292         if (containerFactory == null) {
293             return new JSONObject();
294         }
295 
296         Map<String, Object> m = containerFactory.createObjectContainer();
297 
298         if (m == null) {
299             return new JSONObject();
300         }
301 
302         return m;
303     }
304 
305     private List<Object> createArrayContainer(ContainerFactory containerFactory) {
306         if (containerFactory == null) {
307             return new JSONArray();
308         }
309 
310         List<Object> l = containerFactory.creatArrayContainer();
311 
312         if (l == null) {
313             return new JSONArray();
314         }
315 
316         return l;
317     }
318 
319     public void parse(String s, ContentHandler contentHandler) throws JSONParseException {
320         parse(s, contentHandler, false);
321     }
322 
323     public void parse(String s, ContentHandler contentHandler, boolean isResume) throws JSONParseException {
324         StringReader in = new StringReader(s);
325         try {
326             parse(in, contentHandler, isResume);
327         } catch (IOException ie) {
328             /*
329              * Actually it will never happen.
330              */
331             throw new JSONParseException(-1, JSONParseException.ERROR_UNEXPECTED_EXCEPTION, ie);
332         }
333     }
334 
335     public void parse(Reader in, ContentHandler contentHandler) throws IOException, JSONParseException {
336         parse(in, contentHandler, false);
337     }
338 
339     /**
340      * Stream processing of JSON text.
341      * 
342      * @see ContentHandler
343      * 
344      * @param in
345      * @param contentHandler
346      * @param isResume
347      *            - Indicates if it continues previous parsing operation. If set
348      *            to true, resume parsing the old stream, and parameter 'in'
349      *            will be ignored. If this method is called for the first time
350      *            in this instance, isResume will be ignored.
351      * 
352      * @throws IOException
353      * @throws JSONParseException
354      */
355     public void parse(Reader in, ContentHandler contentHandler, boolean isResume) throws IOException,
356             JSONParseException {
357         if (!isResume) {
358             reset(in);
359             handlerStatusStack = new ArrayDeque<Integer>();
360         } else {
361             if (handlerStatusStack == null) {
362                 isResume = false;
363                 reset(in);
364                 handlerStatusStack = new ArrayDeque<Integer>();
365             }
366         }
367 
368         ArrayDeque<Integer> statusStack = handlerStatusStack;
369 
370         try {
371             do {
372                 switch (status) {
373                 case S_INIT:
374                     contentHandler.startJSON();
375                     nextToken();
376                     switch (token.type) {
377                     case Yytoken.TYPE_VALUE:
378                         status = S_IN_FINISHED_VALUE;
379                         statusStack.addFirst(Integer.valueOf(status));
380                         if (!contentHandler.primitive(token.value)) {
381                             return;
382                         }
383                         break;
384                     case Yytoken.TYPE_LEFT_BRACE:
385                         status = S_IN_OBJECT;
386                         statusStack.addFirst(Integer.valueOf(status));
387                         if (!contentHandler.startObject()) {
388                             return;
389                         }
390                         break;
391                     case Yytoken.TYPE_LEFT_SQUARE:
392                         status = S_IN_ARRAY;
393                         statusStack.addFirst(Integer.valueOf(status));
394                         if (!contentHandler.startArray()) {
395                             return;
396                         }
397                         break;
398                     default:
399                         status = S_IN_ERROR;
400                     }// inner switch
401                     break;
402 
403                 case S_IN_FINISHED_VALUE:
404                     nextToken();
405                     if (token.type == Yytoken.TYPE_EOF) {
406                         contentHandler.endJSON();
407                         status = S_END;
408                         return;
409                     } else {
410                         status = S_IN_ERROR;
411                         throw new JSONParseException(getPosition(), JSONParseException.ERROR_UNEXPECTED_TOKEN, token);
412                     }
413 
414                 case S_IN_OBJECT:
415                     nextToken();
416                     switch (token.type) {
417                     case Yytoken.TYPE_COMMA:
418                         break;
419                     case Yytoken.TYPE_VALUE:
420                         if (token.value instanceof String) {
421                             String key = (String) token.value;
422                             status = S_PASSED_PAIR_KEY;
423                             statusStack.addFirst(Integer.valueOf(status));
424                             if (!contentHandler.startObjectEntry(key)) {
425                                 return;
426                             }
427                         } else {
428                             status = S_IN_ERROR;
429                         }
430                         break;
431                     case Yytoken.TYPE_RIGHT_BRACE:
432                         if (statusStack.size() > 1) {
433                             statusStack.removeFirst();
434                             status = peekStatus(statusStack);
435                         } else {
436                             status = S_IN_FINISHED_VALUE;
437                         }
438                         if (!contentHandler.endObject()) {
439                             return;
440                         }
441                         break;
442                     default:
443                         status = S_IN_ERROR;
444                         break;
445                     }// inner switch
446                     break;
447 
448                 case S_PASSED_PAIR_KEY:
449                     nextToken();
450                     switch (token.type) {
451                     case Yytoken.TYPE_COLON:
452                         break;
453                     case Yytoken.TYPE_VALUE:
454                         statusStack.removeFirst();
455                         status = peekStatus(statusStack);
456                         if (!contentHandler.primitive(token.value)) {
457                             return;
458                         }
459                         if (!contentHandler.endObjectEntry()) {
460                             return;
461                         }
462                         break;
463                     case Yytoken.TYPE_LEFT_SQUARE:
464                         statusStack.removeFirst();
465                         statusStack.addFirst(Integer.valueOf(S_IN_PAIR_VALUE));
466                         status = S_IN_ARRAY;
467                         statusStack.addFirst(Integer.valueOf(status));
468                         if (!contentHandler.startArray()) {
469                             return;
470                         }
471                         break;
472                     case Yytoken.TYPE_LEFT_BRACE:
473                         statusStack.removeFirst();
474                         statusStack.addFirst(Integer.valueOf(S_IN_PAIR_VALUE));
475                         status = S_IN_OBJECT;
476                         statusStack.addFirst(Integer.valueOf(status));
477                         if (!contentHandler.startObject()) {
478                             return;
479                         }
480                         break;
481                     default:
482                         status = S_IN_ERROR;
483                     }
484                     break;
485 
486                 case S_IN_PAIR_VALUE:
487                     /*
488                      * S_IN_PAIR_VALUE is just a marker to indicate the end of
489                      * an object entry, it doesn't proccess any token, therefore
490                      * delay consuming token until next round.
491                      */
492                     statusStack.removeFirst();
493                     status = peekStatus(statusStack);
494                     if (!contentHandler.endObjectEntry()) {
495                         return;
496                     }
497                     break;
498 
499                 case S_IN_ARRAY:
500                     nextToken();
501                     switch (token.type) {
502                     case Yytoken.TYPE_COMMA:
503                         break;
504                     case Yytoken.TYPE_VALUE:
505                         if (!contentHandler.primitive(token.value)) {
506                             return;
507                         }
508                         break;
509                     case Yytoken.TYPE_RIGHT_SQUARE:
510                         if (statusStack.size() > 1) {
511                             statusStack.removeFirst();
512                             status = peekStatus(statusStack);
513                         } else {
514                             status = S_IN_FINISHED_VALUE;
515                         }
516                         if (!contentHandler.endArray()) {
517                             return;
518                         }
519                         break;
520                     case Yytoken.TYPE_LEFT_BRACE:
521                         status = S_IN_OBJECT;
522                         statusStack.addFirst(Integer.valueOf(status));
523                         if (!contentHandler.startObject()) {
524                             return;
525                         }
526                         break;
527                     case Yytoken.TYPE_LEFT_SQUARE:
528                         status = S_IN_ARRAY;
529                         statusStack.addFirst(Integer.valueOf(status));
530                         if (!contentHandler.startArray()) {
531                             return;
532                         }
533                         break;
534                     default:
535                         status = S_IN_ERROR;
536                     }// inner switch
537                     break;
538 
539                 case S_END:
540                     return;
541 
542                 case S_IN_ERROR:
543                     throw new JSONParseException(getPosition(), JSONParseException.ERROR_UNEXPECTED_TOKEN, token);
544                 }// switch
545                 if (status == S_IN_ERROR) {
546                     throw new JSONParseException(getPosition(), JSONParseException.ERROR_UNEXPECTED_TOKEN, token);
547                 }
548             } while (token.type != Yytoken.TYPE_EOF);
549         } catch (IOException ie) {
550             status = S_IN_ERROR;
551             throw ie;
552         } catch (JSONParseException pe) {
553             status = S_IN_ERROR;
554             throw pe;
555         } catch (RuntimeException re) {
556             status = S_IN_ERROR;
557             throw re;
558         }
559 
560         status = S_IN_ERROR;
561         throw new JSONParseException(getPosition(), JSONParseException.ERROR_UNEXPECTED_TOKEN, token);
562     }
563 }