From 6e59e502c2461c11d0e6099f10f65c666acabc45 Mon Sep 17 00:00:00 2001 From: Inderjeet Singh Date: Thu, 14 May 2009 20:18:45 +0000 Subject: [PATCH] Refactored the Async parser interface out of JsonParser into a new class JsonParserAsync. Updated the JsonParser to detect Eof and return a null instead of throwing a ParseException. --- gson/src/main/java/com/google/gson/Gson.java | 8 +- .../main/java/com/google/gson/JsonParser.java | 59 ++-------- .../java/com/google/gson/JsonParserAsync.java | 77 ++++++++++++ .../com/google/gson/JsonParserJavacc.java | 111 +++++++++++------- .../gson/JsonParserJavaccTokenManager.java | 1 + gson/src/main/javacc/JsonParser.jj | 4 + .../com/google/gson/JsonParserAsyncTest.java | 19 +++ .../google/gson/functional/ObjectTest.java | 7 +- .../gson/functional/ReadersWritersTest.java | 20 ++-- 9 files changed, 191 insertions(+), 115 deletions(-) create mode 100644 gson/src/main/java/com/google/gson/JsonParserAsync.java create mode 100644 gson/src/test/java/com/google/gson/JsonParserAsyncTest.java diff --git a/gson/src/main/java/com/google/gson/Gson.java b/gson/src/main/java/com/google/gson/Gson.java index 6be1e41a..61991131 100644 --- a/gson/src/main/java/com/google/gson/Gson.java +++ b/gson/src/main/java/com/google/gson/Gson.java @@ -411,10 +411,7 @@ public final class Gson { @SuppressWarnings("unchecked") public T fromJson(Reader json, Type typeOfT) throws JsonParseException { JsonElement root = new JsonParser().parse(json); - JsonDeserializationContext context = new JsonDeserializationContextDefault( - createDefaultObjectNavigatorFactory(), deserializers, objectConstructor); - T target = (T) context.deserialize(root, typeOfT); - return target; + return fromJson(root, typeOfT); } /** @@ -458,6 +455,9 @@ public final class Gson { */ @SuppressWarnings("unchecked") public T fromJson(JsonElement json, Type typeOfT) throws JsonParseException { + if (json == null) { + return null; + } JsonDeserializationContext context = new JsonDeserializationContextDefault( createDefaultObjectNavigatorFactory(), deserializers, objectConstructor); T target = (T) context.deserialize(json, typeOfT); diff --git a/gson/src/main/java/com/google/gson/JsonParser.java b/gson/src/main/java/com/google/gson/JsonParser.java index cec265f5..4e706a9b 100755 --- a/gson/src/main/java/com/google/gson/JsonParser.java +++ b/gson/src/main/java/com/google/gson/JsonParser.java @@ -15,6 +15,7 @@ */ package com.google.gson; +import java.io.EOFException; import java.io.Reader; import java.io.StringReader; @@ -27,21 +28,6 @@ import java.io.StringReader; */ public final class JsonParser { - /** - * Interface to provide ability to read multiple {@link JsonElement}s from a stream - * asynchronously. - * - * @since 1.4 - */ - public interface AsyncReader { - - /** - * Parse and return one {@link JsonElement} - * @since 1.4 - */ - public JsonElement readElement(); - } - /** * Parses the specified JSON string into a parse tree * @@ -75,41 +61,12 @@ public final class JsonParser { throw new JsonParseException("Failed parsing JSON source: " + json + " to Json", e); } catch (OutOfMemoryError e) { throw new JsonParseException("Failed parsing JSON source: " + json + " to Json", e); - } - } - - /** - * Returns {@link AsyncReader} to allow reading of multiple {@link JsonElement}s from the - * specified reader asynchronously. - * - * @param json The data stream containing JSON elements concatenated to each other. - * @return {@link AsyncReader} for reading {@link JsonElement}s asynchronously. - * @throws JsonParseException if the incoming stream is malformed JSON. - * @since 1.4 - */ - public AsyncReader parseAsync(Reader json) throws JsonParseException { - return new AsyncReaderJavacc(json); - } - - private static class AsyncReaderJavacc implements AsyncReader { - private final JsonParserJavacc parser; - private AsyncReaderJavacc(Reader json) { - parser = new JsonParserJavacc(json); - } - - public JsonElement readElement() { - try { - JsonElement element = parser.parse(); - return element; - } catch (TokenMgrError e) { - throw new JsonParseException("Failed parsing JSON source to Json", e); - } catch (ParseException e) { - throw new JsonParseException("Failed parsing JSON source to Json", e); - } catch (StackOverflowError e) { - throw new JsonParseException("Failed parsing JSON source to Json", e); - } catch (OutOfMemoryError e) { - throw new JsonParseException("Failed parsing JSON source to Json", e); + } catch (JsonParseException e) { + if (e.getCause() instanceof EOFException) { + return null; + } else { + throw e; } - } - } + } + } } diff --git a/gson/src/main/java/com/google/gson/JsonParserAsync.java b/gson/src/main/java/com/google/gson/JsonParserAsync.java new file mode 100644 index 00000000..ef4015b6 --- /dev/null +++ b/gson/src/main/java/com/google/gson/JsonParserAsync.java @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2009 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.gson; + +import java.io.EOFException; +import java.io.Reader; +import java.io.StringReader; + +/** + * A parser that allows reading of multiple {@link JsonElement}s from the specified reader + * asynchronously. + * + * @author Inderjeet Singh + * @author Joel Leitch + * @since 1.4 + */ +public final class JsonParserAsync { + + private final JsonParserJavacc parser; + + /** + * @param json The string containing JSON elements concatenated to each other. + * @since 1.4 + */ + public JsonParserAsync(String json) { + this(new StringReader(json)); + } + + /** + * @param reader The data stream containing JSON elements concatenated to each other. + * @since 1.4 + */ + public JsonParserAsync(Reader reader) { + parser = new JsonParserJavacc(reader); + } + + /** + * Returns the next available {@link JsonElement} on the reader. Null if none available. + * + * @return the next available {@link JsonElement} on the reader. Null if none available. + * @throws JsonParseException if the incoming stream is malformed JSON. + * @since 1.4 + */ + public JsonElement nextElement() throws JsonParseException { + try { + JsonElement element = parser.parse(); + return element; + } catch (TokenMgrError e) { + throw new JsonParseException("Failed parsing JSON source to Json", e); + } catch (ParseException e) { + throw new JsonParseException("Failed parsing JSON source to Json", e); + } catch (StackOverflowError e) { + throw new JsonParseException("Failed parsing JSON source to Json", e); + } catch (OutOfMemoryError e) { + throw new JsonParseException("Failed parsing JSON source to Json", e); + } catch (JsonParseException e) { + if (e.getCause() instanceof EOFException) { + return null; + } else { + throw e; + } + } + } +} diff --git a/gson/src/main/java/com/google/gson/JsonParserJavacc.java b/gson/src/main/java/com/google/gson/JsonParserJavacc.java index 24a6baf2..81ce70fc 100644 --- a/gson/src/main/java/com/google/gson/JsonParserJavacc.java +++ b/gson/src/main/java/com/google/gson/JsonParserJavacc.java @@ -1,5 +1,6 @@ /* Generated By:JavaCC: Do not edit this line. JsonParserJavacc.java */ package com.google.gson; +import java.io.EOFException; @SuppressWarnings("all") final class JsonParserJavacc implements JsonParserJavaccConstants { @@ -7,38 +8,60 @@ final class JsonParserJavacc implements JsonParserJavaccConstants { final public JsonElement parse() throws ParseException { JsonElement json = null; switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { - case 26: - jj_consume_token(26); - break; - default: - jj_la1[0] = jj_gen; - ; - } - switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { - case 27: - json = JsonObject(); - break; - case 31: - json = JsonArray(); + case 0: + jj_consume_token(0); + {if (true) throw new JsonParseException(new EOFException());} break; case DIGITS: + case NULL: case NAN: case INFINITY: case BOOLEAN: case SINGLE_QUOTE_LITERAL: case DOUBLE_QUOTE_LITERAL: + case 26: + case 27: + case 31: case 33: - json = JsonPrimitive(); - break; - case NULL: - json = JsonNull(); + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case 26: + jj_consume_token(26); + break; + default: + jj_la1[0] = jj_gen; + ; + } + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case 27: + json = JsonObject(); + break; + case 31: + json = JsonArray(); + break; + case DIGITS: + case NAN: + case INFINITY: + case BOOLEAN: + case SINGLE_QUOTE_LITERAL: + case DOUBLE_QUOTE_LITERAL: + case 33: + json = JsonPrimitive(); + break; + case NULL: + json = JsonNull(); + break; + default: + jj_la1[1] = jj_gen; + jj_consume_token(-1); + throw new ParseException(); + } + {if (true) return json;} break; default: - jj_la1[1] = jj_gen; + jj_la1[2] = jj_gen; jj_consume_token(-1); throw new ParseException(); } - {if (true) return json;} throw new Error("Missing return statement in function"); } @@ -53,7 +76,7 @@ final class JsonParserJavacc implements JsonParserJavaccConstants { Members(o); break; default: - jj_la1[2] = jj_gen; + jj_la1[3] = jj_gen; ; } jj_consume_token(28); @@ -76,7 +99,7 @@ final class JsonParserJavacc implements JsonParserJavaccConstants { Members(o); break; default: - jj_la1[3] = jj_gen; + jj_la1[4] = jj_gen; ; } } @@ -104,7 +127,7 @@ final class JsonParserJavacc implements JsonParserJavaccConstants { {if (true) return value;} break; default: - jj_la1[4] = jj_gen; + jj_la1[5] = jj_gen; jj_consume_token(-1); throw new ParseException(); } @@ -128,7 +151,7 @@ final class JsonParserJavacc implements JsonParserJavaccConstants { Elements(array); break; default: - jj_la1[5] = jj_gen; + jj_la1[6] = jj_gen; ; } jj_consume_token(32); @@ -146,7 +169,7 @@ final class JsonParserJavacc implements JsonParserJavaccConstants { Elements(array); break; default: - jj_la1[6] = jj_gen; + jj_la1[7] = jj_gen; ; } array.add(element); @@ -178,7 +201,7 @@ final class JsonParserJavacc implements JsonParserJavaccConstants { o = JsonNull(); break; default: - jj_la1[7] = jj_gen; + jj_la1[8] = jj_gen; jj_consume_token(-1); throw new ParseException(); } @@ -214,7 +237,7 @@ final class JsonParserJavacc implements JsonParserJavaccConstants { {if (true) return value;} break; default: - jj_la1[8] = jj_gen; + jj_la1[9] = jj_gen; jj_consume_token(-1); throw new ParseException(); } @@ -239,7 +262,7 @@ final class JsonParserJavacc implements JsonParserJavaccConstants { fracpart = JsonFrac(); break; default: - jj_la1[9] = jj_gen; + jj_la1[10] = jj_gen; ; } switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { @@ -247,7 +270,7 @@ final class JsonParserJavacc implements JsonParserJavaccConstants { exppart = JsonExp(); break; default: - jj_la1[10] = jj_gen; + jj_la1[11] = jj_gen; ; } Number n; @@ -261,7 +284,7 @@ final class JsonParserJavacc implements JsonParserJavaccConstants { {if (true) return new JsonPrimitive(n);} break; default: - jj_la1[11] = jj_gen; + jj_la1[12] = jj_gen; jj_consume_token(-1); throw new ParseException(); } @@ -284,14 +307,14 @@ final class JsonParserJavacc implements JsonParserJavaccConstants { negative = true; break; default: - jj_la1[12] = jj_gen; + jj_la1[13] = jj_gen; ; } jj_consume_token(INFINITY); {if (true) return new JsonPrimitive(negative ? Double.NEGATIVE_INFINITY : Double.POSITIVE_INFINITY);} break; default: - jj_la1[13] = jj_gen; + jj_la1[14] = jj_gen; jj_consume_token(-1); throw new ParseException(); } @@ -307,7 +330,7 @@ final class JsonParserJavacc implements JsonParserJavaccConstants { negative = true; break; default: - jj_la1[14] = jj_gen; + jj_la1[15] = jj_gen; ; } digits = Digits(); @@ -342,7 +365,7 @@ final class JsonParserJavacc implements JsonParserJavaccConstants { t = jj_consume_token(IDENTIFIER_SANS_EXPONENT); break; default: - jj_la1[15] = jj_gen; + jj_la1[16] = jj_gen; jj_consume_token(-1); throw new ParseException(); } @@ -367,7 +390,7 @@ final class JsonParserJavacc implements JsonParserJavaccConstants { t = jj_consume_token(DOUBLE_QUOTE_LITERAL); break; default: - jj_la1[16] = jj_gen; + jj_la1[17] = jj_gen; jj_consume_token(-1); throw new ParseException(); } @@ -427,7 +450,7 @@ final class JsonParserJavacc implements JsonParserJavaccConstants { private Token jj_scanpos, jj_lastpos; private int jj_la; private int jj_gen; - final private int[] jj_la1 = new int[17]; + final private int[] jj_la1 = new int[18]; static private int[] jj_la1_0; static private int[] jj_la1_1; static { @@ -435,10 +458,10 @@ final class JsonParserJavacc implements JsonParserJavaccConstants { jj_la1_init_1(); } private static void jj_la1_init_0() { - jj_la1_0 = new int[] {0x4000000,0x880307c0,0x31800,0x20000000,0x31800,0x880307c0,0x20000000,0x880307c0,0x30740,0x0,0x20,0x40,0x0,0x300,0x0,0x1800,0x30000,}; + jj_la1_0 = new int[] {0x4000000,0x880307c0,0x8c0307c1,0x31800,0x20000000,0x31800,0x880307c0,0x20000000,0x880307c0,0x30740,0x0,0x20,0x40,0x0,0x300,0x0,0x1800,0x30000,}; } private static void jj_la1_init_1() { - jj_la1_1 = new int[] {0x0,0x2,0x0,0x0,0x0,0x2,0x0,0x2,0x2,0x4,0x0,0x2,0x2,0x2,0x2,0x0,0x0,}; + jj_la1_1 = new int[] {0x0,0x2,0x2,0x0,0x0,0x0,0x2,0x0,0x2,0x2,0x4,0x0,0x2,0x2,0x2,0x2,0x0,0x0,}; } final private JJCalls[] jj_2_rtns = new JJCalls[1]; private boolean jj_rescan = false; @@ -455,7 +478,7 @@ final class JsonParserJavacc implements JsonParserJavaccConstants { token = new Token(); jj_ntk = -1; jj_gen = 0; - for (int i = 0; i < 17; i++) jj_la1[i] = -1; + for (int i = 0; i < 18; i++) jj_la1[i] = -1; for (int i = 0; i < jj_2_rtns.length; i++) jj_2_rtns[i] = new JJCalls(); } @@ -470,7 +493,7 @@ final class JsonParserJavacc implements JsonParserJavaccConstants { token = new Token(); jj_ntk = -1; jj_gen = 0; - for (int i = 0; i < 17; i++) jj_la1[i] = -1; + for (int i = 0; i < 18; i++) jj_la1[i] = -1; for (int i = 0; i < jj_2_rtns.length; i++) jj_2_rtns[i] = new JJCalls(); } @@ -481,7 +504,7 @@ final class JsonParserJavacc implements JsonParserJavaccConstants { token = new Token(); jj_ntk = -1; jj_gen = 0; - for (int i = 0; i < 17; i++) jj_la1[i] = -1; + for (int i = 0; i < 18; i++) jj_la1[i] = -1; for (int i = 0; i < jj_2_rtns.length; i++) jj_2_rtns[i] = new JJCalls(); } @@ -492,7 +515,7 @@ final class JsonParserJavacc implements JsonParserJavaccConstants { token = new Token(); jj_ntk = -1; jj_gen = 0; - for (int i = 0; i < 17; i++) jj_la1[i] = -1; + for (int i = 0; i < 18; i++) jj_la1[i] = -1; for (int i = 0; i < jj_2_rtns.length; i++) jj_2_rtns[i] = new JJCalls(); } @@ -502,7 +525,7 @@ final class JsonParserJavacc implements JsonParserJavaccConstants { token = new Token(); jj_ntk = -1; jj_gen = 0; - for (int i = 0; i < 17; i++) jj_la1[i] = -1; + for (int i = 0; i < 18; i++) jj_la1[i] = -1; for (int i = 0; i < jj_2_rtns.length; i++) jj_2_rtns[i] = new JJCalls(); } @@ -512,7 +535,7 @@ final class JsonParserJavacc implements JsonParserJavaccConstants { token = new Token(); jj_ntk = -1; jj_gen = 0; - for (int i = 0; i < 17; i++) jj_la1[i] = -1; + for (int i = 0; i < 18; i++) jj_la1[i] = -1; for (int i = 0; i < jj_2_rtns.length; i++) jj_2_rtns[i] = new JJCalls(); } @@ -629,7 +652,7 @@ final class JsonParserJavacc implements JsonParserJavaccConstants { la1tokens[jj_kind] = true; jj_kind = -1; } - for (int i = 0; i < 17; i++) { + for (int i = 0; i < 18; i++) { if (jj_la1[i] == jj_gen) { for (int j = 0; j < 32; j++) { if ((jj_la1_0[i] & (1<){ + throw new JsonParseException(new EOFException()); + } | [")]}'\n"]( json=JsonObject() | json=JsonArray() | json=JsonPrimitive() | diff --git a/gson/src/test/java/com/google/gson/JsonParserAsyncTest.java b/gson/src/test/java/com/google/gson/JsonParserAsyncTest.java new file mode 100644 index 00000000..648400e0 --- /dev/null +++ b/gson/src/test/java/com/google/gson/JsonParserAsyncTest.java @@ -0,0 +1,19 @@ +package com.google.gson; + +import junit.framework.TestCase; + +/** + * Unit tests for {@link JsonParserAsync} + * + * @author Inderjeet Singh + */ +public class JsonParserAsyncTest extends TestCase { + + public void testParseTwoStrings() { + JsonParserAsync parser = new JsonParserAsync("'one' 'two'"); + String actualOne = parser.nextElement().getAsString(); + assertEquals("one", actualOne); + String actualTwo = parser.nextElement().getAsString(); + assertEquals("two", actualTwo); + } +} diff --git a/gson/src/test/java/com/google/gson/functional/ObjectTest.java b/gson/src/test/java/com/google/gson/functional/ObjectTest.java index 040d175f..67e9c50b 100644 --- a/gson/src/test/java/com/google/gson/functional/ObjectTest.java +++ b/gson/src/test/java/com/google/gson/functional/ObjectTest.java @@ -178,11 +178,8 @@ public class ObjectTest extends TestCase { } public void testNullDeserialization() throws Exception { - try { - gson.fromJson("", Object.class); - fail("Null strings should not be allowed"); - } catch (JsonParseException expected) { - } + Object object = gson.fromJson("", Object.class); + assertNull(object); } public void testNullFieldsSerialization() throws Exception { diff --git a/gson/src/test/java/com/google/gson/functional/ReadersWritersTest.java b/gson/src/test/java/com/google/gson/functional/ReadersWritersTest.java index 8b5316f3..cd06a736 100644 --- a/gson/src/test/java/com/google/gson/functional/ReadersWritersTest.java +++ b/gson/src/test/java/com/google/gson/functional/ReadersWritersTest.java @@ -17,9 +17,8 @@ package com.google.gson.functional; import com.google.gson.Gson; import com.google.gson.GsonBuilder; -import com.google.gson.JsonParseException; -import com.google.gson.JsonParser; -import com.google.gson.JsonParser.AsyncReader; +import com.google.gson.JsonElement; +import com.google.gson.JsonParserAsync; import com.google.gson.common.TestTypes.BagOfPrimitives; import junit.framework.TestCase; @@ -93,11 +92,10 @@ public class ReadersWritersTest extends TestCase { writer.write(gson.toJson("one").toCharArray()); writer.write(gson.toJson("two").toCharArray()); CharArrayReader reader = new CharArrayReader(writer.toCharArray()); - JsonParser parser = new JsonParser(); - AsyncReader asyncReader = parser.parseAsync(reader); - String actualOne = gson.fromJson(asyncReader.readElement(), String.class); + JsonParserAsync parser = new JsonParserAsync(reader); + String actualOne = gson.fromJson(parser.nextElement(), String.class); assertEquals("one", actualOne); - String actualTwo = gson.fromJson(asyncReader.readElement(), String.class); + String actualTwo = gson.fromJson(parser.nextElement(), String.class); assertEquals("two", actualTwo); } @@ -109,11 +107,11 @@ public class ReadersWritersTest extends TestCase { BagOfPrimitives expectedTwo = new BagOfPrimitives(2, 2, false, "two"); writer.write(gson.toJson(expectedTwo).toCharArray()); CharArrayReader reader = new CharArrayReader(writer.toCharArray()); - JsonParser parser = new JsonParser(); - AsyncReader asyncReader = parser.parseAsync(reader); - BagOfPrimitives actualOne = gson.fromJson(asyncReader.readElement(), BagOfPrimitives.class); + JsonParserAsync parser = new JsonParserAsync(reader); + BagOfPrimitives actualOne = gson.fromJson(parser.nextElement(), BagOfPrimitives.class); assertEquals("one", actualOne.stringValue); - BagOfPrimitives actualTwo = gson.fromJson(asyncReader.readElement(), BagOfPrimitives.class); + BagOfPrimitives actualTwo = gson.fromJson(parser.nextElement(), BagOfPrimitives.class); assertEquals("two", actualTwo.stringValue); + JsonElement jsonElement = parser.nextElement(); } }