/* * $Id: JSONParser.java,v 1.1 2006/04/15 14:10:48 platform Exp $ * Created on 2006-4-15 */ package org.json.simple.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 org.json.simple.JSONArray; import org.json.simple.JSONObject; /** * Parser for JSON text. Please note that JSONParser is NOT thread-safe. * * @author FangYidong<fangyidong@yahoo.com.cn> */ 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 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 status=(Integer)statusStack.getFirst(); return status.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. * @throws IOException * @throws ParseException */ 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 * @param containerFactory - Use this factory to createyour own JSON object and JSON array containers. * @return Instance of the following: * org.json.simple.JSONObject, * org.json.simple.JSONArray, * java.lang.String, * java.lang.Number, * java.lang.Boolean, * null * * @throws IOException * @throws ParseException */ public Object parse(Reader in, ContainerFactory containerFactory) throws IOException, ParseException{ reset(in); LinkedList statusStack = new LinkedList(); LinkedList valueStack = new LinkedList(); 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 * @param contentHandler * @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 * @throws ParseException */ public void parse(Reader in, ContentHandler contentHandler, boolean isResume) throws IOException, ParseException{ if(!isResume){ reset(in); handlerStatusStack = new LinkedList(); } else{ if(handlerStatusStack == null){ isResume = false; reset(in); handlerStatusStack = new LinkedList(); } } LinkedList 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); } }