mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
534 lines
14 KiB
Java
534 lines
14 KiB
Java
/*
|
|
* $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);
|
|
}
|
|
}
|