michael@0: /* michael@0: * $Id: JSONParser.java,v 1.1 2006/04/15 14:10:48 platform Exp $ michael@0: * Created on 2006-4-15 michael@0: */ michael@0: package org.json.simple.parser; michael@0: michael@0: import java.io.IOException; michael@0: import java.io.Reader; michael@0: import java.io.StringReader; michael@0: import java.util.LinkedList; michael@0: import java.util.List; michael@0: import java.util.Map; michael@0: michael@0: import org.json.simple.JSONArray; michael@0: import org.json.simple.JSONObject; michael@0: michael@0: michael@0: /** michael@0: * Parser for JSON text. Please note that JSONParser is NOT thread-safe. michael@0: * michael@0: * @author FangYidong michael@0: */ michael@0: public class JSONParser { michael@0: public static final int S_INIT=0; michael@0: public static final int S_IN_FINISHED_VALUE=1;//string,number,boolean,null,object,array michael@0: public static final int S_IN_OBJECT=2; michael@0: public static final int S_IN_ARRAY=3; michael@0: public static final int S_PASSED_PAIR_KEY=4; michael@0: public static final int S_IN_PAIR_VALUE=5; michael@0: public static final int S_END=6; michael@0: public static final int S_IN_ERROR=-1; michael@0: michael@0: private LinkedList handlerStatusStack; michael@0: private Yylex lexer = new Yylex((Reader)null); michael@0: private Yytoken token = null; michael@0: private int status = S_INIT; michael@0: michael@0: private int peekStatus(LinkedList statusStack){ michael@0: if(statusStack.size()==0) michael@0: return -1; michael@0: Integer status=(Integer)statusStack.getFirst(); michael@0: return status.intValue(); michael@0: } michael@0: michael@0: /** michael@0: * Reset the parser to the initial state without resetting the underlying reader. michael@0: * michael@0: */ michael@0: public void reset(){ michael@0: token = null; michael@0: status = S_INIT; michael@0: handlerStatusStack = null; michael@0: } michael@0: michael@0: /** michael@0: * Reset the parser to the initial state with a new character reader. michael@0: * michael@0: * @param in - The new character reader. michael@0: * @throws IOException michael@0: * @throws ParseException michael@0: */ michael@0: public void reset(Reader in){ michael@0: lexer.yyreset(in); michael@0: reset(); michael@0: } michael@0: michael@0: /** michael@0: * @return The position of the beginning of the current token. michael@0: */ michael@0: public int getPosition(){ michael@0: return lexer.getPosition(); michael@0: } michael@0: michael@0: public Object parse(String s) throws ParseException{ michael@0: return parse(s, (ContainerFactory)null); michael@0: } michael@0: michael@0: public Object parse(String s, ContainerFactory containerFactory) throws ParseException{ michael@0: StringReader in=new StringReader(s); michael@0: try{ michael@0: return parse(in, containerFactory); michael@0: } michael@0: catch(IOException ie){ michael@0: /* michael@0: * Actually it will never happen. michael@0: */ michael@0: throw new ParseException(-1, ParseException.ERROR_UNEXPECTED_EXCEPTION, ie); michael@0: } michael@0: } michael@0: michael@0: public Object parse(Reader in) throws IOException, ParseException{ michael@0: return parse(in, (ContainerFactory)null); michael@0: } michael@0: michael@0: /** michael@0: * Parse JSON text into java object from the input source. michael@0: * michael@0: * @param in michael@0: * @param containerFactory - Use this factory to createyour own JSON object and JSON array containers. michael@0: * @return Instance of the following: michael@0: * org.json.simple.JSONObject, michael@0: * org.json.simple.JSONArray, michael@0: * java.lang.String, michael@0: * java.lang.Number, michael@0: * java.lang.Boolean, michael@0: * null michael@0: * michael@0: * @throws IOException michael@0: * @throws ParseException michael@0: */ michael@0: public Object parse(Reader in, ContainerFactory containerFactory) throws IOException, ParseException{ michael@0: reset(in); michael@0: LinkedList statusStack = new LinkedList(); michael@0: LinkedList valueStack = new LinkedList(); michael@0: michael@0: try{ michael@0: do{ michael@0: nextToken(); michael@0: switch(status){ michael@0: case S_INIT: michael@0: switch(token.type){ michael@0: case Yytoken.TYPE_VALUE: michael@0: status=S_IN_FINISHED_VALUE; michael@0: statusStack.addFirst(new Integer(status)); michael@0: valueStack.addFirst(token.value); michael@0: break; michael@0: case Yytoken.TYPE_LEFT_BRACE: michael@0: status=S_IN_OBJECT; michael@0: statusStack.addFirst(new Integer(status)); michael@0: valueStack.addFirst(createObjectContainer(containerFactory)); michael@0: break; michael@0: case Yytoken.TYPE_LEFT_SQUARE: michael@0: status=S_IN_ARRAY; michael@0: statusStack.addFirst(new Integer(status)); michael@0: valueStack.addFirst(createArrayContainer(containerFactory)); michael@0: break; michael@0: default: michael@0: status=S_IN_ERROR; michael@0: }//inner switch michael@0: break; michael@0: michael@0: case S_IN_FINISHED_VALUE: michael@0: if(token.type==Yytoken.TYPE_EOF) michael@0: return valueStack.removeFirst(); michael@0: else michael@0: throw new ParseException(getPosition(), ParseException.ERROR_UNEXPECTED_TOKEN, token); michael@0: michael@0: case S_IN_OBJECT: michael@0: switch(token.type){ michael@0: case Yytoken.TYPE_COMMA: michael@0: break; michael@0: case Yytoken.TYPE_VALUE: michael@0: if(token.value instanceof String){ michael@0: String key=(String)token.value; michael@0: valueStack.addFirst(key); michael@0: status=S_PASSED_PAIR_KEY; michael@0: statusStack.addFirst(new Integer(status)); michael@0: } michael@0: else{ michael@0: status=S_IN_ERROR; michael@0: } michael@0: break; michael@0: case Yytoken.TYPE_RIGHT_BRACE: michael@0: if(valueStack.size()>1){ michael@0: statusStack.removeFirst(); michael@0: valueStack.removeFirst(); michael@0: status=peekStatus(statusStack); michael@0: } michael@0: else{ michael@0: status=S_IN_FINISHED_VALUE; michael@0: } michael@0: break; michael@0: default: michael@0: status=S_IN_ERROR; michael@0: break; michael@0: }//inner switch michael@0: break; michael@0: michael@0: case S_PASSED_PAIR_KEY: michael@0: switch(token.type){ michael@0: case Yytoken.TYPE_COLON: michael@0: break; michael@0: case Yytoken.TYPE_VALUE: michael@0: statusStack.removeFirst(); michael@0: String key=(String)valueStack.removeFirst(); michael@0: Map parent=(Map)valueStack.getFirst(); michael@0: parent.put(key,token.value); michael@0: status=peekStatus(statusStack); michael@0: break; michael@0: case Yytoken.TYPE_LEFT_SQUARE: michael@0: statusStack.removeFirst(); michael@0: key=(String)valueStack.removeFirst(); michael@0: parent=(Map)valueStack.getFirst(); michael@0: List newArray=createArrayContainer(containerFactory); michael@0: parent.put(key,newArray); michael@0: status=S_IN_ARRAY; michael@0: statusStack.addFirst(new Integer(status)); michael@0: valueStack.addFirst(newArray); michael@0: break; michael@0: case Yytoken.TYPE_LEFT_BRACE: michael@0: statusStack.removeFirst(); michael@0: key=(String)valueStack.removeFirst(); michael@0: parent=(Map)valueStack.getFirst(); michael@0: Map newObject=createObjectContainer(containerFactory); michael@0: parent.put(key,newObject); michael@0: status=S_IN_OBJECT; michael@0: statusStack.addFirst(new Integer(status)); michael@0: valueStack.addFirst(newObject); michael@0: break; michael@0: default: michael@0: status=S_IN_ERROR; michael@0: } michael@0: break; michael@0: michael@0: case S_IN_ARRAY: michael@0: switch(token.type){ michael@0: case Yytoken.TYPE_COMMA: michael@0: break; michael@0: case Yytoken.TYPE_VALUE: michael@0: List val=(List)valueStack.getFirst(); michael@0: val.add(token.value); michael@0: break; michael@0: case Yytoken.TYPE_RIGHT_SQUARE: michael@0: if(valueStack.size()>1){ michael@0: statusStack.removeFirst(); michael@0: valueStack.removeFirst(); michael@0: status=peekStatus(statusStack); michael@0: } michael@0: else{ michael@0: status=S_IN_FINISHED_VALUE; michael@0: } michael@0: break; michael@0: case Yytoken.TYPE_LEFT_BRACE: michael@0: val=(List)valueStack.getFirst(); michael@0: Map newObject=createObjectContainer(containerFactory); michael@0: val.add(newObject); michael@0: status=S_IN_OBJECT; michael@0: statusStack.addFirst(new Integer(status)); michael@0: valueStack.addFirst(newObject); michael@0: break; michael@0: case Yytoken.TYPE_LEFT_SQUARE: michael@0: val=(List)valueStack.getFirst(); michael@0: List newArray=createArrayContainer(containerFactory); michael@0: val.add(newArray); michael@0: status=S_IN_ARRAY; michael@0: statusStack.addFirst(new Integer(status)); michael@0: valueStack.addFirst(newArray); michael@0: break; michael@0: default: michael@0: status=S_IN_ERROR; michael@0: }//inner switch michael@0: break; michael@0: case S_IN_ERROR: michael@0: throw new ParseException(getPosition(), ParseException.ERROR_UNEXPECTED_TOKEN, token); michael@0: }//switch michael@0: if(status==S_IN_ERROR){ michael@0: throw new ParseException(getPosition(), ParseException.ERROR_UNEXPECTED_TOKEN, token); michael@0: } michael@0: }while(token.type!=Yytoken.TYPE_EOF); michael@0: } michael@0: catch(IOException ie){ michael@0: throw ie; michael@0: } michael@0: michael@0: throw new ParseException(getPosition(), ParseException.ERROR_UNEXPECTED_TOKEN, token); michael@0: } michael@0: michael@0: private void nextToken() throws ParseException, IOException{ michael@0: token = lexer.yylex(); michael@0: if(token == null) michael@0: token = new Yytoken(Yytoken.TYPE_EOF, null); michael@0: } michael@0: michael@0: private Map createObjectContainer(ContainerFactory containerFactory){ michael@0: if(containerFactory == null) michael@0: return new JSONObject(); michael@0: Map m = containerFactory.createObjectContainer(); michael@0: michael@0: if(m == null) michael@0: return new JSONObject(); michael@0: return m; michael@0: } michael@0: michael@0: private List createArrayContainer(ContainerFactory containerFactory){ michael@0: if(containerFactory == null) michael@0: return new JSONArray(); michael@0: List l = containerFactory.creatArrayContainer(); michael@0: michael@0: if(l == null) michael@0: return new JSONArray(); michael@0: return l; michael@0: } michael@0: michael@0: public void parse(String s, ContentHandler contentHandler) throws ParseException{ michael@0: parse(s, contentHandler, false); michael@0: } michael@0: michael@0: public void parse(String s, ContentHandler contentHandler, boolean isResume) throws ParseException{ michael@0: StringReader in=new StringReader(s); michael@0: try{ michael@0: parse(in, contentHandler, isResume); michael@0: } michael@0: catch(IOException ie){ michael@0: /* michael@0: * Actually it will never happen. michael@0: */ michael@0: throw new ParseException(-1, ParseException.ERROR_UNEXPECTED_EXCEPTION, ie); michael@0: } michael@0: } michael@0: michael@0: public void parse(Reader in, ContentHandler contentHandler) throws IOException, ParseException{ michael@0: parse(in, contentHandler, false); michael@0: } michael@0: michael@0: /** michael@0: * Stream processing of JSON text. michael@0: * michael@0: * @see ContentHandler michael@0: * michael@0: * @param in michael@0: * @param contentHandler michael@0: * @param isResume - Indicates if it continues previous parsing operation. michael@0: * If set to true, resume parsing the old stream, and parameter 'in' will be ignored. michael@0: * If this method is called for the first time in this instance, isResume will be ignored. michael@0: * michael@0: * @throws IOException michael@0: * @throws ParseException michael@0: */ michael@0: public void parse(Reader in, ContentHandler contentHandler, boolean isResume) throws IOException, ParseException{ michael@0: if(!isResume){ michael@0: reset(in); michael@0: handlerStatusStack = new LinkedList(); michael@0: } michael@0: else{ michael@0: if(handlerStatusStack == null){ michael@0: isResume = false; michael@0: reset(in); michael@0: handlerStatusStack = new LinkedList(); michael@0: } michael@0: } michael@0: michael@0: LinkedList statusStack = handlerStatusStack; michael@0: michael@0: try{ michael@0: do{ michael@0: switch(status){ michael@0: case S_INIT: michael@0: contentHandler.startJSON(); michael@0: nextToken(); michael@0: switch(token.type){ michael@0: case Yytoken.TYPE_VALUE: michael@0: status=S_IN_FINISHED_VALUE; michael@0: statusStack.addFirst(new Integer(status)); michael@0: if(!contentHandler.primitive(token.value)) michael@0: return; michael@0: break; michael@0: case Yytoken.TYPE_LEFT_BRACE: michael@0: status=S_IN_OBJECT; michael@0: statusStack.addFirst(new Integer(status)); michael@0: if(!contentHandler.startObject()) michael@0: return; michael@0: break; michael@0: case Yytoken.TYPE_LEFT_SQUARE: michael@0: status=S_IN_ARRAY; michael@0: statusStack.addFirst(new Integer(status)); michael@0: if(!contentHandler.startArray()) michael@0: return; michael@0: break; michael@0: default: michael@0: status=S_IN_ERROR; michael@0: }//inner switch michael@0: break; michael@0: michael@0: case S_IN_FINISHED_VALUE: michael@0: nextToken(); michael@0: if(token.type==Yytoken.TYPE_EOF){ michael@0: contentHandler.endJSON(); michael@0: status = S_END; michael@0: return; michael@0: } michael@0: else{ michael@0: status = S_IN_ERROR; michael@0: throw new ParseException(getPosition(), ParseException.ERROR_UNEXPECTED_TOKEN, token); michael@0: } michael@0: michael@0: case S_IN_OBJECT: michael@0: nextToken(); michael@0: switch(token.type){ michael@0: case Yytoken.TYPE_COMMA: michael@0: break; michael@0: case Yytoken.TYPE_VALUE: michael@0: if(token.value instanceof String){ michael@0: String key=(String)token.value; michael@0: status=S_PASSED_PAIR_KEY; michael@0: statusStack.addFirst(new Integer(status)); michael@0: if(!contentHandler.startObjectEntry(key)) michael@0: return; michael@0: } michael@0: else{ michael@0: status=S_IN_ERROR; michael@0: } michael@0: break; michael@0: case Yytoken.TYPE_RIGHT_BRACE: michael@0: if(statusStack.size()>1){ michael@0: statusStack.removeFirst(); michael@0: status=peekStatus(statusStack); michael@0: } michael@0: else{ michael@0: status=S_IN_FINISHED_VALUE; michael@0: } michael@0: if(!contentHandler.endObject()) michael@0: return; michael@0: break; michael@0: default: michael@0: status=S_IN_ERROR; michael@0: break; michael@0: }//inner switch michael@0: break; michael@0: michael@0: case S_PASSED_PAIR_KEY: michael@0: nextToken(); michael@0: switch(token.type){ michael@0: case Yytoken.TYPE_COLON: michael@0: break; michael@0: case Yytoken.TYPE_VALUE: michael@0: statusStack.removeFirst(); michael@0: status=peekStatus(statusStack); michael@0: if(!contentHandler.primitive(token.value)) michael@0: return; michael@0: if(!contentHandler.endObjectEntry()) michael@0: return; michael@0: break; michael@0: case Yytoken.TYPE_LEFT_SQUARE: michael@0: statusStack.removeFirst(); michael@0: statusStack.addFirst(new Integer(S_IN_PAIR_VALUE)); michael@0: status=S_IN_ARRAY; michael@0: statusStack.addFirst(new Integer(status)); michael@0: if(!contentHandler.startArray()) michael@0: return; michael@0: break; michael@0: case Yytoken.TYPE_LEFT_BRACE: michael@0: statusStack.removeFirst(); michael@0: statusStack.addFirst(new Integer(S_IN_PAIR_VALUE)); michael@0: status=S_IN_OBJECT; michael@0: statusStack.addFirst(new Integer(status)); michael@0: if(!contentHandler.startObject()) michael@0: return; michael@0: break; michael@0: default: michael@0: status=S_IN_ERROR; michael@0: } michael@0: break; michael@0: michael@0: case S_IN_PAIR_VALUE: michael@0: /* michael@0: * S_IN_PAIR_VALUE is just a marker to indicate the end of an object entry, it doesn't proccess any token, michael@0: * therefore delay consuming token until next round. michael@0: */ michael@0: statusStack.removeFirst(); michael@0: status = peekStatus(statusStack); michael@0: if(!contentHandler.endObjectEntry()) michael@0: return; michael@0: break; michael@0: michael@0: case S_IN_ARRAY: michael@0: nextToken(); michael@0: switch(token.type){ michael@0: case Yytoken.TYPE_COMMA: michael@0: break; michael@0: case Yytoken.TYPE_VALUE: michael@0: if(!contentHandler.primitive(token.value)) michael@0: return; michael@0: break; michael@0: case Yytoken.TYPE_RIGHT_SQUARE: michael@0: if(statusStack.size()>1){ michael@0: statusStack.removeFirst(); michael@0: status=peekStatus(statusStack); michael@0: } michael@0: else{ michael@0: status=S_IN_FINISHED_VALUE; michael@0: } michael@0: if(!contentHandler.endArray()) michael@0: return; michael@0: break; michael@0: case Yytoken.TYPE_LEFT_BRACE: michael@0: status=S_IN_OBJECT; michael@0: statusStack.addFirst(new Integer(status)); michael@0: if(!contentHandler.startObject()) michael@0: return; michael@0: break; michael@0: case Yytoken.TYPE_LEFT_SQUARE: michael@0: status=S_IN_ARRAY; michael@0: statusStack.addFirst(new Integer(status)); michael@0: if(!contentHandler.startArray()) michael@0: return; michael@0: break; michael@0: default: michael@0: status=S_IN_ERROR; michael@0: }//inner switch michael@0: break; michael@0: michael@0: case S_END: michael@0: return; michael@0: michael@0: case S_IN_ERROR: michael@0: throw new ParseException(getPosition(), ParseException.ERROR_UNEXPECTED_TOKEN, token); michael@0: }//switch michael@0: if(status==S_IN_ERROR){ michael@0: throw new ParseException(getPosition(), ParseException.ERROR_UNEXPECTED_TOKEN, token); michael@0: } michael@0: }while(token.type!=Yytoken.TYPE_EOF); michael@0: } michael@0: catch(IOException ie){ michael@0: status = S_IN_ERROR; michael@0: throw ie; michael@0: } michael@0: catch(ParseException pe){ michael@0: status = S_IN_ERROR; michael@0: throw pe; michael@0: } michael@0: catch(RuntimeException re){ michael@0: status = S_IN_ERROR; michael@0: throw re; michael@0: } michael@0: catch(Error e){ michael@0: status = S_IN_ERROR; michael@0: throw e; michael@0: } michael@0: michael@0: status = S_IN_ERROR; michael@0: throw new ParseException(getPosition(), ParseException.ERROR_UNEXPECTED_TOKEN, token); michael@0: } michael@0: }