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.Gson; 20 import com.google.gson.JsonArray; 21 import com.google.gson.JsonElement; 22 import com.google.gson.JsonIOException; 23 import com.google.gson.JsonNull; 24 import com.google.gson.JsonObject; 25 import com.google.gson.JsonPrimitive; 26 import com.google.gson.JsonSyntaxException; 27 import com.google.gson.TypeAdapter; 28 import com.google.gson.TypeAdapterFactory; 29 import com.google.gson.annotations.SerializedName; 30 import com.google.gson.internal.LazilyParsedNumber; 31 import com.google.gson.reflect.TypeToken; 32 import com.google.gson.stream.JsonReader; 33 import com.google.gson.stream.JsonToken; 34 import com.google.gson.stream.JsonWriter; 35 import java.io.IOException; 36 import java.lang.reflect.AccessibleObject; 37 import java.lang.reflect.Field; 38 import java.math.BigDecimal; 39 import java.math.BigInteger; 40 import java.net.InetAddress; 41 import java.net.URI; 42 import java.net.URISyntaxException; 43 import java.net.URL; 44 import java.security.AccessController; 45 import java.security.PrivilegedAction; 46 import java.util.ArrayDeque; 47 import java.util.ArrayList; 48 import java.util.BitSet; 49 import java.util.Calendar; 50 import java.util.Currency; 51 import java.util.Deque; 52 import java.util.GregorianCalendar; 53 import java.util.HashMap; 54 import java.util.List; 55 import java.util.Locale; 56 import java.util.Map; 57 import java.util.StringTokenizer; 58 import java.util.UUID; 59 import java.util.concurrent.atomic.AtomicBoolean; 60 import java.util.concurrent.atomic.AtomicInteger; 61 import java.util.concurrent.atomic.AtomicIntegerArray; 62 63 /** 64 * Type adapters for basic types. 65 */ 66 public final class TypeAdapters { TypeAdapters()67 private TypeAdapters() { 68 throw new UnsupportedOperationException(); 69 } 70 71 @SuppressWarnings("rawtypes") 72 public static final TypeAdapter<Class> CLASS = new TypeAdapter<Class>() { 73 @Override 74 public void write(JsonWriter out, Class value) throws IOException { 75 throw new UnsupportedOperationException("Attempted to serialize java.lang.Class: " 76 + value.getName() + ". Forgot to register a type adapter?"); 77 } 78 @Override 79 public Class read(JsonReader in) throws IOException { 80 throw new UnsupportedOperationException( 81 "Attempted to deserialize a java.lang.Class. Forgot to register a type adapter?"); 82 } 83 }.nullSafe(); 84 85 public static final TypeAdapterFactory CLASS_FACTORY = newFactory(Class.class, CLASS); 86 87 public static final TypeAdapter<BitSet> BIT_SET = new TypeAdapter<BitSet>() { 88 @Override public BitSet read(JsonReader in) throws IOException { 89 BitSet bitset = new BitSet(); 90 in.beginArray(); 91 int i = 0; 92 JsonToken tokenType = in.peek(); 93 while (tokenType != JsonToken.END_ARRAY) { 94 boolean set; 95 switch (tokenType) { 96 case NUMBER: 97 case STRING: 98 int intValue = in.nextInt(); 99 if (intValue == 0) { 100 set = false; 101 } else if (intValue == 1) { 102 set = true; 103 } else { 104 throw new JsonSyntaxException("Invalid bitset value " + intValue + ", expected 0 or 1; at path " + in.getPreviousPath()); 105 } 106 break; 107 case BOOLEAN: 108 set = in.nextBoolean(); 109 break; 110 default: 111 throw new JsonSyntaxException("Invalid bitset value type: " + tokenType + "; at path " + in.getPath()); 112 } 113 if (set) { 114 bitset.set(i); 115 } 116 ++i; 117 tokenType = in.peek(); 118 } 119 in.endArray(); 120 return bitset; 121 } 122 123 @Override public void write(JsonWriter out, BitSet src) throws IOException { 124 out.beginArray(); 125 for (int i = 0, length = src.length(); i < length; i++) { 126 int value = (src.get(i)) ? 1 : 0; 127 out.value(value); 128 } 129 out.endArray(); 130 } 131 }.nullSafe(); 132 133 public static final TypeAdapterFactory BIT_SET_FACTORY = newFactory(BitSet.class, BIT_SET); 134 135 public static final TypeAdapter<Boolean> BOOLEAN = new TypeAdapter<Boolean>() { 136 @Override 137 public Boolean read(JsonReader in) throws IOException { 138 JsonToken peek = in.peek(); 139 if (peek == JsonToken.NULL) { 140 in.nextNull(); 141 return null; 142 } else if (peek == JsonToken.STRING) { 143 // support strings for compatibility with GSON 1.7 144 return Boolean.parseBoolean(in.nextString()); 145 } 146 return in.nextBoolean(); 147 } 148 @Override 149 public void write(JsonWriter out, Boolean value) throws IOException { 150 out.value(value); 151 } 152 }; 153 154 /** 155 * Writes a boolean as a string. Useful for map keys, where booleans aren't 156 * otherwise permitted. 157 */ 158 public static final TypeAdapter<Boolean> BOOLEAN_AS_STRING = new TypeAdapter<Boolean>() { 159 @Override public Boolean read(JsonReader in) throws IOException { 160 if (in.peek() == JsonToken.NULL) { 161 in.nextNull(); 162 return null; 163 } 164 return Boolean.valueOf(in.nextString()); 165 } 166 167 @Override public void write(JsonWriter out, Boolean value) throws IOException { 168 out.value(value == null ? "null" : value.toString()); 169 } 170 }; 171 172 public static final TypeAdapterFactory BOOLEAN_FACTORY 173 = newFactory(boolean.class, Boolean.class, BOOLEAN); 174 175 public static final TypeAdapter<Number> BYTE = new TypeAdapter<Number>() { 176 @Override 177 public Number read(JsonReader in) throws IOException { 178 if (in.peek() == JsonToken.NULL) { 179 in.nextNull(); 180 return null; 181 } 182 183 int intValue; 184 try { 185 intValue = in.nextInt(); 186 } catch (NumberFormatException e) { 187 throw new JsonSyntaxException(e); 188 } 189 // Allow up to 255 to support unsigned values 190 if (intValue > 255 || intValue < Byte.MIN_VALUE) { 191 throw new JsonSyntaxException("Lossy conversion from " + intValue + " to byte; at path " + in.getPreviousPath()); 192 } 193 return (byte) intValue; 194 } 195 @Override 196 public void write(JsonWriter out, Number value) throws IOException { 197 if (value == null) { 198 out.nullValue(); 199 } else { 200 out.value(value.byteValue()); 201 } 202 } 203 }; 204 205 public static final TypeAdapterFactory BYTE_FACTORY 206 = newFactory(byte.class, Byte.class, BYTE); 207 208 public static final TypeAdapter<Number> SHORT = new TypeAdapter<Number>() { 209 @Override 210 public Number read(JsonReader in) throws IOException { 211 if (in.peek() == JsonToken.NULL) { 212 in.nextNull(); 213 return null; 214 } 215 216 int intValue; 217 try { 218 intValue = in.nextInt(); 219 } catch (NumberFormatException e) { 220 throw new JsonSyntaxException(e); 221 } 222 // Allow up to 65535 to support unsigned values 223 if (intValue > 65535 || intValue < Short.MIN_VALUE) { 224 throw new JsonSyntaxException("Lossy conversion from " + intValue + " to short; at path " + in.getPreviousPath()); 225 } 226 return (short) intValue; 227 } 228 @Override 229 public void write(JsonWriter out, Number value) throws IOException { 230 if (value == null) { 231 out.nullValue(); 232 } else { 233 out.value(value.shortValue()); 234 } 235 } 236 }; 237 238 public static final TypeAdapterFactory SHORT_FACTORY 239 = newFactory(short.class, Short.class, SHORT); 240 241 public static final TypeAdapter<Number> INTEGER = new TypeAdapter<Number>() { 242 @Override 243 public Number read(JsonReader in) throws IOException { 244 if (in.peek() == JsonToken.NULL) { 245 in.nextNull(); 246 return null; 247 } 248 try { 249 return in.nextInt(); 250 } catch (NumberFormatException e) { 251 throw new JsonSyntaxException(e); 252 } 253 } 254 @Override 255 public void write(JsonWriter out, Number value) throws IOException { 256 if (value == null) { 257 out.nullValue(); 258 } else { 259 out.value(value.intValue()); 260 } 261 } 262 }; 263 public static final TypeAdapterFactory INTEGER_FACTORY 264 = newFactory(int.class, Integer.class, INTEGER); 265 266 public static final TypeAdapter<AtomicInteger> ATOMIC_INTEGER = new TypeAdapter<AtomicInteger>() { 267 @Override public AtomicInteger read(JsonReader in) throws IOException { 268 try { 269 return new AtomicInteger(in.nextInt()); 270 } catch (NumberFormatException e) { 271 throw new JsonSyntaxException(e); 272 } 273 } 274 @Override public void write(JsonWriter out, AtomicInteger value) throws IOException { 275 out.value(value.get()); 276 } 277 }.nullSafe(); 278 public static final TypeAdapterFactory ATOMIC_INTEGER_FACTORY = 279 newFactory(AtomicInteger.class, TypeAdapters.ATOMIC_INTEGER); 280 281 public static final TypeAdapter<AtomicBoolean> ATOMIC_BOOLEAN = new TypeAdapter<AtomicBoolean>() { 282 @Override public AtomicBoolean read(JsonReader in) throws IOException { 283 return new AtomicBoolean(in.nextBoolean()); 284 } 285 @Override public void write(JsonWriter out, AtomicBoolean value) throws IOException { 286 out.value(value.get()); 287 } 288 }.nullSafe(); 289 public static final TypeAdapterFactory ATOMIC_BOOLEAN_FACTORY = 290 newFactory(AtomicBoolean.class, TypeAdapters.ATOMIC_BOOLEAN); 291 292 public static final TypeAdapter<AtomicIntegerArray> ATOMIC_INTEGER_ARRAY = new TypeAdapter<AtomicIntegerArray>() { 293 @Override public AtomicIntegerArray read(JsonReader in) throws IOException { 294 List<Integer> list = new ArrayList<>(); 295 in.beginArray(); 296 while (in.hasNext()) { 297 try { 298 int integer = in.nextInt(); 299 list.add(integer); 300 } catch (NumberFormatException e) { 301 throw new JsonSyntaxException(e); 302 } 303 } 304 in.endArray(); 305 int length = list.size(); 306 AtomicIntegerArray array = new AtomicIntegerArray(length); 307 for (int i = 0; i < length; ++i) { 308 array.set(i, list.get(i)); 309 } 310 return array; 311 } 312 @Override public void write(JsonWriter out, AtomicIntegerArray value) throws IOException { 313 out.beginArray(); 314 for (int i = 0, length = value.length(); i < length; i++) { 315 out.value(value.get(i)); 316 } 317 out.endArray(); 318 } 319 }.nullSafe(); 320 public static final TypeAdapterFactory ATOMIC_INTEGER_ARRAY_FACTORY = 321 newFactory(AtomicIntegerArray.class, TypeAdapters.ATOMIC_INTEGER_ARRAY); 322 323 public static final TypeAdapter<Number> LONG = new TypeAdapter<Number>() { 324 @Override 325 public Number read(JsonReader in) throws IOException { 326 if (in.peek() == JsonToken.NULL) { 327 in.nextNull(); 328 return null; 329 } 330 try { 331 return in.nextLong(); 332 } catch (NumberFormatException e) { 333 throw new JsonSyntaxException(e); 334 } 335 } 336 @Override 337 public void write(JsonWriter out, Number value) throws IOException { 338 if (value == null) { 339 out.nullValue(); 340 } else { 341 out.value(value.longValue()); 342 } 343 } 344 }; 345 346 public static final TypeAdapter<Number> FLOAT = new TypeAdapter<Number>() { 347 @Override 348 public Number read(JsonReader in) throws IOException { 349 if (in.peek() == JsonToken.NULL) { 350 in.nextNull(); 351 return null; 352 } 353 return (float) in.nextDouble(); 354 } 355 @Override 356 public void write(JsonWriter out, Number value) throws IOException { 357 if (value == null) { 358 out.nullValue(); 359 } else { 360 // For backward compatibility don't call `JsonWriter.value(float)` because that method has 361 // been newly added and not all custom JsonWriter implementations might override it yet 362 Number floatNumber = value instanceof Float ? value : value.floatValue(); 363 out.value(floatNumber); 364 } 365 } 366 }; 367 368 public static final TypeAdapter<Number> DOUBLE = new TypeAdapter<Number>() { 369 @Override 370 public Number read(JsonReader in) throws IOException { 371 if (in.peek() == JsonToken.NULL) { 372 in.nextNull(); 373 return null; 374 } 375 return in.nextDouble(); 376 } 377 @Override 378 public void write(JsonWriter out, Number value) throws IOException { 379 if (value == null) { 380 out.nullValue(); 381 } else { 382 out.value(value.doubleValue()); 383 } 384 } 385 }; 386 387 public static final TypeAdapter<Character> CHARACTER = new TypeAdapter<Character>() { 388 @Override 389 public Character read(JsonReader in) throws IOException { 390 if (in.peek() == JsonToken.NULL) { 391 in.nextNull(); 392 return null; 393 } 394 String str = in.nextString(); 395 if (str.length() != 1) { 396 throw new JsonSyntaxException("Expecting character, got: " + str + "; at " + in.getPreviousPath()); 397 } 398 return str.charAt(0); 399 } 400 @Override 401 public void write(JsonWriter out, Character value) throws IOException { 402 out.value(value == null ? null : String.valueOf(value)); 403 } 404 }; 405 406 public static final TypeAdapterFactory CHARACTER_FACTORY 407 = newFactory(char.class, Character.class, CHARACTER); 408 409 public static final TypeAdapter<String> STRING = new TypeAdapter<String>() { 410 @Override 411 public String read(JsonReader in) throws IOException { 412 JsonToken peek = in.peek(); 413 if (peek == JsonToken.NULL) { 414 in.nextNull(); 415 return null; 416 } 417 /* coerce booleans to strings for backwards compatibility */ 418 if (peek == JsonToken.BOOLEAN) { 419 return Boolean.toString(in.nextBoolean()); 420 } 421 return in.nextString(); 422 } 423 @Override 424 public void write(JsonWriter out, String value) throws IOException { 425 out.value(value); 426 } 427 }; 428 429 public static final TypeAdapter<BigDecimal> BIG_DECIMAL = new TypeAdapter<BigDecimal>() { 430 @Override public BigDecimal read(JsonReader in) throws IOException { 431 if (in.peek() == JsonToken.NULL) { 432 in.nextNull(); 433 return null; 434 } 435 String s = in.nextString(); 436 try { 437 return new BigDecimal(s); 438 } catch (NumberFormatException e) { 439 throw new JsonSyntaxException("Failed parsing '" + s + "' as BigDecimal; at path " + in.getPreviousPath(), e); 440 } 441 } 442 443 @Override public void write(JsonWriter out, BigDecimal value) throws IOException { 444 out.value(value); 445 } 446 }; 447 448 public static final TypeAdapter<BigInteger> BIG_INTEGER = new TypeAdapter<BigInteger>() { 449 @Override public BigInteger read(JsonReader in) throws IOException { 450 if (in.peek() == JsonToken.NULL) { 451 in.nextNull(); 452 return null; 453 } 454 String s = in.nextString(); 455 try { 456 return new BigInteger(s); 457 } catch (NumberFormatException e) { 458 throw new JsonSyntaxException("Failed parsing '" + s + "' as BigInteger; at path " + in.getPreviousPath(), e); 459 } 460 } 461 462 @Override public void write(JsonWriter out, BigInteger value) throws IOException { 463 out.value(value); 464 } 465 }; 466 467 public static final TypeAdapter<LazilyParsedNumber> LAZILY_PARSED_NUMBER = new TypeAdapter<LazilyParsedNumber>() { 468 // Normally users should not be able to access and deserialize LazilyParsedNumber because 469 // it is an internal type, but implement this nonetheless in case there are legit corner 470 // cases where this is possible 471 @Override public LazilyParsedNumber read(JsonReader in) throws IOException { 472 if (in.peek() == JsonToken.NULL) { 473 in.nextNull(); 474 return null; 475 } 476 return new LazilyParsedNumber(in.nextString()); 477 } 478 479 @Override public void write(JsonWriter out, LazilyParsedNumber value) throws IOException { 480 out.value(value); 481 } 482 }; 483 484 public static final TypeAdapterFactory STRING_FACTORY = newFactory(String.class, STRING); 485 486 public static final TypeAdapter<StringBuilder> STRING_BUILDER = new TypeAdapter<StringBuilder>() { 487 @Override 488 public StringBuilder read(JsonReader in) throws IOException { 489 if (in.peek() == JsonToken.NULL) { 490 in.nextNull(); 491 return null; 492 } 493 return new StringBuilder(in.nextString()); 494 } 495 @Override 496 public void write(JsonWriter out, StringBuilder value) throws IOException { 497 out.value(value == null ? null : value.toString()); 498 } 499 }; 500 501 public static final TypeAdapterFactory STRING_BUILDER_FACTORY = 502 newFactory(StringBuilder.class, STRING_BUILDER); 503 504 public static final TypeAdapter<StringBuffer> STRING_BUFFER = new TypeAdapter<StringBuffer>() { 505 @Override 506 public StringBuffer read(JsonReader in) throws IOException { 507 if (in.peek() == JsonToken.NULL) { 508 in.nextNull(); 509 return null; 510 } 511 return new StringBuffer(in.nextString()); 512 } 513 @Override 514 public void write(JsonWriter out, StringBuffer value) throws IOException { 515 out.value(value == null ? null : value.toString()); 516 } 517 }; 518 519 public static final TypeAdapterFactory STRING_BUFFER_FACTORY = 520 newFactory(StringBuffer.class, STRING_BUFFER); 521 522 public static final TypeAdapter<URL> URL = new TypeAdapter<URL>() { 523 @Override 524 public URL read(JsonReader in) throws IOException { 525 if (in.peek() == JsonToken.NULL) { 526 in.nextNull(); 527 return null; 528 } 529 String nextString = in.nextString(); 530 return "null".equals(nextString) ? null : new URL(nextString); 531 } 532 @Override 533 public void write(JsonWriter out, URL value) throws IOException { 534 out.value(value == null ? null : value.toExternalForm()); 535 } 536 }; 537 538 public static final TypeAdapterFactory URL_FACTORY = newFactory(URL.class, URL); 539 540 public static final TypeAdapter<URI> URI = new TypeAdapter<URI>() { 541 @Override 542 public URI read(JsonReader in) throws IOException { 543 if (in.peek() == JsonToken.NULL) { 544 in.nextNull(); 545 return null; 546 } 547 try { 548 String nextString = in.nextString(); 549 return "null".equals(nextString) ? null : new URI(nextString); 550 } catch (URISyntaxException e) { 551 throw new JsonIOException(e); 552 } 553 } 554 @Override 555 public void write(JsonWriter out, URI value) throws IOException { 556 out.value(value == null ? null : value.toASCIIString()); 557 } 558 }; 559 560 public static final TypeAdapterFactory URI_FACTORY = newFactory(URI.class, URI); 561 562 public static final TypeAdapter<InetAddress> INET_ADDRESS = new TypeAdapter<InetAddress>() { 563 @Override 564 public InetAddress read(JsonReader in) throws IOException { 565 if (in.peek() == JsonToken.NULL) { 566 in.nextNull(); 567 return null; 568 } 569 // regrettably, this should have included both the host name and the host address 570 return InetAddress.getByName(in.nextString()); 571 } 572 @Override 573 public void write(JsonWriter out, InetAddress value) throws IOException { 574 out.value(value == null ? null : value.getHostAddress()); 575 } 576 }; 577 578 public static final TypeAdapterFactory INET_ADDRESS_FACTORY = 579 newTypeHierarchyFactory(InetAddress.class, INET_ADDRESS); 580 581 public static final TypeAdapter<UUID> UUID = new TypeAdapter<UUID>() { 582 @Override 583 public UUID read(JsonReader in) throws IOException { 584 if (in.peek() == JsonToken.NULL) { 585 in.nextNull(); 586 return null; 587 } 588 String s = in.nextString(); 589 try { 590 return java.util.UUID.fromString(s); 591 } catch (IllegalArgumentException e) { 592 throw new JsonSyntaxException("Failed parsing '" + s + "' as UUID; at path " + in.getPreviousPath(), e); 593 } 594 } 595 @Override 596 public void write(JsonWriter out, UUID value) throws IOException { 597 out.value(value == null ? null : value.toString()); 598 } 599 }; 600 601 public static final TypeAdapterFactory UUID_FACTORY = newFactory(UUID.class, UUID); 602 603 public static final TypeAdapter<Currency> CURRENCY = new TypeAdapter<Currency>() { 604 @Override 605 public Currency read(JsonReader in) throws IOException { 606 String s = in.nextString(); 607 try { 608 return Currency.getInstance(s); 609 } catch (IllegalArgumentException e) { 610 throw new JsonSyntaxException("Failed parsing '" + s + "' as Currency; at path " + in.getPreviousPath(), e); 611 } 612 } 613 @Override 614 public void write(JsonWriter out, Currency value) throws IOException { 615 out.value(value.getCurrencyCode()); 616 } 617 }.nullSafe(); 618 public static final TypeAdapterFactory CURRENCY_FACTORY = newFactory(Currency.class, CURRENCY); 619 620 public static final TypeAdapter<Calendar> CALENDAR = new TypeAdapter<Calendar>() { 621 private static final String YEAR = "year"; 622 private static final String MONTH = "month"; 623 private static final String DAY_OF_MONTH = "dayOfMonth"; 624 private static final String HOUR_OF_DAY = "hourOfDay"; 625 private static final String MINUTE = "minute"; 626 private static final String SECOND = "second"; 627 628 @Override 629 public Calendar read(JsonReader in) throws IOException { 630 if (in.peek() == JsonToken.NULL) { 631 in.nextNull(); 632 return null; 633 } 634 in.beginObject(); 635 int year = 0; 636 int month = 0; 637 int dayOfMonth = 0; 638 int hourOfDay = 0; 639 int minute = 0; 640 int second = 0; 641 while (in.peek() != JsonToken.END_OBJECT) { 642 String name = in.nextName(); 643 int value = in.nextInt(); 644 if (YEAR.equals(name)) { 645 year = value; 646 } else if (MONTH.equals(name)) { 647 month = value; 648 } else if (DAY_OF_MONTH.equals(name)) { 649 dayOfMonth = value; 650 } else if (HOUR_OF_DAY.equals(name)) { 651 hourOfDay = value; 652 } else if (MINUTE.equals(name)) { 653 minute = value; 654 } else if (SECOND.equals(name)) { 655 second = value; 656 } 657 } 658 in.endObject(); 659 return new GregorianCalendar(year, month, dayOfMonth, hourOfDay, minute, second); 660 } 661 662 @Override 663 public void write(JsonWriter out, Calendar value) throws IOException { 664 if (value == null) { 665 out.nullValue(); 666 return; 667 } 668 out.beginObject(); 669 out.name(YEAR); 670 out.value(value.get(Calendar.YEAR)); 671 out.name(MONTH); 672 out.value(value.get(Calendar.MONTH)); 673 out.name(DAY_OF_MONTH); 674 out.value(value.get(Calendar.DAY_OF_MONTH)); 675 out.name(HOUR_OF_DAY); 676 out.value(value.get(Calendar.HOUR_OF_DAY)); 677 out.name(MINUTE); 678 out.value(value.get(Calendar.MINUTE)); 679 out.name(SECOND); 680 out.value(value.get(Calendar.SECOND)); 681 out.endObject(); 682 } 683 }; 684 685 public static final TypeAdapterFactory CALENDAR_FACTORY = 686 newFactoryForMultipleTypes(Calendar.class, GregorianCalendar.class, CALENDAR); 687 688 public static final TypeAdapter<Locale> LOCALE = new TypeAdapter<Locale>() { 689 @Override 690 public Locale read(JsonReader in) throws IOException { 691 if (in.peek() == JsonToken.NULL) { 692 in.nextNull(); 693 return null; 694 } 695 String locale = in.nextString(); 696 StringTokenizer tokenizer = new StringTokenizer(locale, "_"); 697 String language = null; 698 String country = null; 699 String variant = null; 700 if (tokenizer.hasMoreElements()) { 701 language = tokenizer.nextToken(); 702 } 703 if (tokenizer.hasMoreElements()) { 704 country = tokenizer.nextToken(); 705 } 706 if (tokenizer.hasMoreElements()) { 707 variant = tokenizer.nextToken(); 708 } 709 if (country == null && variant == null) { 710 return new Locale(language); 711 } else if (variant == null) { 712 return new Locale(language, country); 713 } else { 714 return new Locale(language, country, variant); 715 } 716 } 717 @Override 718 public void write(JsonWriter out, Locale value) throws IOException { 719 out.value(value == null ? null : value.toString()); 720 } 721 }; 722 723 public static final TypeAdapterFactory LOCALE_FACTORY = newFactory(Locale.class, LOCALE); 724 725 public static final TypeAdapter<JsonElement> JSON_ELEMENT = new TypeAdapter<JsonElement>() { 726 /** 727 * Tries to begin reading a JSON array or JSON object, returning {@code null} if 728 * the next element is neither of those. 729 */ 730 private JsonElement tryBeginNesting(JsonReader in, JsonToken peeked) throws IOException { 731 switch (peeked) { 732 case BEGIN_ARRAY: 733 in.beginArray(); 734 return new JsonArray(); 735 case BEGIN_OBJECT: 736 in.beginObject(); 737 return new JsonObject(); 738 default: 739 return null; 740 } 741 } 742 743 /** Reads a {@link JsonElement} which cannot have any nested elements */ 744 private JsonElement readTerminal(JsonReader in, JsonToken peeked) throws IOException { 745 switch (peeked) { 746 case STRING: 747 return new JsonPrimitive(in.nextString()); 748 case NUMBER: 749 String number = in.nextString(); 750 return new JsonPrimitive(new LazilyParsedNumber(number)); 751 case BOOLEAN: 752 return new JsonPrimitive(in.nextBoolean()); 753 case NULL: 754 in.nextNull(); 755 return JsonNull.INSTANCE; 756 default: 757 // When read(JsonReader) is called with JsonReader in invalid state 758 throw new IllegalStateException("Unexpected token: " + peeked); 759 } 760 } 761 762 @Override public JsonElement read(JsonReader in) throws IOException { 763 if (in instanceof JsonTreeReader) { 764 return ((JsonTreeReader) in).nextJsonElement(); 765 } 766 767 // Either JsonArray or JsonObject 768 JsonElement current; 769 JsonToken peeked = in.peek(); 770 771 current = tryBeginNesting(in, peeked); 772 if (current == null) { 773 return readTerminal(in, peeked); 774 } 775 776 Deque<JsonElement> stack = new ArrayDeque<>(); 777 778 while (true) { 779 while (in.hasNext()) { 780 String name = null; 781 // Name is only used for JSON object members 782 if (current instanceof JsonObject) { 783 name = in.nextName(); 784 } 785 786 peeked = in.peek(); 787 JsonElement value = tryBeginNesting(in, peeked); 788 boolean isNesting = value != null; 789 790 if (value == null) { 791 value = readTerminal(in, peeked); 792 } 793 794 if (current instanceof JsonArray) { 795 ((JsonArray) current).add(value); 796 } else { 797 ((JsonObject) current).add(name, value); 798 } 799 800 if (isNesting) { 801 stack.addLast(current); 802 current = value; 803 } 804 } 805 806 // End current element 807 if (current instanceof JsonArray) { 808 in.endArray(); 809 } else { 810 in.endObject(); 811 } 812 813 if (stack.isEmpty()) { 814 return current; 815 } else { 816 // Continue with enclosing element 817 current = stack.removeLast(); 818 } 819 } 820 } 821 822 @Override public void write(JsonWriter out, JsonElement value) throws IOException { 823 if (value == null || value.isJsonNull()) { 824 out.nullValue(); 825 } else if (value.isJsonPrimitive()) { 826 JsonPrimitive primitive = value.getAsJsonPrimitive(); 827 if (primitive.isNumber()) { 828 out.value(primitive.getAsNumber()); 829 } else if (primitive.isBoolean()) { 830 out.value(primitive.getAsBoolean()); 831 } else { 832 out.value(primitive.getAsString()); 833 } 834 835 } else if (value.isJsonArray()) { 836 out.beginArray(); 837 for (JsonElement e : value.getAsJsonArray()) { 838 write(out, e); 839 } 840 out.endArray(); 841 842 } else if (value.isJsonObject()) { 843 out.beginObject(); 844 for (Map.Entry<String, JsonElement> e : value.getAsJsonObject().entrySet()) { 845 out.name(e.getKey()); 846 write(out, e.getValue()); 847 } 848 out.endObject(); 849 850 } else { 851 throw new IllegalArgumentException("Couldn't write " + value.getClass()); 852 } 853 } 854 }; 855 856 public static final TypeAdapterFactory JSON_ELEMENT_FACTORY 857 = newTypeHierarchyFactory(JsonElement.class, JSON_ELEMENT); 858 859 private static final class EnumTypeAdapter<T extends Enum<T>> extends TypeAdapter<T> { 860 private final Map<String, T> nameToConstant = new HashMap<>(); 861 private final Map<String, T> stringToConstant = new HashMap<>(); 862 private final Map<T, String> constantToName = new HashMap<>(); 863 EnumTypeAdapter(final Class<T> classOfT)864 public EnumTypeAdapter(final Class<T> classOfT) { 865 try { 866 // Uses reflection to find enum constants to work around name mismatches for obfuscated classes 867 // Reflection access might throw SecurityException, therefore run this in privileged context; 868 // should be acceptable because this only retrieves enum constants, but does not expose anything else 869 Field[] constantFields = AccessController.doPrivileged(new PrivilegedAction<Field[]>() { 870 @Override public Field[] run() { 871 Field[] fields = classOfT.getDeclaredFields(); 872 ArrayList<Field> constantFieldsList = new ArrayList<>(fields.length); 873 for (Field f : fields) { 874 if (f.isEnumConstant()) { 875 constantFieldsList.add(f); 876 } 877 } 878 879 Field[] constantFields = constantFieldsList.toArray(new Field[0]); 880 AccessibleObject.setAccessible(constantFields, true); 881 return constantFields; 882 } 883 }); 884 for (Field constantField : constantFields) { 885 @SuppressWarnings("unchecked") 886 T constant = (T)(constantField.get(null)); 887 String name = constant.name(); 888 String toStringVal = constant.toString(); 889 890 SerializedName annotation = constantField.getAnnotation(SerializedName.class); 891 if (annotation != null) { 892 name = annotation.value(); 893 for (String alternate : annotation.alternate()) { 894 nameToConstant.put(alternate, constant); 895 } 896 } 897 nameToConstant.put(name, constant); 898 stringToConstant.put(toStringVal, constant); 899 constantToName.put(constant, name); 900 } 901 } catch (IllegalAccessException e) { 902 throw new AssertionError(e); 903 } 904 } read(JsonReader in)905 @Override public T read(JsonReader in) throws IOException { 906 if (in.peek() == JsonToken.NULL) { 907 in.nextNull(); 908 return null; 909 } 910 String key = in.nextString(); 911 T constant = nameToConstant.get(key); 912 return (constant == null) ? stringToConstant.get(key) : constant; 913 } 914 write(JsonWriter out, T value)915 @Override public void write(JsonWriter out, T value) throws IOException { 916 out.value(value == null ? null : constantToName.get(value)); 917 } 918 } 919 920 public static final TypeAdapterFactory ENUM_FACTORY = new TypeAdapterFactory() { 921 @Override public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) { 922 Class<? super T> rawType = typeToken.getRawType(); 923 if (!Enum.class.isAssignableFrom(rawType) || rawType == Enum.class) { 924 return null; 925 } 926 if (!rawType.isEnum()) { 927 rawType = rawType.getSuperclass(); // handle anonymous subclasses 928 } 929 @SuppressWarnings({"rawtypes", "unchecked"}) 930 TypeAdapter<T> adapter = (TypeAdapter<T>) new EnumTypeAdapter(rawType); 931 return adapter; 932 } 933 }; 934 newFactory( final TypeToken<TT> type, final TypeAdapter<TT> typeAdapter)935 public static <TT> TypeAdapterFactory newFactory( 936 final TypeToken<TT> type, final TypeAdapter<TT> typeAdapter) { 937 return new TypeAdapterFactory() { 938 @SuppressWarnings("unchecked") // we use a runtime check to make sure the 'T's equal 939 @Override public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) { 940 return typeToken.equals(type) ? (TypeAdapter<T>) typeAdapter : null; 941 } 942 }; 943 } 944 945 public static <TT> TypeAdapterFactory newFactory( 946 final Class<TT> type, final TypeAdapter<TT> typeAdapter) { 947 return new TypeAdapterFactory() { 948 @SuppressWarnings("unchecked") // we use a runtime check to make sure the 'T's equal 949 @Override public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) { 950 return typeToken.getRawType() == type ? (TypeAdapter<T>) typeAdapter : null; 951 } 952 @Override public String toString() { 953 return "Factory[type=" + type.getName() + ",adapter=" + typeAdapter + "]"; 954 } 955 }; 956 } 957 958 public static <TT> TypeAdapterFactory newFactory( 959 final Class<TT> unboxed, final Class<TT> boxed, final TypeAdapter<? super TT> typeAdapter) { 960 return new TypeAdapterFactory() { 961 @SuppressWarnings("unchecked") // we use a runtime check to make sure the 'T's equal 962 @Override public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) { 963 Class<? super T> rawType = typeToken.getRawType(); 964 return (rawType == unboxed || rawType == boxed) ? (TypeAdapter<T>) typeAdapter : null; 965 } 966 @Override public String toString() { 967 return "Factory[type=" + boxed.getName() 968 + "+" + unboxed.getName() + ",adapter=" + typeAdapter + "]"; 969 } 970 }; 971 } 972 973 public static <TT> TypeAdapterFactory newFactoryForMultipleTypes(final Class<TT> base, 974 final Class<? extends TT> sub, final TypeAdapter<? super TT> typeAdapter) { 975 return new TypeAdapterFactory() { 976 @SuppressWarnings("unchecked") // we use a runtime check to make sure the 'T's equal 977 @Override public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) { 978 Class<? super T> rawType = typeToken.getRawType(); 979 return (rawType == base || rawType == sub) ? (TypeAdapter<T>) typeAdapter : null; 980 } 981 @Override public String toString() { 982 return "Factory[type=" + base.getName() 983 + "+" + sub.getName() + ",adapter=" + typeAdapter + "]"; 984 } 985 }; 986 } 987 988 /** 989 * Returns a factory for all subtypes of {@code typeAdapter}. We do a runtime check to confirm 990 * that the deserialized type matches the type requested. 991 */ 992 public static <T1> TypeAdapterFactory newTypeHierarchyFactory( 993 final Class<T1> clazz, final TypeAdapter<T1> typeAdapter) { 994 return new TypeAdapterFactory() { 995 @SuppressWarnings("unchecked") 996 @Override public <T2> TypeAdapter<T2> create(Gson gson, TypeToken<T2> typeToken) { 997 final Class<? super T2> requestedType = typeToken.getRawType(); 998 if (!clazz.isAssignableFrom(requestedType)) { 999 return null; 1000 } 1001 return (TypeAdapter<T2>) new TypeAdapter<T1>() { 1002 @Override public void write(JsonWriter out, T1 value) throws IOException { 1003 typeAdapter.write(out, value); 1004 } 1005 1006 @Override public T1 read(JsonReader in) throws IOException { 1007 T1 result = typeAdapter.read(in); 1008 if (result != null && !requestedType.isInstance(result)) { 1009 throw new JsonSyntaxException("Expected a " + requestedType.getName() 1010 + " but was " + result.getClass().getName() + "; at path " + in.getPreviousPath()); 1011 } 1012 return result; 1013 } 1014 }; 1015 } 1016 @Override public String toString() { 1017 return "Factory[typeHierarchy=" + clazz.getName() + ",adapter=" + typeAdapter + "]"; 1018 } 1019 }; 1020 } 1021 } 1022