001/* ============ 002 * Orson Charts 003 * ============ 004 * 005 * (C)opyright 2013-2016, 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<Integer> 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 @SuppressWarnings("unchecked") 131 public Object parse(Reader in, ContainerFactory containerFactory) 132 throws IOException, ParseException{ 133 reset(in); 134 LinkedList<Integer> statusStack = new LinkedList<Integer>(); 135 LinkedList<Object> valueStack = new LinkedList<Object>(); 136 137 try { 138 do { 139 nextToken(); 140 switch(status) { 141 case S_INIT: 142 switch(token.type) { 143 case Yytoken.TYPE_VALUE: 144 status=S_IN_FINISHED_VALUE; 145 statusStack.addFirst(new Integer(status)); 146 valueStack.addFirst(token.value); 147 break; 148 case Yytoken.TYPE_LEFT_BRACE: 149 status=S_IN_OBJECT; 150 statusStack.addFirst(new Integer(status)); 151 valueStack.addFirst(createObjectContainer(containerFactory)); 152 break; 153 case Yytoken.TYPE_LEFT_SQUARE: 154 status=S_IN_ARRAY; 155 statusStack.addFirst(new Integer(status)); 156 valueStack.addFirst(createArrayContainer(containerFactory)); 157 break; 158 default: 159 status=S_IN_ERROR; 160 }//inner switch 161 break; 162 163 case S_IN_FINISHED_VALUE: 164 if (token.type == Yytoken.TYPE_EOF) { 165 return valueStack.removeFirst(); 166 } else { 167 throw new ParseException(getPosition(), 168 ParseException.ERROR_UNEXPECTED_TOKEN, token); 169 } 170 171 case S_IN_OBJECT: 172 switch(token.type) { 173 case Yytoken.TYPE_COMMA: 174 break; 175 case Yytoken.TYPE_VALUE: 176 if (token.value instanceof String) { 177 String key = (String) token.value; 178 valueStack.addFirst(key); 179 status = S_PASSED_PAIR_KEY; 180 statusStack.addFirst(new Integer(status)); 181 } 182 else{ 183 status = S_IN_ERROR; 184 } 185 break; 186 case Yytoken.TYPE_RIGHT_BRACE: 187 if (valueStack.size() > 1) { 188 statusStack.removeFirst(); 189 valueStack.removeFirst(); 190 status = peekStatus(statusStack); 191 } 192 else{ 193 status = S_IN_FINISHED_VALUE; 194 } 195 break; 196 default: 197 status = S_IN_ERROR; 198 break; 199 }//inner switch 200 break; 201 202 case S_PASSED_PAIR_KEY: 203 switch(token.type) { 204 case Yytoken.TYPE_COLON: 205 break; 206 case Yytoken.TYPE_VALUE: 207 statusStack.removeFirst(); 208 String key = (String) valueStack.removeFirst(); 209 Map parent = (Map) valueStack.getFirst(); 210 parent.put(key,token.value); 211 status = peekStatus(statusStack); 212 break; 213 case Yytoken.TYPE_LEFT_SQUARE: 214 statusStack.removeFirst(); 215 key = (String) valueStack.removeFirst(); 216 parent = (Map) valueStack.getFirst(); 217 List newArray = createArrayContainer(containerFactory); 218 parent.put(key,newArray); 219 status = S_IN_ARRAY; 220 statusStack.addFirst(new Integer(status)); 221 valueStack.addFirst(newArray); 222 break; 223 case Yytoken.TYPE_LEFT_BRACE: 224 statusStack.removeFirst(); 225 key = (String) valueStack.removeFirst(); 226 parent = (Map) valueStack.getFirst(); 227 Map newObject = createObjectContainer(containerFactory); 228 parent.put(key, newObject); 229 status = S_IN_OBJECT; 230 statusStack.addFirst(new Integer(status)); 231 valueStack.addFirst(newObject); 232 break; 233 default: 234 status = S_IN_ERROR; 235 } 236 break; 237 238 case S_IN_ARRAY: 239 switch(token.type) { 240 case Yytoken.TYPE_COMMA: 241 break; 242 case Yytoken.TYPE_VALUE: 243 List val = (List) valueStack.getFirst(); 244 val.add(token.value); 245 break; 246 case Yytoken.TYPE_RIGHT_SQUARE: 247 if (valueStack.size() > 1) { 248 statusStack.removeFirst(); 249 valueStack.removeFirst(); 250 status = peekStatus(statusStack); 251 } 252 else { 253 status = S_IN_FINISHED_VALUE; 254 } 255 break; 256 case Yytoken.TYPE_LEFT_BRACE: 257 val = (List) valueStack.getFirst(); 258 Map newObject = createObjectContainer(containerFactory); 259 val.add(newObject); 260 status = S_IN_OBJECT; 261 statusStack.addFirst(new Integer(status)); 262 valueStack.addFirst(newObject); 263 break; 264 case Yytoken.TYPE_LEFT_SQUARE: 265 val = (List) valueStack.getFirst(); 266 List newArray = createArrayContainer(containerFactory); 267 val.add(newArray); 268 status = S_IN_ARRAY; 269 statusStack.addFirst(new Integer(status)); 270 valueStack.addFirst(newArray); 271 break; 272 default: 273 status = S_IN_ERROR; 274 }//inner switch 275 break; 276 case S_IN_ERROR: 277 throw new ParseException(getPosition(), 278 ParseException.ERROR_UNEXPECTED_TOKEN, token); 279 }//switch 280 if (status == S_IN_ERROR) { 281 throw new ParseException(getPosition(), 282 ParseException.ERROR_UNEXPECTED_TOKEN, token); 283 } 284 } while (token.type != Yytoken.TYPE_EOF); 285 } 286 catch (IOException ie) { 287 throw ie; 288 } 289 290 throw new ParseException(getPosition(), 291 ParseException.ERROR_UNEXPECTED_TOKEN, token); 292 } 293 294 private void nextToken() throws ParseException, IOException { 295 token = lexer.yylex(); 296 if (token == null) { 297 token = new Yytoken(Yytoken.TYPE_EOF, null); 298 } 299 } 300 301 private Map createObjectContainer(ContainerFactory containerFactory) { 302 if (containerFactory == null) { 303 return new JSONObject(); 304 } 305 Map m = containerFactory.createObjectContainer(); 306 if (m == null) { 307 return new JSONObject(); 308 } 309 return m; 310 } 311 312 private List createArrayContainer(ContainerFactory containerFactory) { 313 if (containerFactory == null) { 314 return new JSONArray(); 315 } 316 List l = containerFactory.creatArrayContainer(); 317 if (l == null) { 318 return new JSONArray(); 319 } 320 return l; 321 } 322 323 public void parse(String s, ContentHandler contentHandler) 324 throws ParseException { 325 parse(s, contentHandler, false); 326 } 327 328 public void parse(String s, ContentHandler contentHandler, boolean isResume) 329 throws ParseException{ 330 StringReader in = new StringReader(s); 331 try{ 332 parse(in, contentHandler, isResume); 333 } 334 catch(IOException ie) { 335 /* 336 * Actually it will never happen. 337 */ 338 throw new ParseException(-1, 339 ParseException.ERROR_UNEXPECTED_EXCEPTION, ie); 340 } 341 } 342 343 public void parse(Reader in, ContentHandler contentHandler) 344 throws IOException, ParseException { 345 parse(in, contentHandler, false); 346 } 347 348 /** 349 * Stream processing of JSON text. 350 * 351 * @see ContentHandler 352 * 353 * @param in the input. 354 * @param contentHandler the content handler. 355 * @param isResume indicates if it continues previous parsing operation. 356 * If set to true, resume parsing the old stream, and parameter 'in' 357 * will be ignored. If this method is called for the first time in 358 * this instance, isResume will be ignored. 359 * 360 * @throws IOException if there is an I/O problem. 361 * @throws ParseException if there is a parsing problem. 362 */ 363 public void parse(Reader in, ContentHandler contentHandler, 364 boolean isResume) throws IOException, ParseException { 365 if (!isResume) { 366 reset(in); 367 handlerStatusStack = new LinkedList<Integer>(); 368 } 369 else{ 370 if (handlerStatusStack == null) { 371 isResume = false; 372 reset(in); 373 handlerStatusStack = new LinkedList<Integer>(); 374 } 375 } 376 377 LinkedList<Integer> statusStack = handlerStatusStack; 378 379 try { 380 do { 381 switch(status) { 382 case S_INIT: 383 contentHandler.startJSON(); 384 nextToken(); 385 switch(token.type) { 386 case Yytoken.TYPE_VALUE: 387 status=S_IN_FINISHED_VALUE; 388 statusStack.addFirst(new Integer(status)); 389 if (!contentHandler.primitive(token.value)) { 390 return; 391 } 392 break; 393 case Yytoken.TYPE_LEFT_BRACE: 394 status = S_IN_OBJECT; 395 statusStack.addFirst(new Integer(status)); 396 if (!contentHandler.startObject()) { 397 return; 398 } 399 break; 400 case Yytoken.TYPE_LEFT_SQUARE: 401 status = S_IN_ARRAY; 402 statusStack.addFirst(new Integer(status)); 403 if (!contentHandler.startArray()) { 404 return; 405 } 406 break; 407 default: 408 status = S_IN_ERROR; 409 }//inner switch 410 break; 411 412 case S_IN_FINISHED_VALUE: 413 nextToken(); 414 if (token.type == Yytoken.TYPE_EOF) { 415 contentHandler.endJSON(); 416 status = S_END; 417 return; 418 } 419 else { 420 status = S_IN_ERROR; 421 throw new ParseException(getPosition(), 422 ParseException.ERROR_UNEXPECTED_TOKEN, token); 423 } 424 425 case S_IN_OBJECT: 426 nextToken(); 427 switch(token.type) { 428 case Yytoken.TYPE_COMMA: 429 break; 430 case Yytoken.TYPE_VALUE: 431 if (token.value instanceof String) { 432 String key = (String) token.value; 433 status = S_PASSED_PAIR_KEY; 434 statusStack.addFirst(new Integer(status)); 435 if (!contentHandler.startObjectEntry(key)) { 436 return; 437 } 438 } 439 else { 440 status = S_IN_ERROR; 441 } 442 break; 443 case Yytoken.TYPE_RIGHT_BRACE: 444 if (statusStack.size() > 1) { 445 statusStack.removeFirst(); 446 status = peekStatus(statusStack); 447 } 448 else{ 449 status = S_IN_FINISHED_VALUE; 450 } 451 if (!contentHandler.endObject()) { 452 return; 453 } 454 break; 455 default: 456 status = S_IN_ERROR; 457 break; 458 }//inner switch 459 break; 460 461 case S_PASSED_PAIR_KEY: 462 nextToken(); 463 switch(token.type) { 464 case Yytoken.TYPE_COLON: 465 break; 466 case Yytoken.TYPE_VALUE: 467 statusStack.removeFirst(); 468 status = peekStatus(statusStack); 469 if (!contentHandler.primitive(token.value)) { 470 return; 471 } 472 if (!contentHandler.endObjectEntry()) { 473 return; 474 } 475 break; 476 case Yytoken.TYPE_LEFT_SQUARE: 477 statusStack.removeFirst(); 478 statusStack.addFirst(new Integer(S_IN_PAIR_VALUE)); 479 status = S_IN_ARRAY; 480 statusStack.addFirst(new Integer(status)); 481 if (!contentHandler.startArray()) { 482 return; 483 } 484 break; 485 case Yytoken.TYPE_LEFT_BRACE: 486 statusStack.removeFirst(); 487 statusStack.addFirst(new Integer(S_IN_PAIR_VALUE)); 488 status = S_IN_OBJECT; 489 statusStack.addFirst(new Integer(status)); 490 if (!contentHandler.startObject()) { 491 return; 492 } 493 break; 494 default: 495 status = S_IN_ERROR; 496 } 497 break; 498 499 case S_IN_PAIR_VALUE: 500 /* 501 * S_IN_PAIR_VALUE is just a marker to indicate the end of 502 * an object entry, it doesn't proccess any token, 503 * therefore delay consuming token until next round. 504 */ 505 statusStack.removeFirst(); 506 status = peekStatus(statusStack); 507 if (!contentHandler.endObjectEntry()) { 508 return; 509 } 510 break; 511 512 case S_IN_ARRAY: 513 nextToken(); 514 switch(token.type){ 515 case Yytoken.TYPE_COMMA: 516 break; 517 case Yytoken.TYPE_VALUE: 518 if (!contentHandler.primitive(token.value)) { 519 return; 520 } 521 break; 522 case Yytoken.TYPE_RIGHT_SQUARE: 523 if (statusStack.size() > 1) { 524 statusStack.removeFirst(); 525 status = peekStatus(statusStack); 526 } 527 else{ 528 status = S_IN_FINISHED_VALUE; 529 } 530 if (!contentHandler.endArray()) { 531 return; 532 } 533 break; 534 case Yytoken.TYPE_LEFT_BRACE: 535 status = S_IN_OBJECT; 536 statusStack.addFirst(new Integer(status)); 537 if (!contentHandler.startObject()) { 538 return; 539 } 540 break; 541 case Yytoken.TYPE_LEFT_SQUARE: 542 status = S_IN_ARRAY; 543 statusStack.addFirst(new Integer(status)); 544 if (!contentHandler.startArray()) { 545 return; 546 } 547 break; 548 default: 549 status = S_IN_ERROR; 550 }//inner switch 551 break; 552 553 case S_END: 554 return; 555 556 case S_IN_ERROR: 557 throw new ParseException(getPosition(), 558 ParseException.ERROR_UNEXPECTED_TOKEN, token); 559 }//switch 560 if (status == S_IN_ERROR) { 561 throw new ParseException(getPosition(), 562 ParseException.ERROR_UNEXPECTED_TOKEN, token); 563 } 564 } while(token.type != Yytoken.TYPE_EOF); 565 } 566 catch (IOException ie) { 567 status = S_IN_ERROR; 568 throw ie; 569 } 570 catch (ParseException pe) { 571 status = S_IN_ERROR; 572 throw pe; 573 } 574 catch (RuntimeException re) { 575 status = S_IN_ERROR; 576 throw re; 577 } 578 catch (Error e) { 579 status = S_IN_ERROR; 580 throw e; 581 } 582 583 status = S_IN_ERROR; 584 throw new ParseException(getPosition(), 585 ParseException.ERROR_UNEXPECTED_TOKEN, token); 586 } 587}