OSDN Git Service

imported from subversion repository
[xerial/xerial-core.git] / src / main / java / org / xerial / json / JSONPullParser.java
1 /*--------------------------------------------------------------------------\r
2  *  Copyright 2007 Taro L. Saito\r
3  *\r
4  *  Licensed under the Apache License, Version 2.0 (the "License");\r
5  *  you may not use this file except in compliance with the License.\r
6  *  You may obtain a copy of the License at\r
7  *\r
8  *     http://www.apache.org/licenses/LICENSE-2.0\r
9  *\r
10  *  Unless required by applicable law or agreed to in writing, software\r
11  *  distributed under the License is distributed on an "AS IS" BASIS,\r
12  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
13  *  See the License for the specific language governing permissions and\r
14  *  limitations under the License.\r
15  *--------------------------------------------------------------------------*/\r
16 //--------------------------------------\r
17 // XerialJ Project\r
18 //\r
19 // JSONPullParser.java\r
20 // Since: May 8, 2007\r
21 //\r
22 // $URL: http://dev.utgenome.org/svn/utgb/trunk/common/src/org/utgenome/json/JSONPullParser.java $ \r
23 // $Author: leo $\r
24 //--------------------------------------\r
25 package org.xerial.json;\r
26 \r
27 import java.io.IOException;\r
28 import java.io.InputStream;\r
29 import java.io.Reader;\r
30 \r
31 import org.antlr.runtime.ANTLRInputStream;\r
32 import org.antlr.runtime.ANTLRReaderStream;\r
33 import org.antlr.runtime.ANTLRStringStream;\r
34 import org.antlr.runtime.CharStream;\r
35 import org.antlr.runtime.Token;\r
36 import org.xerial.json.impl.JSONLexer;\r
37 import org.xerial.util.ArrayDeque;\r
38 import org.xerial.util.log.Logger;\r
39 \r
40 /**\r
41  * Pull Parser for JSON data\r
42  * \r
43  * @author leo\r
44  * \r
45  */\r
46 public class JSONPullParser\r
47 {\r
48     private static enum ParseState {\r
49         Root, InObject, InArray, Key, KeyedValue\r
50     }\r
51 \r
52     private static Logger _logger = Logger.getLogger(JSONPullParser.class);\r
53     private JSONLexer _lexer = new JSONLexer();\r
54 \r
55     private ArrayDeque<ParseState> parseStateStack = new ArrayDeque<ParseState>();\r
56     private ArrayDeque<String> keyStack = new ArrayDeque<String>();\r
57 \r
58     private JSONPullParserEvent lastReportedEvent = null;\r
59     private int currentDepth = 0;\r
60 \r
61     public JSONPullParser(String jsonString)\r
62     {\r
63         reset(jsonString);\r
64     }\r
65 \r
66     public JSONPullParser(JSONObject jsonObject)\r
67     {\r
68         reset(jsonObject);\r
69     }\r
70 \r
71     public JSONPullParser(InputStream jsonStream) throws IOException\r
72     {\r
73         reset(jsonStream);\r
74     }\r
75 \r
76     public JSONPullParser(Reader reader) throws IOException\r
77     {\r
78         reset(reader);\r
79     }\r
80 \r
81     public void reset(String jsonString)\r
82     {\r
83         reset(new ANTLRStringStream(jsonString));\r
84     }\r
85 \r
86     public void reset(JSONObject jsonObject)\r
87     {\r
88         reset(new ANTLRStringStream(jsonObject.toJSONString()));\r
89     }\r
90 \r
91     public void reset(InputStream jsonStream) throws IOException\r
92     {\r
93         reset(new ANTLRInputStream(jsonStream));\r
94     }\r
95 \r
96     public void reset(Reader reader) throws IOException\r
97     {\r
98         reset(new ANTLRReaderStream(reader));\r
99     }\r
100 \r
101     private void reset(CharStream newStream)\r
102     {\r
103         _lexer.reset();\r
104         _lexer.setCharStream(newStream);\r
105 \r
106         parseStateStack.clear();\r
107         keyStack.clear();\r
108 \r
109         lastReportedEvent = null;\r
110         currentDepth = 0;\r
111 \r
112         parseStateStack.addLast(ParseState.Root);\r
113     }\r
114 \r
115     private ParseState getCurrentParseState()\r
116     {\r
117         return parseStateStack.getLast();\r
118     }\r
119 \r
120     private void validateParseState(ParseState... possibleParseState) throws JSONException\r
121     {\r
122         ParseState current = getCurrentParseState();\r
123         for (ParseState ps : possibleParseState)\r
124         {\r
125             if (ps == current)\r
126                 return;\r
127         }\r
128         throw new JSONException(JSONErrorCode.InvalidJSONData, "invalid parse state: " + current.name() + " line = "\r
129                 + _lexer.getLine());\r
130     }\r
131 \r
132     private void popKeyStack()\r
133     {\r
134         if (getCurrentParseState() == ParseState.Key)\r
135             keyStack.removeLast();\r
136     }\r
137 \r
138     private void pushParseState(ParseState ps)\r
139     {\r
140         parseStateStack.addLast(ps);\r
141         // _logger.trace("push: " + StringUtil.join(parseStateStack, ", "));\r
142     }\r
143 \r
144     private void popParseState()\r
145     {\r
146         parseStateStack.removeLast();\r
147         // _logger.trace("pop : " + StringUtil.join(parseStateStack, ", "));\r
148     }\r
149 \r
150     private void valueWithKeyTest()\r
151     {\r
152         if (getCurrentParseState() == ParseState.Key)\r
153             pushParseState(ParseState.KeyedValue);\r
154     }\r
155 \r
156     /**\r
157      * Reads the current JSONValue, which is one of {@link JSONObject} ,\r
158      * {@link JSONArray}, {@link JSONInteger}, {@link JSONDouble},\r
159      * {@link JSONString}, {@link JSONBoolean} and {@link JSONNull}. This\r
160      * methods proceeds the parse state up to the end of the returned value.\r
161      * \r
162      * @return the current JSONValue\r
163      * @throws JSONException\r
164      *             when the current token is not a {@link JSONValue}\r
165      */\r
166     public JSONValue getValue() throws JSONException\r
167     {\r
168         if (lastReportedEvent == null)\r
169             next();\r
170 \r
171         while (lastReportedEvent.getEvent() != JSONEvent.EndJSON)\r
172         {\r
173             switch (lastReportedEvent.getEvent())\r
174             {\r
175             case String:\r
176                 return new JSONString(getText());\r
177             case Integer:\r
178                 return new JSONInteger(getText());\r
179             case Double:\r
180                 return new JSONDouble(getText());\r
181             case Boolean:\r
182                 return new JSONBoolean(getText());\r
183             case Null:\r
184                 return new JSONNull();\r
185             case StartObject:\r
186                 return readJSONObject(new JSONObject(), getDepth());\r
187             case StartArray:\r
188                 return readJSONArray(new JSONArray(), getDepth());\r
189             default:\r
190                 next();\r
191             }\r
192         }\r
193         throw new JSONException(JSONErrorCode.JSONValueIsNotFound);\r
194     }\r
195 \r
196     /**\r
197      * Reads the current JSONValue as String data\r
198      * \r
199      * @return\r
200      * @throws JSONException\r
201      */\r
202     public String getValueAsText() throws JSONException\r
203     {\r
204         if (lastReportedEvent == null)\r
205             next();\r
206 \r
207         while (lastReportedEvent.getEvent() != JSONEvent.EndJSON)\r
208         {\r
209             switch (lastReportedEvent.getEvent())\r
210             {\r
211             case String:\r
212             case Integer:\r
213             case Double:\r
214             case Boolean:\r
215                 return getText();\r
216             case Null:\r
217             case StartObject:\r
218                 return readJSONObject(new JSONObject(), getDepth()).toJSONString();\r
219             case StartArray:\r
220                 return readJSONArray(new JSONArray(), getDepth()).toJSONString();\r
221             default:\r
222                 next();\r
223             }\r
224         }\r
225         throw new JSONException(JSONErrorCode.JSONValueIsNotFound);\r
226 \r
227     }\r
228 \r
229     private JSONObject readJSONObject(JSONObject jsonObject, int baseObjectDepth) throws JSONException\r
230     {\r
231         while (true)\r
232         {\r
233             JSONEvent event = next();\r
234             switch (event)\r
235             {\r
236             case StartObject:\r
237                 jsonObject.put(getKeyName(), readJSONObject(new JSONObject(), getDepth()));\r
238                 break;\r
239             case EndObject:\r
240                 if (getDepth() < baseObjectDepth)\r
241                     return jsonObject;\r
242                 else\r
243                     throw new JSONException(JSONErrorCode.ParseError);\r
244             case StartArray:\r
245                 jsonObject.put(getKeyName(), readJSONArray(new JSONArray(), getDepth()));\r
246                 break;\r
247             case EndArray:\r
248                 throw new JSONException(JSONErrorCode.NotInAJSONObject);\r
249             case String:\r
250             case Boolean:\r
251             case Double:\r
252             case Integer:\r
253             case Null:\r
254                 jsonObject.put(getKeyName(), getValue());\r
255                 break;\r
256             case EndJSON:\r
257             default:\r
258                 throw new JSONException(JSONErrorCode.UnexpectedEndOfJSON);\r
259             }\r
260         }\r
261     }\r
262 \r
263     private JSONArray readJSONArray(JSONArray jsonArray, int baseArrayDepth) throws JSONException\r
264     {\r
265         while (true)\r
266         {\r
267             JSONEvent event = next();\r
268             switch (event)\r
269             {\r
270             case StartObject:\r
271                 jsonArray.add(readJSONObject(new JSONObject(), getDepth()));\r
272                 break;\r
273             case EndObject:\r
274                 throw new JSONException(JSONErrorCode.ParseError);\r
275             case StartArray:\r
276                 jsonArray.add(readJSONArray(new JSONArray(), getDepth()));\r
277                 break;\r
278             case EndArray:\r
279                 if (getDepth() < baseArrayDepth)\r
280                     return jsonArray;\r
281                 else\r
282                     throw new JSONException(JSONErrorCode.ParseError);\r
283             case String:\r
284             case Boolean:\r
285             case Double:\r
286             case Integer:\r
287             case Null:\r
288                 jsonArray.add(getValue());\r
289                 break;\r
290             case EndJSON:\r
291             default:\r
292                 throw new JSONException(JSONErrorCode.UnexpectedEndOfJSON);\r
293             }\r
294         }\r
295     }\r
296 \r
297     /**\r
298      * Reads the next {@link JSONEvent}\r
299      * \r
300      * @return the next {@link JSONEvent}. If no more token is available,\r
301      *         returns {@link JSONEvent#EndJSON}.\r
302      * @throws JSONException\r
303      *             when some syntax error is found.\r
304      */\r
305     public JSONEvent next() throws JSONException\r
306     {\r
307         Token token;\r
308         while ((token = _lexer.nextToken()) != Token.EOF_TOKEN)\r
309         {\r
310             if (getCurrentParseState() == ParseState.KeyedValue)\r
311             {\r
312                 keyStack.removeLast();\r
313                 popParseState();\r
314                 if (getCurrentParseState() == ParseState.Key)\r
315                     popParseState();\r
316                 else\r
317                     throw new JSONException(JSONErrorCode.ParseError);\r
318             }\r
319 \r
320             int tokenType = token.getType();\r
321 \r
322             switch (tokenType)\r
323             {\r
324             case JSONLexer.LBrace:\r
325                 valueWithKeyTest();\r
326                 currentDepth++;\r
327                 pushParseState(ParseState.InObject);\r
328                 return reportEvent(token, JSONEvent.StartObject);\r
329             case JSONLexer.RBrace:\r
330                 currentDepth--;\r
331                 validateParseState(ParseState.InObject);\r
332                 popParseState();\r
333                 popKeyStack();\r
334                 return reportEvent(token, JSONEvent.EndObject);\r
335             case JSONLexer.LBracket:\r
336                 valueWithKeyTest();\r
337                 currentDepth++;\r
338                 pushParseState(ParseState.InArray);\r
339                 return reportEvent(token, JSONEvent.StartArray);\r
340             case JSONLexer.RBracket:\r
341                 currentDepth--;\r
342                 validateParseState(ParseState.InArray);\r
343                 popParseState();\r
344                 popKeyStack();\r
345                 return reportEvent(token, JSONEvent.EndArray);\r
346             case JSONLexer.Comma:\r
347                 validateParseState(ParseState.InArray, ParseState.InObject);\r
348                 continue;\r
349             case JSONLexer.Colon:\r
350                 validateParseState(ParseState.Key); // next sequence will be a\r
351                 // keyed value\r
352                 continue;\r
353             case JSONLexer.String:\r
354                 if (getCurrentParseState() == ParseState.InObject)\r
355                 {\r
356                     // key\r
357                     pushParseState(ParseState.Key);\r
358                     keyStack.addLast(removeDoubleQuotation(token.getText()));\r
359                     continue;\r
360                 }\r
361                 valueWithKeyTest();\r
362                 return reportEvent(token, JSONEvent.String);\r
363             case JSONLexer.Integer:\r
364                 valueWithKeyTest();\r
365                 return reportEvent(token, JSONEvent.Integer);\r
366             case JSONLexer.Double:\r
367                 valueWithKeyTest();\r
368                 return reportEvent(token, JSONEvent.Double);\r
369             case JSONLexer.TRUE:\r
370             case JSONLexer.FALSE:\r
371                 valueWithKeyTest();\r
372                 return reportEvent(token, JSONEvent.Boolean);\r
373             case JSONLexer.NULL:\r
374                 valueWithKeyTest();\r
375                 return reportEvent(token, JSONEvent.Null);\r
376             }\r
377         }\r
378 \r
379         return JSONEvent.EndJSON;\r
380     }\r
381 \r
382     protected JSONEvent reportEvent(Token token, JSONEvent e)\r
383     {\r
384         lastReportedEvent = new JSONPullParserEvent(token, e);\r
385         return lastReportedEvent.getEvent();\r
386     }\r
387 \r
388     /**\r
389      * Gets the current object/array/value key.\r
390      * \r
391      * @return the current key, or null when the current token is no name array\r
392      *         or object.\r
393      * @throws JSONException\r
394      */\r
395     public String getKeyName() throws JSONException\r
396     {\r
397         if (keyStack.isEmpty())\r
398             return null;\r
399         else\r
400             return keyStack.getLast();\r
401     }\r
402 \r
403     public String getText()\r
404     {\r
405         if (lastReportedEvent.getEvent() == JSONEvent.String)\r
406             return removeDoubleQuotation(lastReportedEvent.getToken().getText());\r
407         else\r
408             return lastReportedEvent.getToken().getText();\r
409     }\r
410 \r
411     private static String removeDoubleQuotation(String text)\r
412     {\r
413         return text.substring(1, text.length() - 1);\r
414     }\r
415 \r
416     public int getDepth()\r
417     {\r
418         return currentDepth;\r
419     }\r
420 \r
421 }\r
422 \r
423 /**\r
424  * A pull parser event\r
425  * \r
426  * @author leo\r
427  * \r
428  */\r
429 class JSONPullParserEvent\r
430 {\r
431     private Token t;\r
432     private JSONEvent event;\r
433 \r
434     public JSONPullParserEvent(Token t, JSONEvent event)\r
435     {\r
436         this.t = t;\r
437         this.event = event;\r
438     }\r
439 \r
440     public Token getToken()\r
441     {\r
442         return t;\r
443     }\r
444 \r
445     public void setToken(Token t)\r
446     {\r
447         this.t = t;\r
448     }\r
449 \r
450     public JSONEvent getEvent()\r
451     {\r
452         return event;\r
453     }\r
454 \r
455     public void setEvent(JSONEvent event)\r
456     {\r
457         this.event = event;\r
458     }\r
459 }\r