|
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 } |