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