mirror of
https://github.com/2006-Scape/Parabot.git
synced 2026-07-03 00:37:55 +00:00
545 lines
14 KiB
Java
545 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;
|
|
|
|
@SuppressWarnings("rawtypes")
|
|
private LinkedList handlerStatusStack;
|
|
private Yylex lexer = new Yylex((Reader) null);
|
|
private Yytoken token = null;
|
|
private int status = S_INIT;
|
|
|
|
@SuppressWarnings("rawtypes")
|
|
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
|
|
*/
|
|
@SuppressWarnings({ "unchecked", "rawtypes" })
|
|
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);
|
|
}
|
|
|
|
@SuppressWarnings("rawtypes")
|
|
private Map createObjectContainer(ContainerFactory containerFactory) {
|
|
if (containerFactory == null)
|
|
return new JSONObject();
|
|
Map m = containerFactory.createObjectContainer();
|
|
|
|
if (m == null)
|
|
return new JSONObject();
|
|
return m;
|
|
}
|
|
|
|
@SuppressWarnings("rawtypes")
|
|
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
|
|
*/
|
|
@SuppressWarnings({ "rawtypes", "unchecked" })
|
|
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);
|
|
}
|
|
}
|