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

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