+++ /dev/null
-/*--------------------------------------------------------------------------\r
- * Copyright 2007 Taro L. Saito\r
- *\r
- * Licensed under the Apache License, Version 2.0 (the "License");\r
- * you may not use this file except in compliance with the License.\r
- * You may obtain a copy of the License at\r
- *\r
- * http://www.apache.org/licenses/LICENSE-2.0\r
- *\r
- * Unless required by applicable law or agreed to in writing, software\r
- * distributed under the License is distributed on an "AS IS" BASIS,\r
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- * See the License for the specific language governing permissions and\r
- * limitations under the License.\r
- *--------------------------------------------------------------------------*/\r
-//--------------------------------------\r
-// XerialJ Project\r
-//\r
-// JSONPullParser.java\r
-// Since: May 8, 2007\r
-//\r
-// $URL: http://dev.utgenome.org/svn/utgb/trunk/common/src/org/utgenome/json/JSONPullParser.java $ \r
-// $Author: leo $\r
-//--------------------------------------\r
-package org.xerial.json;\r
-\r
-import java.io.IOException;\r
-import java.io.InputStream;\r
-import java.io.Reader;\r
-\r
-import org.antlr.runtime.ANTLRInputStream;\r
-import org.antlr.runtime.ANTLRReaderStream;\r
-import org.antlr.runtime.ANTLRStringStream;\r
-import org.antlr.runtime.CharStream;\r
-import org.antlr.runtime.Token;\r
-import org.xerial.json.impl.JSONLexer;\r
-import org.xerial.util.ArrayDeque;\r
-import org.xerial.util.log.Logger;\r
-\r
-/**\r
- * Pull Parser for JSON data\r
- * \r
- * @author leo\r
- * \r
- */\r
-public class JSONPullParser\r
-{\r
- private static enum ParseState {\r
- Root, InObject, InArray, Key, KeyedValue\r
- }\r
-\r
- private static Logger _logger = Logger.getLogger(JSONPullParser.class);\r
- private JSONLexer _lexer = new JSONLexer();\r
-\r
- private ArrayDeque<ParseState> parseStateStack = new ArrayDeque<ParseState>();\r
- private ArrayDeque<String> keyStack = new ArrayDeque<String>();\r
-\r
- private JSONPullParserEvent lastReportedEvent = null;\r
- private int currentDepth = 0;\r
-\r
- public JSONPullParser(String jsonString)\r
- {\r
- reset(jsonString);\r
- }\r
-\r
- public JSONPullParser(JSONObject jsonObject)\r
- {\r
- reset(jsonObject);\r
- }\r
-\r
- public JSONPullParser(InputStream jsonStream) throws IOException\r
- {\r
- reset(jsonStream);\r
- }\r
-\r
- public JSONPullParser(Reader reader) throws IOException\r
- {\r
- reset(reader);\r
- }\r
-\r
- public void reset(String jsonString)\r
- {\r
- reset(new ANTLRStringStream(jsonString));\r
- }\r
-\r
- public void reset(JSONObject jsonObject)\r
- {\r
- reset(new ANTLRStringStream(jsonObject.toJSONString()));\r
- }\r
-\r
- public void reset(InputStream jsonStream) throws IOException\r
- {\r
- reset(new ANTLRInputStream(jsonStream));\r
- }\r
-\r
- public void reset(Reader reader) throws IOException\r
- {\r
- reset(new ANTLRReaderStream(reader));\r
- }\r
-\r
- private void reset(CharStream newStream)\r
- {\r
- _lexer.reset();\r
- _lexer.setCharStream(newStream);\r
-\r
- parseStateStack.clear();\r
- keyStack.clear();\r
-\r
- lastReportedEvent = null;\r
- currentDepth = 0;\r
-\r
- parseStateStack.addLast(ParseState.Root);\r
- }\r
-\r
- private ParseState getCurrentParseState()\r
- {\r
- return parseStateStack.getLast();\r
- }\r
-\r
- private void validateParseState(ParseState... possibleParseState) throws JSONException\r
- {\r
- ParseState current = getCurrentParseState();\r
- for (ParseState ps : possibleParseState)\r
- {\r
- if (ps == current)\r
- return;\r
- }\r
- throw new JSONException(JSONErrorCode.InvalidJSONData, "invalid parse state: " + current.name() + " line = "\r
- + _lexer.getLine());\r
- }\r
-\r
- private void popKeyStack()\r
- {\r
- if (getCurrentParseState() == ParseState.Key)\r
- keyStack.removeLast();\r
- }\r
-\r
- private void pushParseState(ParseState ps)\r
- {\r
- parseStateStack.addLast(ps);\r
- // _logger.trace("push: " + StringUtil.join(parseStateStack, ", "));\r
- }\r
-\r
- private void popParseState()\r
- {\r
- parseStateStack.removeLast();\r
- // _logger.trace("pop : " + StringUtil.join(parseStateStack, ", "));\r
- }\r
-\r
- private void valueWithKeyTest()\r
- {\r
- if (getCurrentParseState() == ParseState.Key)\r
- pushParseState(ParseState.KeyedValue);\r
- }\r
-\r
- /**\r
- * Reads the current JSONValue, which is one of {@link JSONObject} ,\r
- * {@link JSONArray}, {@link JSONInteger}, {@link JSONDouble},\r
- * {@link JSONString}, {@link JSONBoolean} and {@link JSONNull}. This\r
- * methods proceeds the parse state up to the end of the returned value.\r
- * \r
- * @return the current JSONValue\r
- * @throws JSONException\r
- * when the current token is not a {@link JSONValue}\r
- */\r
- public JSONValue getValue() throws JSONException\r
- {\r
- if (lastReportedEvent == null)\r
- next();\r
-\r
- while (lastReportedEvent.getEvent() != JSONEvent.EndJSON)\r
- {\r
- switch (lastReportedEvent.getEvent())\r
- {\r
- case String:\r
- return new JSONString(getText());\r
- case Integer:\r
- return new JSONInteger(getText());\r
- case Double:\r
- return new JSONDouble(getText());\r
- case Boolean:\r
- return new JSONBoolean(getText());\r
- case Null:\r
- return new JSONNull();\r
- case StartObject:\r
- return readJSONObject(new JSONObject(), getDepth());\r
- case StartArray:\r
- return readJSONArray(new JSONArray(), getDepth());\r
- default:\r
- next();\r
- }\r
- }\r
- throw new JSONException(JSONErrorCode.JSONValueIsNotFound);\r
- }\r
-\r
- /**\r
- * Reads the current JSONValue as String data\r
- * \r
- * @return\r
- * @throws JSONException\r
- */\r
- public String getValueAsText() throws JSONException\r
- {\r
- if (lastReportedEvent == null)\r
- next();\r
-\r
- while (lastReportedEvent.getEvent() != JSONEvent.EndJSON)\r
- {\r
- switch (lastReportedEvent.getEvent())\r
- {\r
- case String:\r
- case Integer:\r
- case Double:\r
- case Boolean:\r
- return getText();\r
- case Null:\r
- case StartObject:\r
- return readJSONObject(new JSONObject(), getDepth()).toJSONString();\r
- case StartArray:\r
- return readJSONArray(new JSONArray(), getDepth()).toJSONString();\r
- default:\r
- next();\r
- }\r
- }\r
- throw new JSONException(JSONErrorCode.JSONValueIsNotFound);\r
-\r
- }\r
-\r
- private JSONObject readJSONObject(JSONObject jsonObject, int baseObjectDepth) throws JSONException\r
- {\r
- while (true)\r
- {\r
- JSONEvent event = next();\r
- switch (event)\r
- {\r
- case StartObject:\r
- jsonObject.put(getKeyName(), readJSONObject(new JSONObject(), getDepth()));\r
- break;\r
- case EndObject:\r
- if (getDepth() < baseObjectDepth)\r
- return jsonObject;\r
- else\r
- throw new JSONException(JSONErrorCode.ParseError);\r
- case StartArray:\r
- jsonObject.put(getKeyName(), readJSONArray(new JSONArray(), getDepth()));\r
- break;\r
- case EndArray:\r
- throw new JSONException(JSONErrorCode.NotInAJSONObject);\r
- case String:\r
- case Boolean:\r
- case Double:\r
- case Integer:\r
- case Null:\r
- jsonObject.put(getKeyName(), getValue());\r
- break;\r
- case EndJSON:\r
- default:\r
- throw new JSONException(JSONErrorCode.UnexpectedEndOfJSON);\r
- }\r
- }\r
- }\r
-\r
- private JSONArray readJSONArray(JSONArray jsonArray, int baseArrayDepth) throws JSONException\r
- {\r
- while (true)\r
- {\r
- JSONEvent event = next();\r
- switch (event)\r
- {\r
- case StartObject:\r
- jsonArray.add(readJSONObject(new JSONObject(), getDepth()));\r
- break;\r
- case EndObject:\r
- throw new JSONException(JSONErrorCode.ParseError);\r
- case StartArray:\r
- jsonArray.add(readJSONArray(new JSONArray(), getDepth()));\r
- break;\r
- case EndArray:\r
- if (getDepth() < baseArrayDepth)\r
- return jsonArray;\r
- else\r
- throw new JSONException(JSONErrorCode.ParseError);\r
- case String:\r
- case Boolean:\r
- case Double:\r
- case Integer:\r
- case Null:\r
- jsonArray.add(getValue());\r
- break;\r
- case EndJSON:\r
- default:\r
- throw new JSONException(JSONErrorCode.UnexpectedEndOfJSON);\r
- }\r
- }\r
- }\r
-\r
- /**\r
- * Reads the next {@link JSONEvent}\r
- * \r
- * @return the next {@link JSONEvent}. If no more token is available,\r
- * returns {@link JSONEvent#EndJSON}.\r
- * @throws JSONException\r
- * when some syntax error is found.\r
- */\r
- public JSONEvent next() throws JSONException\r
- {\r
- Token token;\r
- while ((token = _lexer.nextToken()) != Token.EOF_TOKEN)\r
- {\r
- if (getCurrentParseState() == ParseState.KeyedValue)\r
- {\r
- keyStack.removeLast();\r
- popParseState();\r
- if (getCurrentParseState() == ParseState.Key)\r
- popParseState();\r
- else\r
- throw new JSONException(JSONErrorCode.ParseError);\r
- }\r
-\r
- int tokenType = token.getType();\r
-\r
- switch (tokenType)\r
- {\r
- case JSONLexer.LBrace:\r
- valueWithKeyTest();\r
- currentDepth++;\r
- pushParseState(ParseState.InObject);\r
- return reportEvent(token, JSONEvent.StartObject);\r
- case JSONLexer.RBrace:\r
- currentDepth--;\r
- validateParseState(ParseState.InObject);\r
- popParseState();\r
- popKeyStack();\r
- return reportEvent(token, JSONEvent.EndObject);\r
- case JSONLexer.LBracket:\r
- valueWithKeyTest();\r
- currentDepth++;\r
- pushParseState(ParseState.InArray);\r
- return reportEvent(token, JSONEvent.StartArray);\r
- case JSONLexer.RBracket:\r
- currentDepth--;\r
- validateParseState(ParseState.InArray);\r
- popParseState();\r
- popKeyStack();\r
- return reportEvent(token, JSONEvent.EndArray);\r
- case JSONLexer.Comma:\r
- validateParseState(ParseState.InArray, ParseState.InObject);\r
- continue;\r
- case JSONLexer.Colon:\r
- validateParseState(ParseState.Key); // next sequence will be a\r
- // keyed value\r
- continue;\r
- case JSONLexer.String:\r
- if (getCurrentParseState() == ParseState.InObject)\r
- {\r
- // key\r
- pushParseState(ParseState.Key);\r
- keyStack.addLast(removeDoubleQuotation(token.getText()));\r
- continue;\r
- }\r
- valueWithKeyTest();\r
- return reportEvent(token, JSONEvent.String);\r
- case JSONLexer.Integer:\r
- valueWithKeyTest();\r
- return reportEvent(token, JSONEvent.Integer);\r
- case JSONLexer.Double:\r
- valueWithKeyTest();\r
- return reportEvent(token, JSONEvent.Double);\r
- case JSONLexer.TRUE:\r
- case JSONLexer.FALSE:\r
- valueWithKeyTest();\r
- return reportEvent(token, JSONEvent.Boolean);\r
- case JSONLexer.NULL:\r
- valueWithKeyTest();\r
- return reportEvent(token, JSONEvent.Null);\r
- }\r
- }\r
-\r
- return JSONEvent.EndJSON;\r
- }\r
-\r
- protected JSONEvent reportEvent(Token token, JSONEvent e)\r
- {\r
- lastReportedEvent = new JSONPullParserEvent(token, e);\r
- return lastReportedEvent.getEvent();\r
- }\r
-\r
- /**\r
- * Gets the current object/array/value key.\r
- * \r
- * @return the current key, or null when the current token is no name array\r
- * or object.\r
- * @throws JSONException\r
- */\r
- public String getKeyName() throws JSONException\r
- {\r
- if (keyStack.isEmpty())\r
- return null;\r
- else\r
- return keyStack.getLast();\r
- }\r
-\r
- public String getText()\r
- {\r
- if (lastReportedEvent.getEvent() == JSONEvent.String)\r
- return removeDoubleQuotation(lastReportedEvent.getToken().getText());\r
- else\r
- return lastReportedEvent.getToken().getText();\r
- }\r
-\r
- private static String removeDoubleQuotation(String text)\r
- {\r
- return text.substring(1, text.length() - 1);\r
- }\r
-\r
- public int getDepth()\r
- {\r
- return currentDepth;\r
- }\r
-\r
-}\r
-\r
-/**\r
- * A pull parser event\r
- * \r
- * @author leo\r
- * \r
- */\r
-class JSONPullParserEvent\r
-{\r
- private Token t;\r
- private JSONEvent event;\r
-\r
- public JSONPullParserEvent(Token t, JSONEvent event)\r
- {\r
- this.t = t;\r
- this.event = event;\r
- }\r
-\r
- public Token getToken()\r
- {\r
- return t;\r
- }\r
-\r
- public void setToken(Token t)\r
- {\r
- this.t = t;\r
- }\r
-\r
- public JSONEvent getEvent()\r
- {\r
- return event;\r
- }\r
-\r
- public void setEvent(JSONEvent event)\r
- {\r
- this.event = event;\r
- }\r
-}\r