1 | /* |
2 | * Copyright (C) 2010 Google Inc. |
3 | * |
4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
5 | * you may not use this file except in compliance with the License. |
6 | * You may obtain a copy of the License at |
7 | * |
8 | * http://www.apache.org/licenses/LICENSE-2.0 |
9 | * |
10 | * Unless required by applicable law or agreed to in writing, software |
11 | * distributed under the License is distributed on an "AS IS" BASIS, |
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
13 | * See the License for the specific language governing permissions and |
14 | * limitations under the License. |
15 | */ |
16 | |
17 | package com.google.gson; |
18 | |
19 | import com.google.gson.stream.JsonReader; |
20 | import com.google.gson.stream.JsonWriter; |
21 | import com.google.gson.stream.MalformedJsonException; |
22 | import java.io.EOFException; |
23 | import java.io.IOException; |
24 | import java.io.Writer; |
25 | import java.util.Map; |
26 | |
27 | /** |
28 | * Reads and writes GSON parse trees over streams. |
29 | */ |
30 | final class Streams { |
31 | |
32 | /** |
33 | * Takes a reader in any state and returns the next value as a JsonElement. |
34 | */ |
35 | static JsonElement parse(JsonReader reader) throws JsonParseException { |
36 | boolean isEmpty = true; |
37 | try { |
38 | reader.peek(); |
39 | isEmpty = false; |
40 | return parseRecursive(reader); |
41 | } catch (EOFException e) { |
42 | /* |
43 | * For compatibility with JSON 1.5 and earlier, we return a JsonNull for |
44 | * empty documents instead of throwing. |
45 | */ |
46 | if (isEmpty) { |
47 | return JsonNull.createJsonNull(); |
48 | } |
49 | throw new JsonIOException(e); |
50 | } catch (MalformedJsonException e) { |
51 | throw new JsonSyntaxException(e); |
52 | } catch (IOException e) { |
53 | throw new JsonIOException(e); |
54 | } catch (NumberFormatException e) { |
55 | throw new JsonSyntaxException(e); |
56 | } |
57 | } |
58 | |
59 | private static JsonElement parseRecursive(JsonReader reader) throws IOException { |
60 | switch (reader.peek()) { |
61 | case STRING: |
62 | return new JsonPrimitive(reader.nextString()); |
63 | case NUMBER: |
64 | String number = reader.nextString(); |
65 | return new JsonPrimitive(JsonPrimitive.stringToNumber(number)); |
66 | case BOOLEAN: |
67 | return new JsonPrimitive(reader.nextBoolean()); |
68 | case NULL: |
69 | reader.nextNull(); |
70 | return JsonNull.createJsonNull(); |
71 | case BEGIN_ARRAY: |
72 | JsonArray array = new JsonArray(); |
73 | reader.beginArray(); |
74 | while (reader.hasNext()) { |
75 | array.add(parseRecursive(reader)); |
76 | } |
77 | reader.endArray(); |
78 | return array; |
79 | case BEGIN_OBJECT: |
80 | JsonObject object = new JsonObject(); |
81 | reader.beginObject(); |
82 | while (reader.hasNext()) { |
83 | object.add(reader.nextName(), parseRecursive(reader)); |
84 | } |
85 | reader.endObject(); |
86 | return object; |
87 | case END_DOCUMENT: |
88 | case NAME: |
89 | case END_OBJECT: |
90 | case END_ARRAY: |
91 | default: |
92 | throw new IllegalArgumentException(); |
93 | } |
94 | } |
95 | |
96 | /** |
97 | * Writes the JSON element to the writer, recursively. |
98 | */ |
99 | static void write(JsonElement element, boolean serializeNulls, JsonWriter writer) |
100 | throws IOException { |
101 | if (element == null || element.isJsonNull()) { |
102 | if (serializeNulls) { |
103 | writer.nullValue(); |
104 | } |
105 | //BEGIN JCLOUDS PATCH |
106 | // * @see <a href="http://code.google.com/p/google-gson/issues/detail?id=326"/> |
107 | } else if (element instanceof JsonLiteral ) { |
108 | writer.value(JsonLiteral.class.cast(element)); |
109 | //END JCLOUDS PATCH |
110 | } else if (element.isJsonPrimitive()) { |
111 | JsonPrimitive primitive = element.getAsJsonPrimitive(); |
112 | if (primitive.isNumber()) { |
113 | writer.value(primitive.getAsNumber()); |
114 | } else if (primitive.isBoolean()) { |
115 | writer.value(primitive.getAsBoolean()); |
116 | } else { |
117 | writer.value(primitive.getAsString()); |
118 | } |
119 | |
120 | } else if (element.isJsonArray()) { |
121 | writer.beginArray(); |
122 | for (JsonElement e : element.getAsJsonArray()) { |
123 | /* always print null when its parent element is an array! */ |
124 | if (e.isJsonNull()) { |
125 | writer.nullValue(); |
126 | continue; |
127 | } |
128 | write(e, serializeNulls, writer); |
129 | } |
130 | writer.endArray(); |
131 | |
132 | } else if (element.isJsonObject()) { |
133 | writer.beginObject(); |
134 | for (Map.Entry<String, JsonElement> e : element.getAsJsonObject().entrySet()) { |
135 | JsonElement value = e.getValue(); |
136 | if (!serializeNulls && value.isJsonNull()) { |
137 | continue; |
138 | } |
139 | writer.name(e.getKey()); |
140 | write(value, serializeNulls, writer); |
141 | } |
142 | writer.endObject(); |
143 | |
144 | } else { |
145 | throw new IllegalArgumentException("Couldn't write " + element.getClass()); |
146 | } |
147 | } |
148 | |
149 | static Writer writerForAppendable(Appendable appendable) { |
150 | return appendable instanceof Writer ? (Writer) appendable : new AppendableWriter(appendable); |
151 | } |
152 | |
153 | /** |
154 | * Adapts an {@link Appendable} so it can be passed anywhere a {@link Writer} |
155 | * is used. |
156 | */ |
157 | private static class AppendableWriter extends Writer { |
158 | private final Appendable appendable; |
159 | private final CurrentWrite currentWrite = new CurrentWrite(); |
160 | |
161 | private AppendableWriter(Appendable appendable) { |
162 | this.appendable = appendable; |
163 | } |
164 | |
165 | @Override public void write(char[] chars, int offset, int length) throws IOException { |
166 | currentWrite.chars = chars; |
167 | appendable.append(currentWrite, offset, offset + length); |
168 | } |
169 | |
170 | @Override public void write(int i) throws IOException { |
171 | appendable.append((char) i); |
172 | } |
173 | |
174 | @Override public void flush() {} |
175 | @Override public void close() {} |
176 | |
177 | /** |
178 | * A mutable char sequence pointing at a single char[]. |
179 | */ |
180 | static class CurrentWrite implements CharSequence { |
181 | char[] chars; |
182 | public int length() { |
183 | return chars.length; |
184 | } |
185 | public char charAt(int i) { |
186 | return chars[i]; |
187 | } |
188 | public CharSequence subSequence(int start, int end) { |
189 | return new String(chars, start, end - start); |
190 | } |
191 | } |
192 | } |
193 | } |