]>
Commit | Line | Data |
---|---|---|
cfd903b6 MG |
1 | /* |
2 | * Copyright (C) 2011 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.internal.bind; | |
18 | ||
19 | import com.google.gson.TypeAdapterFactory; | |
20 | import java.io.IOException; | |
21 | import java.math.BigDecimal; | |
22 | import java.math.BigInteger; | |
23 | import java.net.InetAddress; | |
24 | import java.net.URI; | |
25 | import java.net.URISyntaxException; | |
26 | import java.net.URL; | |
27 | import java.sql.Timestamp; | |
28 | import java.util.BitSet; | |
29 | import java.util.Calendar; | |
30 | import java.util.Date; | |
31 | import java.util.GregorianCalendar; | |
32 | import java.util.HashMap; | |
33 | import java.util.Locale; | |
34 | import java.util.Map; | |
35 | import java.util.StringTokenizer; | |
36 | import java.util.UUID; | |
37 | ||
38 | import com.google.gson.Gson; | |
39 | import com.google.gson.JsonArray; | |
40 | import com.google.gson.JsonElement; | |
41 | import com.google.gson.JsonIOException; | |
42 | import com.google.gson.JsonNull; | |
43 | import com.google.gson.JsonObject; | |
44 | import com.google.gson.JsonPrimitive; | |
45 | import com.google.gson.JsonSyntaxException; | |
46 | import com.google.gson.TypeAdapter; | |
47 | import com.google.gson.annotations.SerializedName; | |
48 | import com.google.gson.internal.LazilyParsedNumber; | |
49 | import com.google.gson.reflect.TypeToken; | |
50 | import com.google.gson.stream.JsonReader; | |
51 | import com.google.gson.stream.JsonToken; | |
52 | import com.google.gson.stream.JsonWriter; | |
53 | ||
54 | /** | |
55 | * Type adapters for basic types. | |
56 | */ | |
57 | public final class TypeAdapters { | |
58 | private TypeAdapters() {} | |
59 | ||
60 | @SuppressWarnings("rawtypes") | |
61 | public static final TypeAdapter<Class> CLASS = new TypeAdapter<Class>() { | |
62 | @Override | |
63 | public void write(JsonWriter out, Class value) throws IOException { | |
64 | if (value == null) { | |
65 | out.nullValue(); | |
66 | } else { | |
67 | throw new UnsupportedOperationException("Attempted to serialize java.lang.Class: " | |
68 | + value.getName() + ". Forgot to register a type adapter?"); | |
69 | } | |
70 | } | |
71 | @Override | |
72 | public Class read(JsonReader in) throws IOException { | |
73 | if (in.peek() == JsonToken.NULL) { | |
74 | in.nextNull(); | |
75 | return null; | |
76 | } else { | |
77 | throw new UnsupportedOperationException( | |
78 | "Attempted to deserialize a java.lang.Class. Forgot to register a type adapter?"); | |
79 | } | |
80 | } | |
81 | }; | |
82 | public static final TypeAdapterFactory CLASS_FACTORY = newFactory(Class.class, CLASS); | |
83 | ||
84 | public static final TypeAdapter<BitSet> BIT_SET = new TypeAdapter<BitSet>() { | |
85 | public BitSet read(JsonReader in) throws IOException { | |
86 | if (in.peek() == JsonToken.NULL) { | |
87 | in.nextNull(); | |
88 | return null; | |
89 | } | |
90 | ||
91 | BitSet bitset = new BitSet(); | |
92 | in.beginArray(); | |
93 | int i = 0; | |
94 | JsonToken tokenType = in.peek(); | |
95 | while (tokenType != JsonToken.END_ARRAY) { | |
96 | boolean set; | |
97 | switch (tokenType) { | |
98 | case NUMBER: | |
99 | set = in.nextInt() != 0; | |
100 | break; | |
101 | case BOOLEAN: | |
102 | set = in.nextBoolean(); | |
103 | break; | |
104 | case STRING: | |
105 | String stringValue = in.nextString(); | |
106 | try { | |
107 | set = Integer.parseInt(stringValue) != 0; | |
108 | } catch (NumberFormatException e) { | |
109 | throw new JsonSyntaxException( | |
110 | "Error: Expecting: bitset number value (1, 0), Found: " + stringValue); | |
111 | } | |
112 | break; | |
113 | default: | |
114 | throw new JsonSyntaxException("Invalid bitset value type: " + tokenType); | |
115 | } | |
116 | if (set) { | |
117 | bitset.set(i); | |
118 | } | |
119 | ++i; | |
120 | tokenType = in.peek(); | |
121 | } | |
122 | in.endArray(); | |
123 | return bitset; | |
124 | } | |
125 | ||
126 | public void write(JsonWriter out, BitSet src) throws IOException { | |
127 | if (src == null) { | |
128 | out.nullValue(); | |
129 | return; | |
130 | } | |
131 | ||
132 | out.beginArray(); | |
133 | for (int i = 0; i < src.length(); i++) { | |
134 | int value = (src.get(i)) ? 1 : 0; | |
135 | out.value(value); | |
136 | } | |
137 | out.endArray(); | |
138 | } | |
139 | }; | |
140 | ||
141 | public static final TypeAdapterFactory BIT_SET_FACTORY = newFactory(BitSet.class, BIT_SET); | |
142 | ||
143 | public static final TypeAdapter<Boolean> BOOLEAN = new TypeAdapter<Boolean>() { | |
144 | @Override | |
145 | public Boolean read(JsonReader in) throws IOException { | |
146 | if (in.peek() == JsonToken.NULL) { | |
147 | in.nextNull(); | |
148 | return null; | |
149 | } else if (in.peek() == JsonToken.STRING) { | |
150 | // support strings for compatibility with GSON 1.7 | |
151 | return Boolean.parseBoolean(in.nextString()); | |
152 | } | |
153 | return in.nextBoolean(); | |
154 | } | |
155 | @Override | |
156 | public void write(JsonWriter out, Boolean value) throws IOException { | |
157 | if (value == null) { | |
158 | out.nullValue(); | |
159 | return; | |
160 | } | |
161 | out.value(value); | |
162 | } | |
163 | }; | |
164 | ||
165 | /** | |
166 | * Writes a boolean as a string. Useful for map keys, where booleans aren't | |
167 | * otherwise permitted. | |
168 | */ | |
169 | public static final TypeAdapter<Boolean> BOOLEAN_AS_STRING = new TypeAdapter<Boolean>() { | |
170 | @Override public Boolean read(JsonReader in) throws IOException { | |
171 | if (in.peek() == JsonToken.NULL) { | |
172 | in.nextNull(); | |
173 | return null; | |
174 | } | |
175 | return Boolean.valueOf(in.nextString()); | |
176 | } | |
177 | ||
178 | @Override public void write(JsonWriter out, Boolean value) throws IOException { | |
179 | out.value(value == null ? "null" : value.toString()); | |
180 | } | |
181 | }; | |
182 | ||
183 | public static final TypeAdapterFactory BOOLEAN_FACTORY | |
184 | = newFactory(boolean.class, Boolean.class, BOOLEAN); | |
185 | ||
186 | public static final TypeAdapter<Number> BYTE = new TypeAdapter<Number>() { | |
187 | @Override | |
188 | public Number read(JsonReader in) throws IOException { | |
189 | if (in.peek() == JsonToken.NULL) { | |
190 | in.nextNull(); | |
191 | return null; | |
192 | } | |
193 | try { | |
194 | int intValue = in.nextInt(); | |
195 | return (byte) intValue; | |
196 | } catch (NumberFormatException e) { | |
197 | throw new JsonSyntaxException(e); | |
198 | } | |
199 | } | |
200 | @Override | |
201 | public void write(JsonWriter out, Number value) throws IOException { | |
202 | out.value(value); | |
203 | } | |
204 | }; | |
205 | ||
206 | public static final TypeAdapterFactory BYTE_FACTORY | |
207 | = newFactory(byte.class, Byte.class, BYTE); | |
208 | ||
209 | public static final TypeAdapter<Number> SHORT = new TypeAdapter<Number>() { | |
210 | @Override | |
211 | public Number read(JsonReader in) throws IOException { | |
212 | if (in.peek() == JsonToken.NULL) { | |
213 | in.nextNull(); | |
214 | return null; | |
215 | } | |
216 | try { | |
217 | return (short) in.nextInt(); | |
218 | } catch (NumberFormatException e) { | |
219 | throw new JsonSyntaxException(e); | |
220 | } | |
221 | } | |
222 | @Override | |
223 | public void write(JsonWriter out, Number value) throws IOException { | |
224 | out.value(value); | |
225 | } | |
226 | }; | |
227 | ||
228 | public static final TypeAdapterFactory SHORT_FACTORY | |
229 | = newFactory(short.class, Short.class, SHORT); | |
230 | ||
231 | public static final TypeAdapter<Number> INTEGER = new TypeAdapter<Number>() { | |
232 | @Override | |
233 | public Number read(JsonReader in) throws IOException { | |
234 | if (in.peek() == JsonToken.NULL) { | |
235 | in.nextNull(); | |
236 | return null; | |
237 | } | |
238 | try { | |
239 | return in.nextInt(); | |
240 | } catch (NumberFormatException e) { | |
241 | throw new JsonSyntaxException(e); | |
242 | } | |
243 | } | |
244 | @Override | |
245 | public void write(JsonWriter out, Number value) throws IOException { | |
246 | out.value(value); | |
247 | } | |
248 | }; | |
249 | ||
250 | public static final TypeAdapterFactory INTEGER_FACTORY | |
251 | = newFactory(int.class, Integer.class, INTEGER); | |
252 | ||
253 | public static final TypeAdapter<Number> LONG = new TypeAdapter<Number>() { | |
254 | @Override | |
255 | public Number read(JsonReader in) throws IOException { | |
256 | if (in.peek() == JsonToken.NULL) { | |
257 | in.nextNull(); | |
258 | return null; | |
259 | } | |
260 | try { | |
261 | return in.nextLong(); | |
262 | } catch (NumberFormatException e) { | |
263 | throw new JsonSyntaxException(e); | |
264 | } | |
265 | } | |
266 | @Override | |
267 | public void write(JsonWriter out, Number value) throws IOException { | |
268 | out.value(value); | |
269 | } | |
270 | }; | |
271 | ||
272 | public static final TypeAdapter<Number> FLOAT = new TypeAdapter<Number>() { | |
273 | @Override | |
274 | public Number read(JsonReader in) throws IOException { | |
275 | if (in.peek() == JsonToken.NULL) { | |
276 | in.nextNull(); | |
277 | return null; | |
278 | } | |
279 | return (float) in.nextDouble(); | |
280 | } | |
281 | @Override | |
282 | public void write(JsonWriter out, Number value) throws IOException { | |
283 | out.value(value); | |
284 | } | |
285 | }; | |
286 | ||
287 | public static final TypeAdapter<Number> DOUBLE = new TypeAdapter<Number>() { | |
288 | @Override | |
289 | public Number read(JsonReader in) throws IOException { | |
290 | if (in.peek() == JsonToken.NULL) { | |
291 | in.nextNull(); | |
292 | return null; | |
293 | } | |
294 | return in.nextDouble(); | |
295 | } | |
296 | @Override | |
297 | public void write(JsonWriter out, Number value) throws IOException { | |
298 | out.value(value); | |
299 | } | |
300 | }; | |
301 | ||
302 | public static final TypeAdapter<Number> NUMBER = new TypeAdapter<Number>() { | |
303 | @Override | |
304 | public Number read(JsonReader in) throws IOException { | |
305 | JsonToken jsonToken = in.peek(); | |
306 | switch (jsonToken) { | |
307 | case NULL: | |
308 | in.nextNull(); | |
309 | return null; | |
310 | case NUMBER: | |
311 | return new LazilyParsedNumber(in.nextString()); | |
312 | default: | |
313 | throw new JsonSyntaxException("Expecting number, got: " + jsonToken); | |
314 | } | |
315 | } | |
316 | @Override | |
317 | public void write(JsonWriter out, Number value) throws IOException { | |
318 | out.value(value); | |
319 | } | |
320 | }; | |
321 | ||
322 | public static final TypeAdapterFactory NUMBER_FACTORY = newFactory(Number.class, NUMBER); | |
323 | ||
324 | public static final TypeAdapter<Character> CHARACTER = new TypeAdapter<Character>() { | |
325 | @Override | |
326 | public Character read(JsonReader in) throws IOException { | |
327 | if (in.peek() == JsonToken.NULL) { | |
328 | in.nextNull(); | |
329 | return null; | |
330 | } | |
331 | String str = in.nextString(); | |
332 | if (str.length() != 1) { | |
333 | throw new JsonSyntaxException("Expecting character, got: " + str); | |
334 | } | |
335 | return str.charAt(0); | |
336 | } | |
337 | @Override | |
338 | public void write(JsonWriter out, Character value) throws IOException { | |
339 | out.value(value == null ? null : String.valueOf(value)); | |
340 | } | |
341 | }; | |
342 | ||
343 | public static final TypeAdapterFactory CHARACTER_FACTORY | |
344 | = newFactory(char.class, Character.class, CHARACTER); | |
345 | ||
346 | public static final TypeAdapter<String> STRING = new TypeAdapter<String>() { | |
347 | @Override | |
348 | public String read(JsonReader in) throws IOException { | |
349 | JsonToken peek = in.peek(); | |
350 | if (peek == JsonToken.NULL) { | |
351 | in.nextNull(); | |
352 | return null; | |
353 | } | |
354 | /* coerce booleans to strings for backwards compatibility */ | |
355 | if (peek == JsonToken.BOOLEAN) { | |
356 | return Boolean.toString(in.nextBoolean()); | |
357 | } | |
358 | return in.nextString(); | |
359 | } | |
360 | @Override | |
361 | public void write(JsonWriter out, String value) throws IOException { | |
362 | out.value(value); | |
363 | } | |
364 | }; | |
070d3ab2 | 365 | |
cfd903b6 MG |
366 | public static final TypeAdapter<BigDecimal> BIG_DECIMAL = new TypeAdapter<BigDecimal>() { |
367 | @Override public BigDecimal read(JsonReader in) throws IOException { | |
368 | if (in.peek() == JsonToken.NULL) { | |
369 | in.nextNull(); | |
370 | return null; | |
371 | } | |
372 | try { | |
373 | return new BigDecimal(in.nextString()); | |
374 | } catch (NumberFormatException e) { | |
375 | throw new JsonSyntaxException(e); | |
376 | } | |
377 | } | |
378 | ||
379 | @Override public void write(JsonWriter out, BigDecimal value) throws IOException { | |
380 | out.value(value); | |
381 | } | |
382 | }; | |
070d3ab2 | 383 | |
cfd903b6 MG |
384 | public static final TypeAdapter<BigInteger> BIG_INTEGER = new TypeAdapter<BigInteger>() { |
385 | @Override public BigInteger read(JsonReader in) throws IOException { | |
386 | if (in.peek() == JsonToken.NULL) { | |
387 | in.nextNull(); | |
388 | return null; | |
389 | } | |
390 | try { | |
391 | return new BigInteger(in.nextString()); | |
392 | } catch (NumberFormatException e) { | |
393 | throw new JsonSyntaxException(e); | |
394 | } | |
395 | } | |
396 | ||
397 | @Override public void write(JsonWriter out, BigInteger value) throws IOException { | |
398 | out.value(value); | |
399 | } | |
400 | }; | |
401 | ||
402 | public static final TypeAdapterFactory STRING_FACTORY = newFactory(String.class, STRING); | |
403 | ||
404 | public static final TypeAdapter<StringBuilder> STRING_BUILDER = new TypeAdapter<StringBuilder>() { | |
405 | @Override | |
406 | public StringBuilder read(JsonReader in) throws IOException { | |
407 | if (in.peek() == JsonToken.NULL) { | |
408 | in.nextNull(); | |
409 | return null; | |
410 | } | |
411 | return new StringBuilder(in.nextString()); | |
412 | } | |
413 | @Override | |
414 | public void write(JsonWriter out, StringBuilder value) throws IOException { | |
415 | out.value(value == null ? null : value.toString()); | |
416 | } | |
417 | }; | |
418 | ||
419 | public static final TypeAdapterFactory STRING_BUILDER_FACTORY = | |
420 | newFactory(StringBuilder.class, STRING_BUILDER); | |
421 | ||
422 | public static final TypeAdapter<StringBuffer> STRING_BUFFER = new TypeAdapter<StringBuffer>() { | |
423 | @Override | |
424 | public StringBuffer read(JsonReader in) throws IOException { | |
425 | if (in.peek() == JsonToken.NULL) { | |
426 | in.nextNull(); | |
427 | return null; | |
428 | } | |
429 | return new StringBuffer(in.nextString()); | |
430 | } | |
431 | @Override | |
432 | public void write(JsonWriter out, StringBuffer value) throws IOException { | |
433 | out.value(value == null ? null : value.toString()); | |
434 | } | |
435 | }; | |
436 | ||
437 | public static final TypeAdapterFactory STRING_BUFFER_FACTORY = | |
438 | newFactory(StringBuffer.class, STRING_BUFFER); | |
439 | ||
440 | public static final TypeAdapter<URL> URL = new TypeAdapter<URL>() { | |
441 | @Override | |
442 | public URL read(JsonReader in) throws IOException { | |
443 | if (in.peek() == JsonToken.NULL) { | |
444 | in.nextNull(); | |
445 | return null; | |
446 | } | |
447 | String nextString = in.nextString(); | |
448 | return "null".equals(nextString) ? null : new URL(nextString); | |
449 | } | |
450 | @Override | |
451 | public void write(JsonWriter out, URL value) throws IOException { | |
452 | out.value(value == null ? null : value.toExternalForm()); | |
453 | } | |
454 | }; | |
455 | ||
456 | public static final TypeAdapterFactory URL_FACTORY = newFactory(URL.class, URL); | |
457 | ||
458 | public static final TypeAdapter<URI> URI = new TypeAdapter<URI>() { | |
459 | @Override | |
460 | public URI read(JsonReader in) throws IOException { | |
461 | if (in.peek() == JsonToken.NULL) { | |
462 | in.nextNull(); | |
463 | return null; | |
464 | } | |
465 | try { | |
466 | String nextString = in.nextString(); | |
467 | return "null".equals(nextString) ? null : new URI(nextString); | |
468 | } catch (URISyntaxException e) { | |
469 | throw new JsonIOException(e); | |
470 | } | |
471 | } | |
472 | @Override | |
473 | public void write(JsonWriter out, URI value) throws IOException { | |
474 | out.value(value == null ? null : value.toASCIIString()); | |
475 | } | |
476 | }; | |
477 | ||
478 | public static final TypeAdapterFactory URI_FACTORY = newFactory(URI.class, URI); | |
479 | ||
480 | public static final TypeAdapter<InetAddress> INET_ADDRESS = new TypeAdapter<InetAddress>() { | |
481 | @Override | |
482 | public InetAddress read(JsonReader in) throws IOException { | |
483 | if (in.peek() == JsonToken.NULL) { | |
484 | in.nextNull(); | |
485 | return null; | |
486 | } | |
487 | // regrettably, this should have included both the host name and the host address | |
488 | return InetAddress.getByName(in.nextString()); | |
489 | } | |
490 | @Override | |
491 | public void write(JsonWriter out, InetAddress value) throws IOException { | |
492 | out.value(value == null ? null : value.getHostAddress()); | |
493 | } | |
494 | }; | |
495 | ||
496 | public static final TypeAdapterFactory INET_ADDRESS_FACTORY = | |
497 | newTypeHierarchyFactory(InetAddress.class, INET_ADDRESS); | |
498 | ||
499 | public static final TypeAdapter<UUID> UUID = new TypeAdapter<UUID>() { | |
500 | @Override | |
501 | public UUID read(JsonReader in) throws IOException { | |
502 | if (in.peek() == JsonToken.NULL) { | |
503 | in.nextNull(); | |
504 | return null; | |
505 | } | |
506 | return java.util.UUID.fromString(in.nextString()); | |
507 | } | |
508 | @Override | |
509 | public void write(JsonWriter out, UUID value) throws IOException { | |
510 | out.value(value == null ? null : value.toString()); | |
511 | } | |
512 | }; | |
513 | ||
514 | public static final TypeAdapterFactory UUID_FACTORY = newFactory(UUID.class, UUID); | |
515 | ||
516 | public static final TypeAdapterFactory TIMESTAMP_FACTORY = new TypeAdapterFactory() { | |
517 | @SuppressWarnings("unchecked") // we use a runtime check to make sure the 'T's equal | |
518 | public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) { | |
519 | if (typeToken.getRawType() != Timestamp.class) { | |
520 | return null; | |
521 | } | |
522 | ||
523 | final TypeAdapter<Date> dateTypeAdapter = gson.getAdapter(Date.class); | |
524 | return (TypeAdapter<T>) new TypeAdapter<Timestamp>() { | |
525 | @Override public Timestamp read(JsonReader in) throws IOException { | |
526 | Date date = dateTypeAdapter.read(in); | |
527 | return date != null ? new Timestamp(date.getTime()) : null; | |
528 | } | |
529 | ||
530 | @Override public void write(JsonWriter out, Timestamp value) throws IOException { | |
531 | dateTypeAdapter.write(out, value); | |
532 | } | |
533 | }; | |
534 | } | |
535 | }; | |
536 | ||
537 | public static final TypeAdapter<Calendar> CALENDAR = new TypeAdapter<Calendar>() { | |
538 | private static final String YEAR = "year"; | |
539 | private static final String MONTH = "month"; | |
540 | private static final String DAY_OF_MONTH = "dayOfMonth"; | |
541 | private static final String HOUR_OF_DAY = "hourOfDay"; | |
542 | private static final String MINUTE = "minute"; | |
543 | private static final String SECOND = "second"; | |
544 | ||
545 | @Override | |
546 | public Calendar read(JsonReader in) throws IOException { | |
547 | if (in.peek() == JsonToken.NULL) { | |
548 | in.nextNull(); | |
549 | return null; | |
550 | } | |
551 | in.beginObject(); | |
552 | int year = 0; | |
553 | int month = 0; | |
554 | int dayOfMonth = 0; | |
555 | int hourOfDay = 0; | |
556 | int minute = 0; | |
557 | int second = 0; | |
558 | while (in.peek() != JsonToken.END_OBJECT) { | |
559 | String name = in.nextName(); | |
560 | int value = in.nextInt(); | |
561 | if (YEAR.equals(name)) { | |
562 | year = value; | |
563 | } else if (MONTH.equals(name)) { | |
564 | month = value; | |
565 | } else if (DAY_OF_MONTH.equals(name)) { | |
566 | dayOfMonth = value; | |
567 | } else if (HOUR_OF_DAY.equals(name)) { | |
568 | hourOfDay = value; | |
569 | } else if (MINUTE.equals(name)) { | |
570 | minute = value; | |
571 | } else if (SECOND.equals(name)) { | |
572 | second = value; | |
573 | } | |
574 | } | |
575 | in.endObject(); | |
576 | return new GregorianCalendar(year, month, dayOfMonth, hourOfDay, minute, second); | |
577 | } | |
578 | ||
579 | @Override | |
580 | public void write(JsonWriter out, Calendar value) throws IOException { | |
581 | if (value == null) { | |
582 | out.nullValue(); | |
583 | return; | |
584 | } | |
585 | out.beginObject(); | |
586 | out.name(YEAR); | |
587 | out.value(value.get(Calendar.YEAR)); | |
588 | out.name(MONTH); | |
589 | out.value(value.get(Calendar.MONTH)); | |
590 | out.name(DAY_OF_MONTH); | |
591 | out.value(value.get(Calendar.DAY_OF_MONTH)); | |
592 | out.name(HOUR_OF_DAY); | |
593 | out.value(value.get(Calendar.HOUR_OF_DAY)); | |
594 | out.name(MINUTE); | |
595 | out.value(value.get(Calendar.MINUTE)); | |
596 | out.name(SECOND); | |
597 | out.value(value.get(Calendar.SECOND)); | |
598 | out.endObject(); | |
599 | } | |
600 | }; | |
601 | ||
602 | public static final TypeAdapterFactory CALENDAR_FACTORY = | |
603 | newFactoryForMultipleTypes(Calendar.class, GregorianCalendar.class, CALENDAR); | |
604 | ||
605 | public static final TypeAdapter<Locale> LOCALE = new TypeAdapter<Locale>() { | |
606 | @Override | |
607 | public Locale read(JsonReader in) throws IOException { | |
608 | if (in.peek() == JsonToken.NULL) { | |
609 | in.nextNull(); | |
610 | return null; | |
611 | } | |
612 | String locale = in.nextString(); | |
613 | StringTokenizer tokenizer = new StringTokenizer(locale, "_"); | |
614 | String language = null; | |
615 | String country = null; | |
616 | String variant = null; | |
617 | if (tokenizer.hasMoreElements()) { | |
618 | language = tokenizer.nextToken(); | |
619 | } | |
620 | if (tokenizer.hasMoreElements()) { | |
621 | country = tokenizer.nextToken(); | |
622 | } | |
623 | if (tokenizer.hasMoreElements()) { | |
624 | variant = tokenizer.nextToken(); | |
625 | } | |
626 | if (country == null && variant == null) { | |
627 | return new Locale(language); | |
628 | } else if (variant == null) { | |
629 | return new Locale(language, country); | |
630 | } else { | |
631 | return new Locale(language, country, variant); | |
632 | } | |
633 | } | |
634 | @Override | |
635 | public void write(JsonWriter out, Locale value) throws IOException { | |
636 | out.value(value == null ? null : value.toString()); | |
637 | } | |
638 | }; | |
639 | ||
640 | public static final TypeAdapterFactory LOCALE_FACTORY = newFactory(Locale.class, LOCALE); | |
641 | ||
642 | public static final TypeAdapter<JsonElement> JSON_ELEMENT = new TypeAdapter<JsonElement>() { | |
643 | @Override public JsonElement read(JsonReader in) throws IOException { | |
644 | switch (in.peek()) { | |
645 | case STRING: | |
646 | return new JsonPrimitive(in.nextString()); | |
647 | case NUMBER: | |
648 | String number = in.nextString(); | |
649 | return new JsonPrimitive(new LazilyParsedNumber(number)); | |
650 | case BOOLEAN: | |
651 | return new JsonPrimitive(in.nextBoolean()); | |
652 | case NULL: | |
653 | in.nextNull(); | |
654 | return JsonNull.INSTANCE; | |
655 | case BEGIN_ARRAY: | |
656 | JsonArray array = new JsonArray(); | |
657 | in.beginArray(); | |
658 | while (in.hasNext()) { | |
659 | array.add(read(in)); | |
660 | } | |
661 | in.endArray(); | |
662 | return array; | |
663 | case BEGIN_OBJECT: | |
664 | JsonObject object = new JsonObject(); | |
665 | in.beginObject(); | |
666 | while (in.hasNext()) { | |
667 | object.add(in.nextName(), read(in)); | |
668 | } | |
669 | in.endObject(); | |
670 | return object; | |
671 | case END_DOCUMENT: | |
672 | case NAME: | |
673 | case END_OBJECT: | |
674 | case END_ARRAY: | |
675 | default: | |
676 | throw new IllegalArgumentException(); | |
677 | } | |
678 | } | |
679 | ||
680 | @Override public void write(JsonWriter out, JsonElement value) throws IOException { | |
681 | if (value == null || value.isJsonNull()) { | |
682 | out.nullValue(); | |
683 | } else if (value.isJsonPrimitive()) { | |
684 | JsonPrimitive primitive = value.getAsJsonPrimitive(); | |
685 | if (primitive.isNumber()) { | |
686 | out.value(primitive.getAsNumber()); | |
687 | } else if (primitive.isBoolean()) { | |
688 | out.value(primitive.getAsBoolean()); | |
689 | } else { | |
690 | out.value(primitive.getAsString()); | |
691 | } | |
692 | ||
693 | } else if (value.isJsonArray()) { | |
694 | out.beginArray(); | |
695 | for (JsonElement e : value.getAsJsonArray()) { | |
696 | write(out, e); | |
697 | } | |
698 | out.endArray(); | |
699 | ||
700 | } else if (value.isJsonObject()) { | |
701 | out.beginObject(); | |
702 | for (Map.Entry<String, JsonElement> e : value.getAsJsonObject().entrySet()) { | |
703 | out.name(e.getKey()); | |
704 | write(out, e.getValue()); | |
705 | } | |
706 | out.endObject(); | |
707 | ||
708 | } else { | |
709 | throw new IllegalArgumentException("Couldn't write " + value.getClass()); | |
710 | } | |
711 | } | |
712 | }; | |
713 | ||
714 | public static final TypeAdapterFactory JSON_ELEMENT_FACTORY | |
715 | = newTypeHierarchyFactory(JsonElement.class, JSON_ELEMENT); | |
716 | ||
717 | private static final class EnumTypeAdapter<T extends Enum<T>> extends TypeAdapter<T> { | |
718 | private final Map<String, T> nameToConstant = new HashMap<String, T>(); | |
719 | private final Map<T, String> constantToName = new HashMap<T, String>(); | |
720 | ||
721 | public EnumTypeAdapter(Class<T> classOfT) { | |
722 | try { | |
723 | for (T constant : classOfT.getEnumConstants()) { | |
724 | String name = constant.name(); | |
725 | SerializedName annotation = classOfT.getField(name).getAnnotation(SerializedName.class); | |
726 | if (annotation != null) { | |
727 | name = annotation.value(); | |
728 | } | |
729 | nameToConstant.put(name, constant); | |
730 | constantToName.put(constant, name); | |
731 | } | |
732 | } catch (NoSuchFieldException e) { | |
733 | throw new AssertionError(); | |
734 | } | |
735 | } | |
736 | public T read(JsonReader in) throws IOException { | |
737 | if (in.peek() == JsonToken.NULL) { | |
738 | in.nextNull(); | |
739 | return null; | |
740 | } | |
741 | return nameToConstant.get(in.nextString()); | |
742 | } | |
743 | ||
744 | public void write(JsonWriter out, T value) throws IOException { | |
745 | out.value(value == null ? null : constantToName.get(value)); | |
746 | } | |
747 | } | |
748 | ||
749 | public static final TypeAdapterFactory ENUM_FACTORY = newEnumTypeHierarchyFactory(); | |
750 | ||
751 | public static TypeAdapterFactory newEnumTypeHierarchyFactory() { | |
752 | return new TypeAdapterFactory() { | |
753 | @SuppressWarnings({"rawtypes", "unchecked"}) | |
754 | public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) { | |
755 | Class<? super T> rawType = typeToken.getRawType(); | |
756 | if (!Enum.class.isAssignableFrom(rawType) || rawType == Enum.class) { | |
757 | return null; | |
758 | } | |
759 | if (!rawType.isEnum()) { | |
760 | rawType = rawType.getSuperclass(); // handle anonymous subclasses | |
761 | } | |
762 | return (TypeAdapter<T>) new EnumTypeAdapter(rawType); | |
763 | } | |
764 | }; | |
765 | } | |
766 | ||
767 | public static <TT> TypeAdapterFactory newFactory( | |
768 | final TypeToken<TT> type, final TypeAdapter<TT> typeAdapter) { | |
769 | return new TypeAdapterFactory() { | |
770 | @SuppressWarnings("unchecked") // we use a runtime check to make sure the 'T's equal | |
771 | public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) { | |
772 | return typeToken.equals(type) ? (TypeAdapter<T>) typeAdapter : null; | |
773 | } | |
774 | }; | |
775 | } | |
776 | ||
777 | public static <TT> TypeAdapterFactory newFactory( | |
778 | final Class<TT> type, final TypeAdapter<TT> typeAdapter) { | |
779 | return new TypeAdapterFactory() { | |
780 | @SuppressWarnings("unchecked") // we use a runtime check to make sure the 'T's equal | |
781 | public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) { | |
782 | return typeToken.getRawType() == type ? (TypeAdapter<T>) typeAdapter : null; | |
783 | } | |
784 | @Override public String toString() { | |
785 | return "Factory[type=" + type.getName() + ",adapter=" + typeAdapter + "]"; | |
786 | } | |
787 | }; | |
788 | } | |
789 | ||
790 | public static <TT> TypeAdapterFactory newFactory( | |
791 | final Class<TT> unboxed, final Class<TT> boxed, final TypeAdapter<? super TT> typeAdapter) { | |
792 | return new TypeAdapterFactory() { | |
793 | @SuppressWarnings("unchecked") // we use a runtime check to make sure the 'T's equal | |
794 | public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) { | |
795 | Class<? super T> rawType = typeToken.getRawType(); | |
796 | return (rawType == unboxed || rawType == boxed) ? (TypeAdapter<T>) typeAdapter : null; | |
797 | } | |
798 | @Override public String toString() { | |
799 | return "Factory[type=" + boxed.getName() | |
800 | + "+" + unboxed.getName() + ",adapter=" + typeAdapter + "]"; | |
801 | } | |
802 | }; | |
803 | } | |
804 | ||
805 | public static <TT> TypeAdapterFactory newFactoryForMultipleTypes(final Class<TT> base, | |
806 | final Class<? extends TT> sub, final TypeAdapter<? super TT> typeAdapter) { | |
807 | return new TypeAdapterFactory() { | |
808 | @SuppressWarnings("unchecked") // we use a runtime check to make sure the 'T's equal | |
809 | public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) { | |
810 | Class<? super T> rawType = typeToken.getRawType(); | |
811 | return (rawType == base || rawType == sub) ? (TypeAdapter<T>) typeAdapter : null; | |
812 | } | |
813 | @Override public String toString() { | |
814 | return "Factory[type=" + base.getName() | |
815 | + "+" + sub.getName() + ",adapter=" + typeAdapter + "]"; | |
816 | } | |
817 | }; | |
818 | } | |
819 | ||
820 | public static <TT> TypeAdapterFactory newTypeHierarchyFactory( | |
821 | final Class<TT> clazz, final TypeAdapter<TT> typeAdapter) { | |
822 | return new TypeAdapterFactory() { | |
823 | @SuppressWarnings("unchecked") | |
824 | public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) { | |
825 | return clazz.isAssignableFrom(typeToken.getRawType()) ? (TypeAdapter<T>) typeAdapter : null; | |
826 | } | |
827 | @Override public String toString() { | |
828 | return "Factory[typeHierarchy=" + clazz.getName() + ",adapter=" + typeAdapter + "]"; | |
829 | } | |
830 | }; | |
831 | } | |
832 | } |