aboutsummaryrefslogtreecommitdiffstats
path: root/gson/src/main/java/com/google/gson/internal/bind/JsonTreeReader.java
diff options
context:
space:
mode:
Diffstat (limited to 'gson/src/main/java/com/google/gson/internal/bind/JsonTreeReader.java')
-rw-r--r--gson/src/main/java/com/google/gson/internal/bind/JsonTreeReader.java226
1 files changed, 226 insertions, 0 deletions
diff --git a/gson/src/main/java/com/google/gson/internal/bind/JsonTreeReader.java b/gson/src/main/java/com/google/gson/internal/bind/JsonTreeReader.java
new file mode 100644
index 00000000..6a836280
--- /dev/null
+++ b/gson/src/main/java/com/google/gson/internal/bind/JsonTreeReader.java
@@ -0,0 +1,226 @@
+/*
+ * Copyright (C) 2011 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.internal.bind;
+
+import com.google.gson.JsonArray;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonNull;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonPrimitive;
+import com.google.gson.stream.JsonReader;
+import com.google.gson.stream.JsonToken;
+import java.io.IOException;
+import java.io.Reader;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * This reader walks the elements of a JsonElement as if it was coming from a
+ * character stream.
+ *
+ * @author Jesse Wilson
+ */
+public final class JsonTreeReader extends JsonReader {
+ private static final Reader UNREADABLE_READER = new Reader() {
+ @Override public int read(char[] buffer, int offset, int count) throws IOException {
+ throw new AssertionError();
+ }
+ @Override public void close() throws IOException {
+ throw new AssertionError();
+ }
+ };
+ private static final Object SENTINEL_CLOSED = new Object();
+
+ private final List<Object> stack = new ArrayList<Object>();
+
+ public JsonTreeReader(JsonElement element) {
+ super(UNREADABLE_READER);
+ stack.add(element);
+ }
+
+ @Override public void beginArray() throws IOException {
+ expect(JsonToken.BEGIN_ARRAY);
+ JsonArray array = (JsonArray) peekStack();
+ stack.add(array.iterator());
+ }
+
+ @Override public void endArray() throws IOException {
+ expect(JsonToken.END_ARRAY);
+ popStack(); // empty iterator
+ popStack(); // array
+ }
+
+ @Override public void beginObject() throws IOException {
+ expect(JsonToken.BEGIN_OBJECT);
+ JsonObject object = (JsonObject) peekStack();
+ stack.add(object.entrySet().iterator());
+ }
+
+ @Override public void endObject() throws IOException {
+ expect(JsonToken.END_OBJECT);
+ popStack(); // empty iterator
+ popStack(); // object
+ }
+
+ @Override public boolean hasNext() throws IOException {
+ JsonToken token = peek();
+ return token != JsonToken.END_OBJECT && token != JsonToken.END_ARRAY;
+ }
+
+ @Override public JsonToken peek() throws IOException {
+ if (stack.isEmpty()) {
+ return JsonToken.END_DOCUMENT;
+ }
+
+ Object o = peekStack();
+ if (o instanceof Iterator) {
+ boolean isObject = stack.get(stack.size() - 2) instanceof JsonObject;
+ Iterator<?> iterator = (Iterator<?>) o;
+ if (iterator.hasNext()) {
+ if (isObject) {
+ return JsonToken.NAME;
+ } else {
+ stack.add(iterator.next());
+ return peek();
+ }
+ } else {
+ return isObject ? JsonToken.END_OBJECT : JsonToken.END_ARRAY;
+ }
+ } else if (o instanceof JsonObject) {
+ return JsonToken.BEGIN_OBJECT;
+ } else if (o instanceof JsonArray) {
+ return JsonToken.BEGIN_ARRAY;
+ } else if (o instanceof JsonPrimitive) {
+ JsonPrimitive primitive = (JsonPrimitive) o;
+ if (primitive.isString()) {
+ return JsonToken.STRING;
+ } else if (primitive.isBoolean()) {
+ return JsonToken.BOOLEAN;
+ } else if (primitive.isNumber()) {
+ return JsonToken.NUMBER;
+ } else {
+ throw new AssertionError();
+ }
+ } else if (o instanceof JsonNull) {
+ return JsonToken.NULL;
+ } else if (o == SENTINEL_CLOSED) {
+ throw new IllegalStateException("JsonReader is closed");
+ } else {
+ throw new AssertionError();
+ }
+ }
+
+ private Object peekStack() {
+ return stack.get(stack.size() - 1);
+ }
+
+ private Object popStack() {
+ return stack.remove(stack.size() - 1);
+ }
+
+ private void expect(JsonToken expected) throws IOException {
+ if (peek() != expected) {
+ throw new IllegalStateException("Expected " + expected + " but was " + peek());
+ }
+ }
+
+ @Override public String nextName() throws IOException {
+ expect(JsonToken.NAME);
+ Iterator<?> i = (Iterator<?>) peekStack();
+ Map.Entry<?, ?> entry = (Map.Entry<?, ?>) i.next();
+ stack.add(entry.getValue());
+ return (String) entry.getKey();
+ }
+
+ @Override public String nextString() throws IOException {
+ JsonToken token = peek();
+ if (token != JsonToken.STRING && token != JsonToken.NUMBER) {
+ throw new IllegalStateException("Expected " + JsonToken.STRING + " but was " + token);
+ }
+ return ((JsonPrimitive) popStack()).getAsString();
+ }
+
+ @Override public boolean nextBoolean() throws IOException {
+ expect(JsonToken.BOOLEAN);
+ return ((JsonPrimitive) popStack()).getAsBoolean();
+ }
+
+ @Override public void nextNull() throws IOException {
+ expect(JsonToken.NULL);
+ popStack();
+ }
+
+ @Override public double nextDouble() throws IOException {
+ JsonToken token = peek();
+ if (token != JsonToken.NUMBER && token != JsonToken.STRING) {
+ throw new IllegalStateException("Expected " + JsonToken.NUMBER + " but was " + token);
+ }
+ double result = ((JsonPrimitive) peekStack()).getAsDouble();
+ if (!isLenient() && (Double.isNaN(result) || Double.isInfinite(result))) {
+ throw new NumberFormatException("JSON forbids NaN and infinities: " + result);
+ }
+ popStack();
+ return result;
+ }
+
+ @Override public long nextLong() throws IOException {
+ JsonToken token = peek();
+ if (token != JsonToken.NUMBER && token != JsonToken.STRING) {
+ throw new IllegalStateException("Expected " + JsonToken.NUMBER + " but was " + token);
+ }
+ long result = ((JsonPrimitive) peekStack()).getAsLong();
+ popStack();
+ return result;
+ }
+
+ @Override public int nextInt() throws IOException {
+ JsonToken token = peek();
+ if (token != JsonToken.NUMBER && token != JsonToken.STRING) {
+ throw new IllegalStateException("Expected " + JsonToken.NUMBER + " but was " + token);
+ }
+ int result = ((JsonPrimitive) peekStack()).getAsInt();
+ popStack();
+ return result;
+ }
+
+ @Override public void close() throws IOException {
+ stack.clear();
+ stack.add(SENTINEL_CLOSED);
+ }
+
+ @Override public void skipValue() throws IOException {
+ if (peek() == JsonToken.NAME) {
+ nextName();
+ } else {
+ popStack();
+ }
+ }
+
+ @Override public String toString() {
+ return getClass().getSimpleName();
+ }
+
+ public void promoteNameToValue() throws IOException {
+ expect(JsonToken.NAME);
+ Iterator<?> i = (Iterator<?>) peekStack();
+ Map.Entry<?, ?> entry = (Map.Entry<?, ?>) i.next();
+ stack.add(entry.getValue());
+ stack.add(new JsonPrimitive((String)entry.getKey()));
+ }
+}