1
16
17 package com.google.gson.internal.bind;
18
19 import com.google.gson.Gson;
20 import com.google.gson.JsonElement;
21 import com.google.gson.JsonPrimitive;
22 import com.google.gson.JsonSyntaxException;
23 import com.google.gson.TypeAdapter;
24 import com.google.gson.TypeAdapterFactory;
25 import com.google.gson.internal.$Gson$Types;
26 import com.google.gson.internal.ConstructorConstructor;
27 import com.google.gson.internal.JsonReaderInternalAccess;
28 import com.google.gson.internal.ObjectConstructor;
29 import com.google.gson.internal.Streams;
30 import com.google.gson.reflect.TypeToken;
31 import com.google.gson.stream.JsonReader;
32 import com.google.gson.stream.JsonToken;
33 import com.google.gson.stream.JsonWriter;
34 import java.io.IOException;
35 import java.lang.reflect.Type;
36 import java.util.ArrayList;
37 import java.util.List;
38 import java.util.Map;
39
40
105 public final class MapTypeAdapterFactory implements TypeAdapterFactory {
106 private final ConstructorConstructor constructorConstructor;
107 final boolean complexMapKeySerialization;
108
109 public MapTypeAdapterFactory(ConstructorConstructor constructorConstructor,
110 boolean complexMapKeySerialization) {
111 this.constructorConstructor = constructorConstructor;
112 this.complexMapKeySerialization = complexMapKeySerialization;
113 }
114
115 @Override public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) {
116 Type type = typeToken.getType();
117
118 Class<? super T> rawType = typeToken.getRawType();
119 if (!Map.class.isAssignableFrom(rawType)) {
120 return null;
121 }
122
123 Class<?> rawTypeOfSrc = $Gson$Types.getRawType(type);
124 Type[] keyAndValueTypes = $Gson$Types.getMapKeyAndValueTypes(type, rawTypeOfSrc);
125 TypeAdapter<?> keyAdapter = getKeyAdapter(gson, keyAndValueTypes[0]);
126 TypeAdapter<?> valueAdapter = gson.getAdapter(TypeToken.get(keyAndValueTypes[1]));
127 ObjectConstructor<T> constructor = constructorConstructor.get(typeToken);
128
129 @SuppressWarnings({"unchecked", "rawtypes"})
130
131 TypeAdapter<T> result = new Adapter(gson, keyAndValueTypes[0], keyAdapter,
132 keyAndValueTypes[1], valueAdapter, constructor);
133 return result;
134 }
135
136
139 private TypeAdapter<?> getKeyAdapter(Gson context, Type keyType) {
140 return (keyType == boolean.class || keyType == Boolean.class)
141 ? TypeAdapters.BOOLEAN_AS_STRING
142 : context.getAdapter(TypeToken.get(keyType));
143 }
144
145 private final class Adapter<K, V> extends TypeAdapter<Map<K, V>> {
146 private final TypeAdapter<K> keyTypeAdapter;
147 private final TypeAdapter<V> valueTypeAdapter;
148 private final ObjectConstructor<? extends Map<K, V>> constructor;
149
150 public Adapter(Gson context, Type keyType, TypeAdapter<K> keyTypeAdapter,
151 Type valueType, TypeAdapter<V> valueTypeAdapter,
152 ObjectConstructor<? extends Map<K, V>> constructor) {
153 this.keyTypeAdapter =
154 new TypeAdapterRuntimeTypeWrapper<K>(context, keyTypeAdapter, keyType);
155 this.valueTypeAdapter =
156 new TypeAdapterRuntimeTypeWrapper<V>(context, valueTypeAdapter, valueType);
157 this.constructor = constructor;
158 }
159
160 @Override public Map<K, V> read(JsonReader in) throws IOException {
161 JsonToken peek = in.peek();
162 if (peek == JsonToken.NULL) {
163 in.nextNull();
164 return null;
165 }
166
167 Map<K, V> map = constructor.construct();
168
169 if (peek == JsonToken.BEGIN_ARRAY) {
170 in.beginArray();
171 while (in.hasNext()) {
172 in.beginArray();
173 K key = keyTypeAdapter.read(in);
174 V value = valueTypeAdapter.read(in);
175 V replaced = map.put(key, value);
176 if (replaced != null) {
177 throw new JsonSyntaxException("duplicate key: " + key);
178 }
179 in.endArray();
180 }
181 in.endArray();
182 } else {
183 in.beginObject();
184 while (in.hasNext()) {
185 JsonReaderInternalAccess.INSTANCE.promoteNameToValue(in);
186 K key = keyTypeAdapter.read(in);
187 V value = valueTypeAdapter.read(in);
188 V replaced = map.put(key, value);
189 if (replaced != null) {
190 throw new JsonSyntaxException("duplicate key: " + key);
191 }
192 }
193 in.endObject();
194 }
195 return map;
196 }
197
198 @Override public void write(JsonWriter out, Map<K, V> map) throws IOException {
199 if (map == null) {
200 out.nullValue();
201 return;
202 }
203
204 if (!complexMapKeySerialization) {
205 out.beginObject();
206 for (Map.Entry<K, V> entry : map.entrySet()) {
207 out.name(String.valueOf(entry.getKey()));
208 valueTypeAdapter.write(out, entry.getValue());
209 }
210 out.endObject();
211 return;
212 }
213
214 boolean hasComplexKeys = false;
215 List<JsonElement> keys = new ArrayList<JsonElement>(map.size());
216
217 List<V> values = new ArrayList<V>(map.size());
218 for (Map.Entry<K, V> entry : map.entrySet()) {
219 JsonElement keyElement = keyTypeAdapter.toJsonTree(entry.getKey());
220 keys.add(keyElement);
221 values.add(entry.getValue());
222 hasComplexKeys |= keyElement.isJsonArray() || keyElement.isJsonObject();
223 }
224
225 if (hasComplexKeys) {
226 out.beginArray();
227 for (int i = 0, size = keys.size(); i < size; i++) {
228 out.beginArray();
229 Streams.write(keys.get(i), out);
230 valueTypeAdapter.write(out, values.get(i));
231 out.endArray();
232 }
233 out.endArray();
234 } else {
235 out.beginObject();
236 for (int i = 0, size = keys.size(); i < size; i++) {
237 JsonElement keyElement = keys.get(i);
238 out.name(keyToString(keyElement));
239 valueTypeAdapter.write(out, values.get(i));
240 }
241 out.endObject();
242 }
243 }
244
245 private String keyToString(JsonElement keyElement) {
246 if (keyElement.isJsonPrimitive()) {
247 JsonPrimitive primitive = keyElement.getAsJsonPrimitive();
248 if (primitive.isNumber()) {
249 return String.valueOf(primitive.getAsNumber());
250 } else if (primitive.isBoolean()) {
251 return Boolean.toString(primitive.getAsBoolean());
252 } else if (primitive.isString()) {
253 return primitive.getAsString();
254 } else {
255 throw new AssertionError();
256 }
257 } else if (keyElement.isJsonNull()) {
258 return "null";
259 } else {
260 throw new AssertionError();
261 }
262 }
263 }
264 }
265