-/*
- * $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);
- }
-}