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