001/* ============ 002 * Orson Charts 003 * ============ 004 * 005 * (C)opyright 2013, 2014, by Object Refinery Limited. 006 * 007 * http://www.object-refinery.com/orsoncharts/index.html 008 * 009 * JSON.simple 010 * ----------- 011 * The code in this file originates from the JSON.simple project by 012 * FangYidong<fangyidong@yahoo.com.cn>: 013 * 014 * https://code.google.com/p/json-simple/ 015 * 016 * which is licensed under the Apache Software License version 2.0. 017 * 018 * It has been modified locally and repackaged under 019 * com.orsoncharts.util.json.* to avoid conflicts with any other version that 020 * may be present on the classpath. 021 * 022 */ 023 024package com.orsoncharts.util.json.parser; 025 026import java.io.IOException; 027import java.io.Reader; 028import java.io.StringReader; 029import java.util.LinkedList; 030import java.util.List; 031import java.util.Map; 032import com.orsoncharts.util.json.JSONArray; 033import com.orsoncharts.util.json.JSONObject; 034 035 036/** 037 * Parser for JSON text. Please note that JSONParser is NOT thread-safe. 038 */ 039public class JSONParser { 040 041 public static final int S_INIT = 0; 042 public static final int S_IN_FINISHED_VALUE = 1;//string,number,boolean,null,object,array 043 public static final int S_IN_OBJECT = 2; 044 public static final int S_IN_ARRAY = 3; 045 public static final int S_PASSED_PAIR_KEY = 4; 046 public static final int S_IN_PAIR_VALUE = 5; 047 public static final int S_END = 6; 048 public static final int S_IN_ERROR = -1; 049 050 private LinkedList handlerStatusStack; 051 private Yylex lexer = new Yylex((Reader)null); 052 private Yytoken token = null; 053 private int status = S_INIT; 054 055 private int peekStatus(LinkedList statusStack){ 056 if (statusStack.size() == 0) { 057 return -1; 058 } 059 Integer result = (Integer) statusStack.getFirst(); 060 return result.intValue(); 061 } 062 063 /** 064 * Reset the parser to the initial state without resetting the underlying 065 * reader. 066 */ 067 public void reset(){ 068 token = null; 069 status = S_INIT; 070 handlerStatusStack = null; 071 } 072 073 /** 074 * Reset the parser to the initial state with a new character reader. 075 * 076 * @param in the new character reader. 077 */ 078 public void reset(Reader in){ 079 lexer.yyreset(in); 080 reset(); 081 } 082 083 /** 084 * @return The position of the beginning of the current token. 085 */ 086 public int getPosition(){ 087 return lexer.getPosition(); 088 } 089 090 public Object parse(String s) throws ParseException { 091 return parse(s, (ContainerFactory) null); 092 } 093 094 public Object parse(String s, ContainerFactory containerFactory) 095 throws ParseException { 096 StringReader in = new StringReader(s); 097 try { 098 return parse(in, containerFactory); 099 } 100 catch(IOException ie){ 101 /* 102 * Actually it will never happen. 103 */ 104 throw new ParseException(-1, 105 ParseException.ERROR_UNEXPECTED_EXCEPTION, ie); 106 } 107 } 108 109 public Object parse(Reader in) throws IOException, ParseException { 110 return parse(in, (ContainerFactory) null); 111 } 112 113 /** 114 * Parse JSON text into java object from the input source. 115 * 116 * @param in the input source. 117 * @param containerFactory use this factory to create your own JSON 118 * object and JSON array containers. 119 * @return Instance of the following: 120 * com.orsoncharts.util.json.simple.JSONObject, 121 * com.orsoncharts.util.json.simple.JSONArray, 122 * java.lang.String, 123 * java.lang.Number, 124 * java.lang.Boolean, 125 * null 126 * 127 * @throws IOException if there is an I/O problem. 128 * @throws ParseException if there is a parsing problem. 129 */ 130 public Object parse(Reader in, ContainerFactory containerFactory) 131 throws IOException, ParseException{ 132 reset(in); 133 LinkedList statusStack = new LinkedList(); 134 LinkedList valueStack = new LinkedList(); 135 136 try { 137 do { 138 nextToken(); 139 switch(status) { 140 case S_INIT: 141 switch(token.type) { 142 case Yytoken.TYPE_VALUE: 143 status=S_IN_FINISHED_VALUE; 144 statusStack.addFirst(new Integer(status)); 145 valueStack.addFirst(token.value); 146 break; 147 case Yytoken.TYPE_LEFT_BRACE: 148 status=S_IN_OBJECT; 149 statusStack.addFirst(new Integer(status)); 150 valueStack.addFirst(createObjectContainer(containerFactory)); 151 break; 152 case Yytoken.TYPE_LEFT_SQUARE: 153 status=S_IN_ARRAY; 154 statusStack.addFirst(new Integer(status)); 155 valueStack.addFirst(createArrayContainer(containerFactory)); 156 break; 157 default: 158 status=S_IN_ERROR; 159 }//inner switch 160 break; 161 162 case S_IN_FINISHED_VALUE: 163 if (token.type == Yytoken.TYPE_EOF) { 164 return valueStack.removeFirst(); 165 } else { 166 throw new ParseException(getPosition(), 167 ParseException.ERROR_UNEXPECTED_TOKEN, token); 168 } 169 170 case S_IN_OBJECT: 171 switch(token.type) { 172 case Yytoken.TYPE_COMMA: 173 break; 174 case Yytoken.TYPE_VALUE: 175 if (token.value instanceof String) { 176 String key = (String) token.value; 177 valueStack.addFirst(key); 178 status = S_PASSED_PAIR_KEY; 179 statusStack.addFirst(new Integer(status)); 180 } 181 else{ 182 status = S_IN_ERROR; 183 } 184 break; 185 case Yytoken.TYPE_RIGHT_BRACE: 186 if (valueStack.size() > 1) { 187 statusStack.removeFirst(); 188 valueStack.removeFirst(); 189 status = peekStatus(statusStack); 190 } 191 else{ 192 status = S_IN_FINISHED_VALUE; 193 } 194 break; 195 default: 196 status = S_IN_ERROR; 197 break; 198 }//inner switch 199 break; 200 201 case S_PASSED_PAIR_KEY: 202 switch(token.type) { 203 case Yytoken.TYPE_COLON: 204 break; 205 case Yytoken.TYPE_VALUE: 206 statusStack.removeFirst(); 207 String key = (String) valueStack.removeFirst(); 208 Map parent = (Map) valueStack.getFirst(); 209 parent.put(key,token.value); 210 status = peekStatus(statusStack); 211 break; 212 case Yytoken.TYPE_LEFT_SQUARE: 213 statusStack.removeFirst(); 214 key = (String) valueStack.removeFirst(); 215 parent = (Map) valueStack.getFirst(); 216 List newArray = createArrayContainer(containerFactory); 217 parent.put(key,newArray); 218 status = S_IN_ARRAY; 219 statusStack.addFirst(new Integer(status)); 220 valueStack.addFirst(newArray); 221 break; 222 case Yytoken.TYPE_LEFT_BRACE: 223 statusStack.removeFirst(); 224 key = (String) valueStack.removeFirst(); 225 parent = (Map) valueStack.getFirst(); 226 Map newObject = createObjectContainer(containerFactory); 227 parent.put(key, newObject); 228 status = S_IN_OBJECT; 229 statusStack.addFirst(new Integer(status)); 230 valueStack.addFirst(newObject); 231 break; 232 default: 233 status = S_IN_ERROR; 234 } 235 break; 236 237 case S_IN_ARRAY: 238 switch(token.type) { 239 case Yytoken.TYPE_COMMA: 240 break; 241 case Yytoken.TYPE_VALUE: 242 List val = (List) valueStack.getFirst(); 243 val.add(token.value); 244 break; 245 case Yytoken.TYPE_RIGHT_SQUARE: 246 if (valueStack.size() > 1) { 247 statusStack.removeFirst(); 248 valueStack.removeFirst(); 249 status = peekStatus(statusStack); 250 } 251 else { 252 status = S_IN_FINISHED_VALUE; 253 } 254 break; 255 case Yytoken.TYPE_LEFT_BRACE: 256 val = (List) valueStack.getFirst(); 257 Map newObject = createObjectContainer(containerFactory); 258 val.add(newObject); 259 status = S_IN_OBJECT; 260 statusStack.addFirst(new Integer(status)); 261 valueStack.addFirst(newObject); 262 break; 263 case Yytoken.TYPE_LEFT_SQUARE: 264 val = (List) valueStack.getFirst(); 265 List newArray = createArrayContainer(containerFactory); 266 val.add(newArray); 267 status = S_IN_ARRAY; 268 statusStack.addFirst(new Integer(status)); 269 valueStack.addFirst(newArray); 270 break; 271 default: 272 status = S_IN_ERROR; 273 }//inner switch 274 break; 275 case S_IN_ERROR: 276 throw new ParseException(getPosition(), 277 ParseException.ERROR_UNEXPECTED_TOKEN, token); 278 }//switch 279 if (status == S_IN_ERROR) { 280 throw new ParseException(getPosition(), 281 ParseException.ERROR_UNEXPECTED_TOKEN, token); 282 } 283 } while (token.type != Yytoken.TYPE_EOF); 284 } 285 catch (IOException ie) { 286 throw ie; 287 } 288 289 throw new ParseException(getPosition(), 290 ParseException.ERROR_UNEXPECTED_TOKEN, token); 291 } 292 293 private void nextToken() throws ParseException, IOException { 294 token = lexer.yylex(); 295 if (token == null) { 296 token = new Yytoken(Yytoken.TYPE_EOF, null); 297 } 298 } 299 300 private Map createObjectContainer(ContainerFactory containerFactory) { 301 if (containerFactory == null) { 302 return new JSONObject(); 303 } 304 Map m = containerFactory.createObjectContainer(); 305 if (m == null) { 306 return new JSONObject(); 307 } 308 return m; 309 } 310 311 private List createArrayContainer(ContainerFactory containerFactory) { 312 if (containerFactory == null) { 313 return new JSONArray(); 314 } 315 List l = containerFactory.creatArrayContainer(); 316 if (l == null) { 317 return new JSONArray(); 318 } 319 return l; 320 } 321 322 public void parse(String s, ContentHandler contentHandler) 323 throws ParseException { 324 parse(s, contentHandler, false); 325 } 326 327 public void parse(String s, ContentHandler contentHandler, boolean isResume) 328 throws ParseException{ 329 StringReader in = new StringReader(s); 330 try{ 331 parse(in, contentHandler, isResume); 332 } 333 catch(IOException ie) { 334 /* 335 * Actually it will never happen. 336 */ 337 throw new ParseException(-1, 338 ParseException.ERROR_UNEXPECTED_EXCEPTION, ie); 339 } 340 } 341 342 public void parse(Reader in, ContentHandler contentHandler) 343 throws IOException, ParseException { 344 parse(in, contentHandler, false); 345 } 346 347 /** 348 * Stream processing of JSON text. 349 * 350 * @see ContentHandler 351 * 352 * @param in the input. 353 * @param contentHandler the content handler. 354 * @param isResume indicates if it continues previous parsing operation. 355 * If set to true, resume parsing the old stream, and parameter 'in' 356 * will be ignored. If this method is called for the first time in 357 * this instance, isResume will be ignored. 358 * 359 * @throws IOException if there is an I/O problem. 360 * @throws ParseException if there is a parsing problem. 361 */ 362 public void parse(Reader in, ContentHandler contentHandler, 363 boolean isResume) throws IOException, ParseException { 364 if (!isResume) { 365 reset(in); 366 handlerStatusStack = new LinkedList(); 367 } 368 else{ 369 if (handlerStatusStack == null) { 370 isResume = false; 371 reset(in); 372 handlerStatusStack = new LinkedList(); 373 } 374 } 375 376 LinkedList statusStack = handlerStatusStack; 377 378 try { 379 do { 380 switch(status) { 381 case S_INIT: 382 contentHandler.startJSON(); 383 nextToken(); 384 switch(token.type) { 385 case Yytoken.TYPE_VALUE: 386 status=S_IN_FINISHED_VALUE; 387 statusStack.addFirst(new Integer(status)); 388 if (!contentHandler.primitive(token.value)) { 389 return; 390 } 391 break; 392 case Yytoken.TYPE_LEFT_BRACE: 393 status = S_IN_OBJECT; 394 statusStack.addFirst(new Integer(status)); 395 if (!contentHandler.startObject()) { 396 return; 397 } 398 break; 399 case Yytoken.TYPE_LEFT_SQUARE: 400 status = S_IN_ARRAY; 401 statusStack.addFirst(new Integer(status)); 402 if (!contentHandler.startArray()) { 403 return; 404 } 405 break; 406 default: 407 status = S_IN_ERROR; 408 }//inner switch 409 break; 410 411 case S_IN_FINISHED_VALUE: 412 nextToken(); 413 if (token.type == Yytoken.TYPE_EOF) { 414 contentHandler.endJSON(); 415 status = S_END; 416 return; 417 } 418 else { 419 status = S_IN_ERROR; 420 throw new ParseException(getPosition(), 421 ParseException.ERROR_UNEXPECTED_TOKEN, token); 422 } 423 424 case S_IN_OBJECT: 425 nextToken(); 426 switch(token.type) { 427 case Yytoken.TYPE_COMMA: 428 break; 429 case Yytoken.TYPE_VALUE: 430 if (token.value instanceof String) { 431 String key = (String) token.value; 432 status = S_PASSED_PAIR_KEY; 433 statusStack.addFirst(new Integer(status)); 434 if (!contentHandler.startObjectEntry(key)) { 435 return; 436 } 437 } 438 else { 439 status = S_IN_ERROR; 440 } 441 break; 442 case Yytoken.TYPE_RIGHT_BRACE: 443 if (statusStack.size() > 1) { 444 statusStack.removeFirst(); 445 status = peekStatus(statusStack); 446 } 447 else{ 448 status = S_IN_FINISHED_VALUE; 449 } 450 if (!contentHandler.endObject()) { 451 return; 452 } 453 break; 454 default: 455 status = S_IN_ERROR; 456 break; 457 }//inner switch 458 break; 459 460 case S_PASSED_PAIR_KEY: 461 nextToken(); 462 switch(token.type) { 463 case Yytoken.TYPE_COLON: 464 break; 465 case Yytoken.TYPE_VALUE: 466 statusStack.removeFirst(); 467 status = peekStatus(statusStack); 468 if (!contentHandler.primitive(token.value)) { 469 return; 470 } 471 if (!contentHandler.endObjectEntry()) { 472 return; 473 } 474 break; 475 case Yytoken.TYPE_LEFT_SQUARE: 476 statusStack.removeFirst(); 477 statusStack.addFirst(new Integer(S_IN_PAIR_VALUE)); 478 status = S_IN_ARRAY; 479 statusStack.addFirst(new Integer(status)); 480 if (!contentHandler.startArray()) { 481 return; 482 } 483 break; 484 case Yytoken.TYPE_LEFT_BRACE: 485 statusStack.removeFirst(); 486 statusStack.addFirst(new Integer(S_IN_PAIR_VALUE)); 487 status = S_IN_OBJECT; 488 statusStack.addFirst(new Integer(status)); 489 if (!contentHandler.startObject()) { 490 return; 491 } 492 break; 493 default: 494 status = S_IN_ERROR; 495 } 496 break; 497 498 case S_IN_PAIR_VALUE: 499 /* 500 * S_IN_PAIR_VALUE is just a marker to indicate the end of 501 * an object entry, it doesn't proccess any token, 502 * therefore delay consuming token until next round. 503 */ 504 statusStack.removeFirst(); 505 status = peekStatus(statusStack); 506 if (!contentHandler.endObjectEntry()) { 507 return; 508 } 509 break; 510 511 case S_IN_ARRAY: 512 nextToken(); 513 switch(token.type){ 514 case Yytoken.TYPE_COMMA: 515 break; 516 case Yytoken.TYPE_VALUE: 517 if (!contentHandler.primitive(token.value)) { 518 return; 519 } 520 break; 521 case Yytoken.TYPE_RIGHT_SQUARE: 522 if (statusStack.size() > 1) { 523 statusStack.removeFirst(); 524 status = peekStatus(statusStack); 525 } 526 else{ 527 status = S_IN_FINISHED_VALUE; 528 } 529 if (!contentHandler.endArray()) { 530 return; 531 } 532 break; 533 case Yytoken.TYPE_LEFT_BRACE: 534 status = S_IN_OBJECT; 535 statusStack.addFirst(new Integer(status)); 536 if (!contentHandler.startObject()) { 537 return; 538 } 539 break; 540 case Yytoken.TYPE_LEFT_SQUARE: 541 status = S_IN_ARRAY; 542 statusStack.addFirst(new Integer(status)); 543 if (!contentHandler.startArray()) { 544 return; 545 } 546 break; 547 default: 548 status = S_IN_ERROR; 549 }//inner switch 550 break; 551 552 case S_END: 553 return; 554 555 case S_IN_ERROR: 556 throw new ParseException(getPosition(), 557 ParseException.ERROR_UNEXPECTED_TOKEN, token); 558 }//switch 559 if (status == S_IN_ERROR) { 560 throw new ParseException(getPosition(), 561 ParseException.ERROR_UNEXPECTED_TOKEN, token); 562 } 563 } while(token.type != Yytoken.TYPE_EOF); 564 } 565 catch (IOException ie) { 566 status = S_IN_ERROR; 567 throw ie; 568 } 569 catch (ParseException pe) { 570 status = S_IN_ERROR; 571 throw pe; 572 } 573 catch (RuntimeException re) { 574 status = S_IN_ERROR; 575 throw re; 576 } 577 catch (Error e) { 578 status = S_IN_ERROR; 579 throw e; 580 } 581 582 status = S_IN_ERROR; 583 throw new ParseException(getPosition(), 584 ParseException.ERROR_UNEXPECTED_TOKEN, token); 585 } 586}