/* ============
* Orson Charts
* ============
*
* (C)opyright 2013-2016, by Object Refinery Limited.
*
* http://www.object-refinery.com/orsoncharts/index.html
*
* JSON.simple
* -----------
* The code in this file originates from the JSON.simple project by
* FangYidong<fangyidong@yahoo.com.cn>:
*
* https://code.google.com/p/json-simple/
*
* which is licensed under the Apache Software License version 2.0.
*
* It has been modified locally and repackaged under
* com.orsoncharts.util.json.* to avoid conflicts with any other version that
* may be present on the classpath.
*
*/
package com.orsoncharts.util.json.parser;
import java.io.IOException;
import java.io.Reader;
import java.io.StringReader;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import com.orsoncharts.util.json.JSONArray;
import com.orsoncharts.util.json.JSONObject;
/**
* Parser for JSON text. Please note that JSONParser is NOT thread-safe.
*/
public class JSONParser {
public static final int S_INIT = 0;
public static final int S_IN_FINISHED_VALUE = 1;//string,number,boolean,null,object,array
public static final int S_IN_OBJECT = 2;
public static final int S_IN_ARRAY = 3;
public static final int S_PASSED_PAIR_KEY = 4;
public static final int S_IN_PAIR_VALUE = 5;
public static final int S_END = 6;
public static final int S_IN_ERROR = -1;
private LinkedList<Integer> handlerStatusStack;
private Yylex lexer = new Yylex((Reader)null);
private Yytoken token = null;
private int status = S_INIT;
private int peekStatus(LinkedList statusStack){
if (statusStack.size() == 0) {
return -1;
}
Integer result = (Integer) statusStack.getFirst();
return result.intValue();
}
/**
* Reset the parser to the initial state without resetting the underlying
* reader.
*/
public void reset(){
token = null;
status = S_INIT;
handlerStatusStack = null;
}
/**
* Reset the parser to the initial state with a new character reader.
*
* @param in the new character reader.
*/
public void reset(Reader in){
lexer.yyreset(in);
reset();
}
/**
* @return The position of the beginning of the current token.
*/
public int getPosition(){
return lexer.getPosition();
}
public Object parse(String s) throws ParseException {
return parse(s, (ContainerFactory) null);
}
public Object parse(String s, ContainerFactory containerFactory)
throws ParseException {
StringReader in = new StringReader(s);
try {
return parse(in, containerFactory);
}
catch(IOException ie){
/*
* Actually it will never happen.
*/
throw new ParseException(-1,
ParseException.ERROR_UNEXPECTED_EXCEPTION, ie);
}
}
public Object parse(Reader in) throws IOException, ParseException {
return parse(in, (ContainerFactory) null);
}
/**
* Parse JSON text into java object from the input source.
*
* @param in the input source.
* @param containerFactory use this factory to create your own JSON
* object and JSON array containers.
* @return Instance of the following:
* com.orsoncharts.util.json.simple.JSONObject,
* com.orsoncharts.util.json.simple.JSONArray,
* java.lang.String,
* java.lang.Number,
* java.lang.Boolean,
* null
*
* @throws IOException if there is an I/O problem.
* @throws ParseException if there is a parsing problem.
*/
@SuppressWarnings("unchecked")
public Object parse(Reader in, ContainerFactory containerFactory)
throws IOException, ParseException{
reset(in);
LinkedList<Integer> statusStack = new LinkedList<Integer>();
LinkedList<Object> valueStack = new LinkedList<Object>();
try {
do {
nextToken();
switch(status) {
case S_INIT:
switch(token.type) {
case Yytoken.TYPE_VALUE:
status=S_IN_FINISHED_VALUE;
statusStack.addFirst(new Integer(status));
valueStack.addFirst(token.value);
break;
case Yytoken.TYPE_LEFT_BRACE:
status=S_IN_OBJECT;
statusStack.addFirst(new Integer(status));
valueStack.addFirst(createObjectContainer(containerFactory));
break;
case Yytoken.TYPE_LEFT_SQUARE:
status=S_IN_ARRAY;
statusStack.addFirst(new Integer(status));
valueStack.addFirst(createArrayContainer(containerFactory));
break;
default:
status=S_IN_ERROR;
}//inner switch
break;
case S_IN_FINISHED_VALUE:
if (token.type == Yytoken.TYPE_EOF) {
return valueStack.removeFirst();
} else {
throw new ParseException(getPosition(),
ParseException.ERROR_UNEXPECTED_TOKEN, token);
}
case S_IN_OBJECT:
switch(token.type) {
case Yytoken.TYPE_COMMA:
break;
case Yytoken.TYPE_VALUE:
if (token.value instanceof String) {
String key = (String) token.value;
valueStack.addFirst(key);
status = S_PASSED_PAIR_KEY;
statusStack.addFirst(new Integer(status));
}
else{
status = S_IN_ERROR;
}
break;
case Yytoken.TYPE_RIGHT_BRACE:
if (valueStack.size() > 1) {
statusStack.removeFirst();
valueStack.removeFirst();
status = peekStatus(statusStack);
}
else{
status = S_IN_FINISHED_VALUE;
}
break;
default:
status = S_IN_ERROR;
break;
}//inner switch
break;
case S_PASSED_PAIR_KEY:
switch(token.type) {
case Yytoken.TYPE_COLON:
break;
case Yytoken.TYPE_VALUE:
statusStack.removeFirst();
String key = (String) valueStack.removeFirst();
Map parent = (Map) valueStack.getFirst();
parent.put(key,token.value);
status = peekStatus(statusStack);
break;
case Yytoken.TYPE_LEFT_SQUARE:
statusStack.removeFirst();
key = (String) valueStack.removeFirst();
parent = (Map) valueStack.getFirst();
List newArray = createArrayContainer(containerFactory);
parent.put(key,newArray);
status = S_IN_ARRAY;
statusStack.addFirst(new Integer(status));
valueStack.addFirst(newArray);
break;
case Yytoken.TYPE_LEFT_BRACE:
statusStack.removeFirst();
key = (String) valueStack.removeFirst();
parent = (Map) valueStack.getFirst();
Map newObject = createObjectContainer(containerFactory);
parent.put(key, newObject);
status = S_IN_OBJECT;
statusStack.addFirst(new Integer(status));
valueStack.addFirst(newObject);
break;
default:
status = S_IN_ERROR;
}
break;
case S_IN_ARRAY:
switch(token.type) {
case Yytoken.TYPE_COMMA:
break;
case Yytoken.TYPE_VALUE:
List val = (List) valueStack.getFirst();
val.add(token.value);
break;
case Yytoken.TYPE_RIGHT_SQUARE:
if (valueStack.size() > 1) {
statusStack.removeFirst();
valueStack.removeFirst();
status = peekStatus(statusStack);
}
else {
status = S_IN_FINISHED_VALUE;
}
break;
case Yytoken.TYPE_LEFT_BRACE:
val = (List) valueStack.getFirst();
Map newObject = createObjectContainer(containerFactory);
val.add(newObject);
status = S_IN_OBJECT;
statusStack.addFirst(new Integer(status));
valueStack.addFirst(newObject);
break;
case Yytoken.TYPE_LEFT_SQUARE:
val = (List) valueStack.getFirst();
List newArray = createArrayContainer(containerFactory);
val.add(newArray);
status = S_IN_ARRAY;
statusStack.addFirst(new Integer(status));
valueStack.addFirst(newArray);
break;
default:
status = S_IN_ERROR;
}//inner switch
break;
case S_IN_ERROR:
throw new ParseException(getPosition(),
ParseException.ERROR_UNEXPECTED_TOKEN, token);
}//switch
if (status == S_IN_ERROR) {
throw new ParseException(getPosition(),
ParseException.ERROR_UNEXPECTED_TOKEN, token);
}
} while (token.type != Yytoken.TYPE_EOF);
}
catch (IOException ie) {
throw ie;
}
throw new ParseException(getPosition(),
ParseException.ERROR_UNEXPECTED_TOKEN, token);
}
private void nextToken() throws ParseException, IOException {
token = lexer.yylex();
if (token == null) {
token = new Yytoken(Yytoken.TYPE_EOF, null);
}
}
private Map createObjectContainer(ContainerFactory containerFactory) {
if (containerFactory == null) {
return new JSONObject();
}
Map m = containerFactory.createObjectContainer();
if (m == null) {
return new JSONObject();
}
return m;
}
private List createArrayContainer(ContainerFactory containerFactory) {
if (containerFactory == null) {
return new JSONArray();
}
List l = containerFactory.creatArrayContainer();
if (l == null) {
return new JSONArray();
}
return l;
}
public void parse(String s, ContentHandler contentHandler)
throws ParseException {
parse(s, contentHandler, false);
}
public void parse(String s, ContentHandler contentHandler, boolean isResume)
throws ParseException{
StringReader in = new StringReader(s);
try{
parse(in, contentHandler, isResume);
}
catch(IOException ie) {
/*
* Actually it will never happen.
*/
throw new ParseException(-1,
ParseException.ERROR_UNEXPECTED_EXCEPTION, ie);
}
}
public void parse(Reader in, ContentHandler contentHandler)
throws IOException, ParseException {
parse(in, contentHandler, false);
}
/**
* Stream processing of JSON text.
*
* @see ContentHandler
*
* @param in the input.
* @param contentHandler the content handler.
* @param isResume indicates if it continues previous parsing operation.
* If set to true, resume parsing the old stream, and parameter 'in'
* will be ignored. If this method is called for the first time in
* this instance, isResume will be ignored.
*
* @throws IOException if there is an I/O problem.
* @throws ParseException if there is a parsing problem.
*/
public void parse(Reader in, ContentHandler contentHandler,
boolean isResume) throws IOException, ParseException {
if (!isResume) {
reset(in);
handlerStatusStack = new LinkedList<Integer>();
}
else{
if (handlerStatusStack == null) {
isResume = false;
reset(in);
handlerStatusStack = new LinkedList<Integer>();
}
}
LinkedList<Integer> statusStack = handlerStatusStack;
try {
do {
switch(status) {
case S_INIT:
contentHandler.startJSON();
nextToken();
switch(token.type) {
case Yytoken.TYPE_VALUE:
status=S_IN_FINISHED_VALUE;
statusStack.addFirst(new Integer(status));
if (!contentHandler.primitive(token.value)) {
return;
}
break;
case Yytoken.TYPE_LEFT_BRACE:
status = S_IN_OBJECT;
statusStack.addFirst(new Integer(status));
if (!contentHandler.startObject()) {
return;
}
break;
case Yytoken.TYPE_LEFT_SQUARE:
status = S_IN_ARRAY;
statusStack.addFirst(new Integer(status));
if (!contentHandler.startArray()) {
return;
}
break;
default:
status = S_IN_ERROR;
}//inner switch
break;
case S_IN_FINISHED_VALUE:
nextToken();
if (token.type == Yytoken.TYPE_EOF) {
contentHandler.endJSON();
status = S_END;
return;
}
else {
status = S_IN_ERROR;
throw new ParseException(getPosition(),
ParseException.ERROR_UNEXPECTED_TOKEN, token);
}
case S_IN_OBJECT:
nextToken();
switch(token.type) {
case Yytoken.TYPE_COMMA:
break;
case Yytoken.TYPE_VALUE:
if (token.value instanceof String) {
String key = (String) token.value;
status = S_PASSED_PAIR_KEY;
statusStack.addFirst(new Integer(status));
if (!contentHandler.startObjectEntry(key)) {
return;
}
}
else {
status = S_IN_ERROR;
}
break;
case Yytoken.TYPE_RIGHT_BRACE:
if (statusStack.size() > 1) {
statusStack.removeFirst();
status = peekStatus(statusStack);
}
else{
status = S_IN_FINISHED_VALUE;
}
if (!contentHandler.endObject()) {
return;
}
break;
default:
status = S_IN_ERROR;
break;
}//inner switch
break;
case S_PASSED_PAIR_KEY:
nextToken();
switch(token.type) {
case Yytoken.TYPE_COLON:
break;
case Yytoken.TYPE_VALUE:
statusStack.removeFirst();
status = peekStatus(statusStack);
if (!contentHandler.primitive(token.value)) {
return;
}
if (!contentHandler.endObjectEntry()) {
return;
}
break;
case Yytoken.TYPE_LEFT_SQUARE:
statusStack.removeFirst();
statusStack.addFirst(new Integer(S_IN_PAIR_VALUE));
status = S_IN_ARRAY;
statusStack.addFirst(new Integer(status));
if (!contentHandler.startArray()) {
return;
}
break;
case Yytoken.TYPE_LEFT_BRACE:
statusStack.removeFirst();
statusStack.addFirst(new Integer(S_IN_PAIR_VALUE));
status = S_IN_OBJECT;
statusStack.addFirst(new Integer(status));
if (!contentHandler.startObject()) {
return;
}
break;
default:
status = S_IN_ERROR;
}
break;
case S_IN_PAIR_VALUE:
/*
* S_IN_PAIR_VALUE is just a marker to indicate the end of
* an object entry, it doesn't proccess any token,
* therefore delay consuming token until next round.
*/
statusStack.removeFirst();
status = peekStatus(statusStack);
if (!contentHandler.endObjectEntry()) {
return;
}
break;
case S_IN_ARRAY:
nextToken();
switch(token.type){
case Yytoken.TYPE_COMMA:
break;
case Yytoken.TYPE_VALUE:
if (!contentHandler.primitive(token.value)) {
return;
}
break;
case Yytoken.TYPE_RIGHT_SQUARE:
if (statusStack.size() > 1) {
statusStack.removeFirst();
status = peekStatus(statusStack);
}
else{
status = S_IN_FINISHED_VALUE;
}
if (!contentHandler.endArray()) {
return;
}
break;
case Yytoken.TYPE_LEFT_BRACE:
status = S_IN_OBJECT;
statusStack.addFirst(new Integer(status));
if (!contentHandler.startObject()) {
return;
}
break;
case Yytoken.TYPE_LEFT_SQUARE:
status = S_IN_ARRAY;
statusStack.addFirst(new Integer(status));
if (!contentHandler.startArray()) {
return;
}
break;
default:
status = S_IN_ERROR;
}//inner switch
break;
case S_END:
return;
case S_IN_ERROR:
throw new ParseException(getPosition(),
ParseException.ERROR_UNEXPECTED_TOKEN, token);
}//switch
if (status == S_IN_ERROR) {
throw new ParseException(getPosition(),
ParseException.ERROR_UNEXPECTED_TOKEN, token);
}
} while(token.type != Yytoken.TYPE_EOF);
}
catch (IOException ie) {
status = S_IN_ERROR;
throw ie;
}
catch (ParseException pe) {
status = S_IN_ERROR;
throw pe;
}
catch (RuntimeException re) {
status = S_IN_ERROR;
throw re;
}
catch (Error e) {
status = S_IN_ERROR;
throw e;
}
status = S_IN_ERROR;
throw new ParseException(getPosition(),
ParseException.ERROR_UNEXPECTED_TOKEN, token);
}
}