diff --git a/moshi/src/main/java/com/squareup/moshi/JsonReader.java b/moshi/src/main/java/com/squareup/moshi/JsonReader.java index e04acf9..29146a3 100644 --- a/moshi/src/main/java/com/squareup/moshi/JsonReader.java +++ b/moshi/src/main/java/com/squareup/moshi/JsonReader.java @@ -497,8 +497,7 @@ public abstract class JsonReader implements Closeable { * jsonReader.nextInt() // Returns 456, reader contains 789 and ]. * } */ - // TODO(jwilson): make this public once it's supported in JsonUtf8Reader. - abstract JsonReader peekJson(); + public abstract JsonReader peekJson(); /** * Returns a JsonPath to diff --git a/moshi/src/main/java/com/squareup/moshi/JsonUtf8Reader.java b/moshi/src/main/java/com/squareup/moshi/JsonUtf8Reader.java index 2703abe..571dde2 100644 --- a/moshi/src/main/java/com/squareup/moshi/JsonUtf8Reader.java +++ b/moshi/src/main/java/com/squareup/moshi/JsonUtf8Reader.java @@ -98,6 +98,27 @@ final class JsonUtf8Reader extends JsonReader { pushScope(JsonScope.EMPTY_DOCUMENT); } + /** Copy-constructor makes a deep copy for peeking. */ + JsonUtf8Reader(JsonUtf8Reader copyFrom) { + super(copyFrom); + + BufferedSource sourcePeek = copyFrom.source.peek(); + this.source = sourcePeek; + this.buffer = sourcePeek.getBuffer(); + this.peeked = copyFrom.peeked; + this.peekedLong = copyFrom.peekedLong; + this.peekedNumberLength = copyFrom.peekedNumberLength; + this.peekedString = copyFrom.peekedString; + + // Make sure our buffer has as many bytes as the source's buffer. This is necessary because + // JsonUtf8Reader assumes any data it has peeked (like the peekedNumberLength) are buffered. + try { + sourcePeek.require(copyFrom.buffer.size()); + } catch (IOException e) { + throw new AssertionError(); + } + } + @Override public void beginArray() throws IOException { int p = peeked; if (p == PEEKED_NONE) { @@ -1051,8 +1072,8 @@ final class JsonUtf8Reader extends JsonReader { return found; } - @Override JsonReader peekJson() { - throw new UnsupportedOperationException("TODO"); + @Override public JsonReader peekJson() { + return new JsonUtf8Reader(this); } @Override public String toString() { diff --git a/moshi/src/main/java/com/squareup/moshi/JsonValueReader.java b/moshi/src/main/java/com/squareup/moshi/JsonValueReader.java index 4246d3d..33f0eb3 100644 --- a/moshi/src/main/java/com/squareup/moshi/JsonValueReader.java +++ b/moshi/src/main/java/com/squareup/moshi/JsonValueReader.java @@ -318,7 +318,7 @@ final class JsonValueReader extends JsonReader { } } - @Override JsonReader peekJson() { + @Override public JsonReader peekJson() { return new JsonValueReader(this); } diff --git a/moshi/src/test/java/com/squareup/moshi/JsonReaderTest.java b/moshi/src/test/java/com/squareup/moshi/JsonReaderTest.java index 8f5c045..95c1c40 100644 --- a/moshi/src/test/java/com/squareup/moshi/JsonReaderTest.java +++ b/moshi/src/test/java/com/squareup/moshi/JsonReaderTest.java @@ -33,6 +33,7 @@ import static com.squareup.moshi.JsonReader.Token.STRING; import static com.squareup.moshi.TestUtil.repeat; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; import static org.junit.Assert.fail; import static org.junit.Assume.assumeTrue; @@ -988,7 +989,6 @@ public final class JsonReaderTest { @Test public void basicPeekJson() throws IOException { JsonReader reader = newReader("{\"a\":12,\"b\":[34,56],\"c\":78}"); - assumeTrue(reader instanceof JsonValueReader); // Not implemented for JsonUtf8Reader yet! reader.beginObject(); assertThat(reader.nextName()).isEqualTo("a"); assertThat(reader.nextInt()).isEqualTo(12); @@ -1021,7 +1021,6 @@ public final class JsonReaderTest { */ @Test public void peekJsonReader() throws IOException { JsonReader reader = newReader("[12,34,{\"a\":56,\"b\":78},90]"); - assumeTrue(reader instanceof JsonValueReader); // Not implemented for JsonUtf8Reader yet! for (int i = 0; i < 12; i++) { readPeek12Steps(reader.peekJson(), i, 12); readPeek12Steps(reader, i, i + 1); @@ -1084,4 +1083,67 @@ public final class JsonReaderTest { assertThat(reader.getPath()).isEqualTo("$"); } } + + /** Confirm that we can peek in every state of the UTF-8 reader. */ + @Test public void peekAfterPeek() throws IOException { + JsonReader reader = newReader( + "[{\"a\":\"aaa\",'b':'bbb',c:c,\"d\":\"d\"},true,false,null,1,2.0]"); + reader.setLenient(true); + readValue(reader, true); + reader.peekJson(); + } + + @Test public void peekAfterPromoteNameToValue() throws IOException { + JsonReader reader = newReader("{\"a\":\"b\"}"); + reader.beginObject(); + reader.promoteNameToValue(); + assertEquals("a", reader.peekJson().nextString()); + assertEquals("a", reader.nextString()); + assertEquals("b", reader.peekJson().nextString()); + assertEquals("b", reader.nextString()); + reader.endObject(); + } + + /** Peek a value, then read it, recursively. */ + private void readValue(JsonReader reader, boolean peekJsonFirst) throws IOException { + JsonReader.Token token = reader.peek(); + if (peekJsonFirst) { + readValue(reader.peekJson(), false); + } + + switch (token) { + case BEGIN_ARRAY: + reader.beginArray(); + while (reader.hasNext()) { + readValue(reader, peekJsonFirst); + } + reader.peekJson().endArray(); + reader.endArray(); + break; + case BEGIN_OBJECT: + reader.beginObject(); + while (reader.hasNext()) { + assertNotNull(reader.peekJson().nextName()); + assertNotNull(reader.nextName()); + readValue(reader, peekJsonFirst); + } + reader.peekJson().endObject(); + reader.endObject(); + break; + case STRING: + reader.nextString(); + break; + case NUMBER: + reader.nextDouble(); + break; + case BOOLEAN: + reader.nextBoolean(); + break; + case NULL: + reader.nextNull(); + break; + default: + throw new AssertionError(); + } + } } diff --git a/pom.xml b/pom.xml index c6d1cc1..a5814c4 100644 --- a/pom.xml +++ b/pom.xml @@ -35,7 +35,7 @@ 3.1.0 - 1.15.0 + 1.16.0 0.15