diff --git a/gson/src/main/java/com/google/gson/stream/JsonWriter.java b/gson/src/main/java/com/google/gson/stream/JsonWriter.java index 8bd8ee8e..42f9e899 100644 --- a/gson/src/main/java/com/google/gson/stream/JsonWriter.java +++ b/gson/src/main/java/com/google/gson/stream/JsonWriter.java @@ -314,7 +314,11 @@ public class JsonWriter implements Closeable { * Returns the value on the top of the stack. */ private JsonScope peek() { - return stack.get(stack.size() - 1); + int size = stack.size(); + if (size == 0) { + throw new IllegalStateException("JsonWriter is closed."); + } + return stack.get(size - 1); } /** @@ -337,6 +341,9 @@ public class JsonWriter implements Closeable { if (deferredName != null) { throw new IllegalStateException(); } + if (stack.isEmpty()) { + throw new IllegalStateException("JsonWriter is closed."); + } deferredName = name; return this; } @@ -453,6 +460,9 @@ public class JsonWriter implements Closeable { * and flushes that writer. */ public void flush() throws IOException { + if (stack.isEmpty()) { + throw new IllegalStateException("JsonWriter is closed."); + } out.flush(); } @@ -464,9 +474,11 @@ public class JsonWriter implements Closeable { public void close() throws IOException { out.close(); - if (peek() != JsonScope.NONEMPTY_DOCUMENT) { + int size = stack.size(); + if (size > 1 || size == 1 && stack.get(size - 1) != JsonScope.NONEMPTY_DOCUMENT) { throw new IOException("Incomplete document"); } + stack.clear(); } private void string(String value) throws IOException { @@ -574,8 +586,15 @@ public class JsonWriter implements Closeable { * @param root true if the value is a new array or object, the two values * permitted as top-level elements. */ + @SuppressWarnings("fallthrough") private void beforeValue(boolean root) throws IOException { switch (peek()) { + case NONEMPTY_DOCUMENT: + if (!lenient) { + throw new IllegalStateException( + "JSON must have only one top-level value."); + } + // fall-through case EMPTY_DOCUMENT: // first in document if (!lenient && !root) { throw new IllegalStateException( @@ -599,10 +618,6 @@ public class JsonWriter implements Closeable { replaceTop(JsonScope.NONEMPTY_OBJECT); break; - case NONEMPTY_DOCUMENT: - throw new IllegalStateException( - "JSON must have only one top-level value."); - default: throw new IllegalStateException("Nesting problem: " + stack); } diff --git a/gson/src/test/java/com/google/gson/stream/JsonWriterTest.java b/gson/src/test/java/com/google/gson/stream/JsonWriterTest.java index 41a9621c..284ac52c 100644 --- a/gson/src/test/java/com/google/gson/stream/JsonWriterTest.java +++ b/gson/src/test/java/com/google/gson/stream/JsonWriterTest.java @@ -16,12 +16,11 @@ package com.google.gson.stream; -import junit.framework.TestCase; - import java.io.IOException; import java.io.StringWriter; import java.math.BigDecimal; import java.math.BigInteger; +import junit.framework.TestCase; public final class JsonWriterTest extends TestCase { @@ -464,4 +463,104 @@ public final class JsonWriterTest extends TestCase { + "]"; assertEquals(expected, stringWriter.toString()); } + + public void testLenientWriterPermitsMultipleTopLevelValues() throws IOException { + StringWriter stringWriter = new StringWriter(); + JsonWriter writer = new JsonWriter(stringWriter); + writer.setLenient(true); + writer.beginArray(); + writer.endArray(); + writer.beginArray(); + writer.endArray(); + writer.close(); + assertEquals("[][]", stringWriter.toString()); + } + + public void testStrictWriterDoesNotPermitMultipleTopLevelValues() throws IOException { + StringWriter stringWriter = new StringWriter(); + JsonWriter writer = new JsonWriter(stringWriter); + writer.beginArray(); + writer.endArray(); + try { + writer.beginArray(); + fail(); + } catch (IllegalStateException expected) { + } + } + + public void testClosedWriterThrowsOnStructure() throws IOException { + StringWriter stringWriter = new StringWriter(); + JsonWriter writer = new JsonWriter(stringWriter); + writer.beginArray(); + writer.endArray(); + writer.close(); + try { + writer.beginArray(); + fail(); + } catch (IllegalStateException expected) { + } + try { + writer.endArray(); + fail(); + } catch (IllegalStateException expected) { + } + try { + writer.beginObject(); + fail(); + } catch (IllegalStateException expected) { + } + try { + writer.endObject(); + fail(); + } catch (IllegalStateException expected) { + } + } + + public void testClosedWriterThrowsOnName() throws IOException { + StringWriter stringWriter = new StringWriter(); + JsonWriter writer = new JsonWriter(stringWriter); + writer.beginArray(); + writer.endArray(); + writer.close(); + try { + writer.name("a"); + fail(); + } catch (IllegalStateException expected) { + } + } + + public void testClosedWriterThrowsOnValue() throws IOException { + StringWriter stringWriter = new StringWriter(); + JsonWriter writer = new JsonWriter(stringWriter); + writer.beginArray(); + writer.endArray(); + writer.close(); + try { + writer.value("a"); + fail(); + } catch (IllegalStateException expected) { + } + } + + public void testClosedWriterThrowsOnFlush() throws IOException { + StringWriter stringWriter = new StringWriter(); + JsonWriter writer = new JsonWriter(stringWriter); + writer.beginArray(); + writer.endArray(); + writer.close(); + try { + writer.flush(); + fail(); + } catch (IllegalStateException expected) { + } + } + + public void testWriterCloseIsIdempotent() throws IOException { + StringWriter stringWriter = new StringWriter(); + JsonWriter writer = new JsonWriter(stringWriter); + writer.beginArray(); + writer.endArray(); + writer.close(); + writer.close(); + } }