diff --git a/gson/.gitignore b/gson/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..2cbedc2257d6a99b2667b92be12279474aef8e36
--- /dev/null
+++ b/gson/.gitignore
@@ -0,0 +1,6 @@
+/.settings/
+/target/
+/workspace/
+/.classpath
+/.project
+/setting.xml
diff --git a/gson/pom.xml b/gson/pom.xml
new file mode 100644
index 0000000000000000000000000000000000000000..0a879d8d786ee16bfa29190c5c38205fe34fb9fd
--- /dev/null
+++ b/gson/pom.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+	<modelVersion>4.0.0</modelVersion>
+	<parent>
+		<groupId>org.bioimageanalysis.icy</groupId>
+		<artifactId>parent-pom-plugin</artifactId>
+		<version>1.0.4</version>
+	</parent>
+	<artifactId>gson</artifactId>
+	<version>2.8.6</version>
+	<name>Gson library for Icy</name>
+	<description>
+	</description>
+	<dependencies>
+	</dependencies>
+	<repositories>
+		<repository>
+			<id>icy</id>
+			<url>https://icy-nexus.pasteur.fr/repository/Icy/</url>
+		</repository>
+	</repositories>
+</project>
\ No newline at end of file
diff --git a/gson/src/main/java/com/google/gson/DefaultDateTypeAdapter.java b/gson/src/main/java/com/google/gson/DefaultDateTypeAdapter.java
new file mode 100644
index 0000000000000000000000000000000000000000..522963795a3e75cbbb6ec8059d1038bd24183fb5
--- /dev/null
+++ b/gson/src/main/java/com/google/gson/DefaultDateTypeAdapter.java
@@ -0,0 +1,165 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.gson;
+
+import java.io.IOException;
+import java.sql.Timestamp;
+import java.text.DateFormat;
+import java.text.ParseException;
+import java.text.ParsePosition;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import java.util.Locale;
+
+import com.google.gson.internal.JavaVersion;
+import com.google.gson.internal.PreJava9DateFormatProvider;
+import com.google.gson.internal.bind.util.ISO8601Utils;
+import com.google.gson.stream.JsonReader;
+import com.google.gson.stream.JsonToken;
+import com.google.gson.stream.JsonWriter;
+
+/**
+ * This type adapter supports three subclasses of date: Date, Timestamp, and
+ * java.sql.Date.
+ *
+ * @author Inderjeet Singh
+ * @author Joel Leitch
+ */
+final class DefaultDateTypeAdapter extends TypeAdapter<Date> {
+
+  private static final String SIMPLE_NAME = "DefaultDateTypeAdapter";
+
+  private final Class<? extends Date> dateType;
+
+  /**
+   * List of 1 or more different date formats used for de-serialization attempts.
+   * The first of them is used for serialization as well.
+   */
+  private final List<DateFormat> dateFormats = new ArrayList<DateFormat>();
+
+  DefaultDateTypeAdapter(Class<? extends Date> dateType) {
+    this.dateType = verifyDateType(dateType);
+    dateFormats.add(DateFormat.getDateTimeInstance(DateFormat.DEFAULT, DateFormat.DEFAULT, Locale.US));
+    if (!Locale.getDefault().equals(Locale.US)) {
+      dateFormats.add(DateFormat.getDateTimeInstance(DateFormat.DEFAULT, DateFormat.DEFAULT));
+    }
+    if (JavaVersion.isJava9OrLater()) {
+      dateFormats.add(PreJava9DateFormatProvider.getUSDateTimeFormat(DateFormat.DEFAULT, DateFormat.DEFAULT));
+    }
+  }
+
+  DefaultDateTypeAdapter(Class<? extends Date> dateType, String datePattern) {
+    this.dateType = verifyDateType(dateType);
+    dateFormats.add(new SimpleDateFormat(datePattern, Locale.US));
+    if (!Locale.getDefault().equals(Locale.US)) {
+      dateFormats.add(new SimpleDateFormat(datePattern));
+    }
+  }
+
+  DefaultDateTypeAdapter(Class<? extends Date> dateType, int style) {
+    this.dateType = verifyDateType(dateType);
+    dateFormats.add(DateFormat.getDateInstance(style, Locale.US));
+    if (!Locale.getDefault().equals(Locale.US)) {
+      dateFormats.add(DateFormat.getDateInstance(style));
+    }
+    if (JavaVersion.isJava9OrLater()) {
+      dateFormats.add(PreJava9DateFormatProvider.getUSDateFormat(style));
+    }
+  }
+
+  public DefaultDateTypeAdapter(int dateStyle, int timeStyle) {
+    this(Date.class, dateStyle, timeStyle);
+  }
+
+  public DefaultDateTypeAdapter(Class<? extends Date> dateType, int dateStyle, int timeStyle) {
+    this.dateType = verifyDateType(dateType);
+    dateFormats.add(DateFormat.getDateTimeInstance(dateStyle, timeStyle, Locale.US));
+    if (!Locale.getDefault().equals(Locale.US)) {
+      dateFormats.add(DateFormat.getDateTimeInstance(dateStyle, timeStyle));
+    }
+    if (JavaVersion.isJava9OrLater()) {
+      dateFormats.add(PreJava9DateFormatProvider.getUSDateTimeFormat(dateStyle, timeStyle));
+    }
+  }
+
+  private static Class<? extends Date> verifyDateType(Class<? extends Date> dateType) {
+    if ( dateType != Date.class && dateType != java.sql.Date.class && dateType != Timestamp.class ) {
+      throw new IllegalArgumentException("Date type must be one of " + Date.class + ", " + Timestamp.class + ", or " + java.sql.Date.class + " but was " + dateType);
+    }
+    return dateType;
+  }
+
+  // These methods need to be synchronized since JDK DateFormat classes are not thread-safe
+  // See issue 162
+  @Override
+  public void write(JsonWriter out, Date value) throws IOException {
+    if (value == null) {
+      out.nullValue();
+      return;
+    }
+    synchronized(dateFormats) {
+      String dateFormatAsString = dateFormats.get(0).format(value);
+      out.value(dateFormatAsString);
+    }
+  }
+
+  @Override
+  public Date read(JsonReader in) throws IOException {
+    if (in.peek() == JsonToken.NULL) {
+      in.nextNull();
+      return null;
+    }
+    Date date = deserializeToDate(in.nextString());
+    if (dateType == Date.class) {
+      return date;
+    } else if (dateType == Timestamp.class) {
+      return new Timestamp(date.getTime());
+    } else if (dateType == java.sql.Date.class) {
+      return new java.sql.Date(date.getTime());
+    } else {
+      // This must never happen: dateType is guarded in the primary constructor
+      throw new AssertionError();
+    }
+  }
+
+  private Date deserializeToDate(String s) {
+    synchronized (dateFormats) {
+      for (DateFormat dateFormat : dateFormats) {
+        try {
+          return dateFormat.parse(s);
+        } catch (ParseException ignored) {}
+      }
+      try {
+        return ISO8601Utils.parse(s, new ParsePosition(0));
+      } catch (ParseException e) {
+        throw new JsonSyntaxException(s, e);
+      }
+    }
+  }
+
+  @Override
+  public String toString() {
+    DateFormat defaultFormat = dateFormats.get(0);
+    if (defaultFormat instanceof SimpleDateFormat) {
+      return SIMPLE_NAME + '(' + ((SimpleDateFormat) defaultFormat).toPattern() + ')';
+    } else {
+      return SIMPLE_NAME + '(' + defaultFormat.getClass().getSimpleName() + ')';
+    }
+  }
+}
diff --git a/gson/src/main/java/com/google/gson/ExclusionStrategy.java b/gson/src/main/java/com/google/gson/ExclusionStrategy.java
new file mode 100644
index 0000000000000000000000000000000000000000..bc0dc74a0ebd6656dc8db33dab8ce148cfa698ea
--- /dev/null
+++ b/gson/src/main/java/com/google/gson/ExclusionStrategy.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.gson;
+
+/**
+ * A strategy (or policy) definition that is used to decide whether or not a field or top-level
+ * class should be serialized or deserialized as part of the JSON output/input. For serialization,
+ * if the {@link #shouldSkipClass(Class)} method returns true then that class or field type
+ * will not be part of the JSON output. For deserialization, if {@link #shouldSkipClass(Class)}
+ * returns true, then it will not be set as part of the Java object structure.
+ *
+ * <p>The following are a few examples that shows how you can use this exclusion mechanism.
+ *
+ * <p><strong>Exclude fields and objects based on a particular class type:</strong>
+ * <pre class="code">
+ * private static class SpecificClassExclusionStrategy implements ExclusionStrategy {
+ *   private final Class&lt;?&gt; excludedThisClass;
+ *
+ *   public SpecificClassExclusionStrategy(Class&lt;?&gt; excludedThisClass) {
+ *     this.excludedThisClass = excludedThisClass;
+ *   }
+ *
+ *   public boolean shouldSkipClass(Class&lt;?&gt; clazz) {
+ *     return excludedThisClass.equals(clazz);
+ *   }
+ *
+ *   public boolean shouldSkipField(FieldAttributes f) {
+ *     return excludedThisClass.equals(f.getDeclaredClass());
+ *   }
+ * }
+ * </pre>
+ *
+ * <p><strong>Excludes fields and objects based on a particular annotation:</strong>
+ * <pre class="code">
+ * public &#64;interface FooAnnotation {
+ *   // some implementation here
+ * }
+ *
+ * // Excludes any field (or class) that is tagged with an "&#64;FooAnnotation"
+ * private static class FooAnnotationExclusionStrategy implements ExclusionStrategy {
+ *   public boolean shouldSkipClass(Class&lt;?&gt; clazz) {
+ *     return clazz.getAnnotation(FooAnnotation.class) != null;
+ *   }
+ *
+ *   public boolean shouldSkipField(FieldAttributes f) {
+ *     return f.getAnnotation(FooAnnotation.class) != null;
+ *   }
+ * }
+ * </pre>
+ *
+ * <p>Now if you want to configure {@code Gson} to use a user defined exclusion strategy, then
+ * the {@code GsonBuilder} is required. The following is an example of how you can use the
+ * {@code GsonBuilder} to configure Gson to use one of the above sample:
+ * <pre class="code">
+ * ExclusionStrategy excludeStrings = new UserDefinedExclusionStrategy(String.class);
+ * Gson gson = new GsonBuilder()
+ *     .setExclusionStrategies(excludeStrings)
+ *     .create();
+ * </pre>
+ *
+ * <p>For certain model classes, you may only want to serialize a field, but exclude it for
+ * deserialization. To do that, you can write an {@code ExclusionStrategy} as per normal;
+ * however, you would register it with the
+ * {@link GsonBuilder#addDeserializationExclusionStrategy(ExclusionStrategy)} method.
+ * For example:
+ * <pre class="code">
+ * ExclusionStrategy excludeStrings = new UserDefinedExclusionStrategy(String.class);
+ * Gson gson = new GsonBuilder()
+ *     .addDeserializationExclusionStrategy(excludeStrings)
+ *     .create();
+ * </pre>
+ *
+ * @author Inderjeet Singh
+ * @author Joel Leitch
+ *
+ * @see GsonBuilder#setExclusionStrategies(ExclusionStrategy...)
+ * @see GsonBuilder#addDeserializationExclusionStrategy(ExclusionStrategy)
+ * @see GsonBuilder#addSerializationExclusionStrategy(ExclusionStrategy)
+ *
+ * @since 1.4
+ */
+public interface ExclusionStrategy {
+
+  /**
+   * @param f the field object that is under test
+   * @return true if the field should be ignored; otherwise false
+   */
+  public boolean shouldSkipField(FieldAttributes f);
+
+  /**
+   * @param clazz the class object that is under test
+   * @return true if the class should be ignored; otherwise false
+   */
+  public boolean shouldSkipClass(Class<?> clazz);
+}
diff --git a/gson/src/main/java/com/google/gson/FieldAttributes.java b/gson/src/main/java/com/google/gson/FieldAttributes.java
new file mode 100644
index 0000000000000000000000000000000000000000..4ee906a60aed23c6d06aae8e4b0c151ec2f80f42
--- /dev/null
+++ b/gson/src/main/java/com/google/gson/FieldAttributes.java
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2009 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.gson;
+
+import com.google.gson.internal.$Gson$Preconditions;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Field;
+import java.lang.reflect.Type;
+import java.util.Arrays;
+import java.util.Collection;
+
+/**
+ * A data object that stores attributes of a field.
+ *
+ * <p>This class is immutable; therefore, it can be safely shared across threads.
+ *
+ * @author Inderjeet Singh
+ * @author Joel Leitch
+ *
+ * @since 1.4
+ */
+public final class FieldAttributes {
+  private final Field field;
+
+  /**
+   * Constructs a Field Attributes object from the {@code f}.
+   *
+   * @param f the field to pull attributes from
+   */
+  public FieldAttributes(Field f) {
+    $Gson$Preconditions.checkNotNull(f);
+    this.field = f;
+  }
+
+  /**
+   * @return the declaring class that contains this field
+   */
+  public Class<?> getDeclaringClass() {
+    return field.getDeclaringClass();
+  }
+
+  /**
+   * @return the name of the field
+   */
+  public String getName() {
+    return field.getName();
+  }
+
+  /**
+   * <p>For example, assume the following class definition:
+   * <pre class="code">
+   * public class Foo {
+   *   private String bar;
+   *   private List&lt;String&gt; red;
+   * }
+   *
+   * Type listParameterizedType = new TypeToken&lt;List&lt;String&gt;&gt;() {}.getType();
+   * </pre>
+   *
+   * <p>This method would return {@code String.class} for the {@code bar} field and
+   * {@code listParameterizedType} for the {@code red} field.
+   *
+   * @return the specific type declared for this field
+   */
+  public Type getDeclaredType() {
+    return field.getGenericType();
+  }
+
+  /**
+   * Returns the {@code Class} object that was declared for this field.
+   *
+   * <p>For example, assume the following class definition:
+   * <pre class="code">
+   * public class Foo {
+   *   private String bar;
+   *   private List&lt;String&gt; red;
+   * }
+   * </pre>
+   *
+   * <p>This method would return {@code String.class} for the {@code bar} field and
+   * {@code List.class} for the {@code red} field.
+   *
+   * @return the specific class object that was declared for the field
+   */
+  public Class<?> getDeclaredClass() {
+    return field.getType();
+  }
+
+  /**
+   * Return the {@code T} annotation object from this field if it exist; otherwise returns
+   * {@code null}.
+   *
+   * @param annotation the class of the annotation that will be retrieved
+   * @return the annotation instance if it is bound to the field; otherwise {@code null}
+   */
+  public <T extends Annotation> T getAnnotation(Class<T> annotation) {
+    return field.getAnnotation(annotation);
+  }
+
+  /**
+   * Return the annotations that are present on this field.
+   *
+   * @return an array of all the annotations set on the field
+   * @since 1.4
+   */
+  public Collection<Annotation> getAnnotations() {
+    return Arrays.asList(field.getAnnotations());
+  }
+
+  /**
+   * Returns {@code true} if the field is defined with the {@code modifier}.
+   *
+   * <p>This method is meant to be called as:
+   * <pre class="code">
+   * boolean hasPublicModifier = fieldAttribute.hasModifier(java.lang.reflect.Modifier.PUBLIC);
+   * </pre>
+   *
+   * @see java.lang.reflect.Modifier
+   */
+  public boolean hasModifier(int modifier) {
+    return (field.getModifiers() & modifier) != 0;
+  }
+
+  /**
+   * Returns the value of the field represented by this {@code Field}, on
+   * the specified object. The value is automatically wrapped in an
+   * object if it has a primitive type.
+   *
+   * @return the value of the represented field in object
+   * {@code obj}; primitive values are wrapped in an appropriate
+   * object before being returned
+   * @throws IllegalAccessException
+   * @throws IllegalArgumentException
+   */
+  Object get(Object instance) throws IllegalAccessException {
+    return field.get(instance);
+  }
+
+  /**
+   * This is exposed internally only for the removing synthetic fields from the JSON output.
+   *
+   * @return true if the field is synthetic; otherwise false
+   */
+  boolean isSynthetic() {
+    return field.isSynthetic();
+  }
+}
diff --git a/gson/src/main/java/com/google/gson/FieldNamingPolicy.java b/gson/src/main/java/com/google/gson/FieldNamingPolicy.java
new file mode 100644
index 0000000000000000000000000000000000000000..cddfcdfdf99e05ed397d3850b917f648f3fe6be9
--- /dev/null
+++ b/gson/src/main/java/com/google/gson/FieldNamingPolicy.java
@@ -0,0 +1,178 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.gson;
+
+import java.lang.reflect.Field;
+import java.util.Locale;
+
+/**
+ * An enumeration that defines a few standard naming conventions for JSON field names.
+ * This enumeration should be used in conjunction with {@link com.google.gson.GsonBuilder}
+ * to configure a {@link com.google.gson.Gson} instance to properly translate Java field
+ * names into the desired JSON field names.
+ *
+ * @author Inderjeet Singh
+ * @author Joel Leitch
+ */
+public enum FieldNamingPolicy implements FieldNamingStrategy {
+
+  /**
+   * Using this naming policy with Gson will ensure that the field name is
+   * unchanged.
+   */
+  IDENTITY() {
+    @Override public String translateName(Field f) {
+      return f.getName();
+    }
+  },
+
+  /**
+   * Using this naming policy with Gson will ensure that the first "letter" of the Java
+   * field name is capitalized when serialized to its JSON form.
+   *
+   * <p>Here's a few examples of the form "Java Field Name" ---&#62; "JSON Field Name":</p>
+   * <ul>
+   *   <li>someFieldName ---&#62; SomeFieldName</li>
+   *   <li>_someFieldName ---&#62; _SomeFieldName</li>
+   * </ul>
+   */
+  UPPER_CAMEL_CASE() {
+    @Override public String translateName(Field f) {
+      return upperCaseFirstLetter(f.getName());
+    }
+  },
+
+  /**
+   * Using this naming policy with Gson will ensure that the first "letter" of the Java
+   * field name is capitalized when serialized to its JSON form and the words will be
+   * separated by a space.
+   *
+   * <p>Here's a few examples of the form "Java Field Name" ---&#62; "JSON Field Name":</p>
+   * <ul>
+   *   <li>someFieldName ---&#62; Some Field Name</li>
+   *   <li>_someFieldName ---&#62; _Some Field Name</li>
+   * </ul>
+   *
+   * @since 1.4
+   */
+  UPPER_CAMEL_CASE_WITH_SPACES() {
+    @Override public String translateName(Field f) {
+      return upperCaseFirstLetter(separateCamelCase(f.getName(), " "));
+    }
+  },
+
+  /**
+   * Using this naming policy with Gson will modify the Java Field name from its camel cased
+   * form to a lower case field name where each word is separated by an underscore (_).
+   *
+   * <p>Here's a few examples of the form "Java Field Name" ---&#62; "JSON Field Name":</p>
+   * <ul>
+   *   <li>someFieldName ---&#62; some_field_name</li>
+   *   <li>_someFieldName ---&#62; _some_field_name</li>
+   *   <li>aStringField ---&#62; a_string_field</li>
+   *   <li>aURL ---&#62; a_u_r_l</li>
+   * </ul>
+   */
+  LOWER_CASE_WITH_UNDERSCORES() {
+    @Override public String translateName(Field f) {
+      return separateCamelCase(f.getName(), "_").toLowerCase(Locale.ENGLISH);
+    }
+  },
+
+  /**
+   * Using this naming policy with Gson will modify the Java Field name from its camel cased
+   * form to a lower case field name where each word is separated by a dash (-).
+   *
+   * <p>Here's a few examples of the form "Java Field Name" ---&#62; "JSON Field Name":</p>
+   * <ul>
+   *   <li>someFieldName ---&#62; some-field-name</li>
+   *   <li>_someFieldName ---&#62; _some-field-name</li>
+   *   <li>aStringField ---&#62; a-string-field</li>
+   *   <li>aURL ---&#62; a-u-r-l</li>
+   * </ul>
+   * Using dashes in JavaScript is not recommended since dash is also used for a minus sign in
+   * expressions. This requires that a field named with dashes is always accessed as a quoted
+   * property like {@code myobject['my-field']}. Accessing it as an object field
+   * {@code myobject.my-field} will result in an unintended javascript expression.
+   * @since 1.4
+   */
+  LOWER_CASE_WITH_DASHES() {
+    @Override public String translateName(Field f) {
+      return separateCamelCase(f.getName(), "-").toLowerCase(Locale.ENGLISH);
+    }
+  },
+
+  /**
+   * Using this naming policy with Gson will modify the Java Field name from its camel cased
+   * form to a lower case field name where each word is separated by a dot (.).
+   *
+   * <p>Here's a few examples of the form "Java Field Name" ---&#62; "JSON Field Name":</p>
+   * <ul>
+   *   <li>someFieldName ---&#62; some.field.name</li>
+   *   <li>_someFieldName ---&#62; _some.field.name</li>
+   *   <li>aStringField ---&#62; a.string.field</li>
+   *   <li>aURL ---&#62; a.u.r.l</li>
+   * </ul>
+   * Using dots in JavaScript is not recommended since dot is also used for a member sign in
+   * expressions. This requires that a field named with dots is always accessed as a quoted
+   * property like {@code myobject['my.field']}. Accessing it as an object field
+   * {@code myobject.my.field} will result in an unintended javascript expression.
+   * @since 2.8
+   */
+  LOWER_CASE_WITH_DOTS() {
+    @Override public String translateName(Field f) {
+      return separateCamelCase(f.getName(), ".").toLowerCase(Locale.ENGLISH);
+    }
+  };
+
+  /**
+   * Converts the field name that uses camel-case define word separation into
+   * separate words that are separated by the provided {@code separatorString}.
+   */
+  static String separateCamelCase(String name, String separator) {
+    StringBuilder translation = new StringBuilder();
+    for (int i = 0, length = name.length(); i < length; i++) {
+      char character = name.charAt(i);
+      if (Character.isUpperCase(character) && translation.length() != 0) {
+        translation.append(separator);
+      }
+      translation.append(character);
+    }
+    return translation.toString();
+  }
+
+  /**
+   * Ensures the JSON field names begins with an upper case letter.
+   */
+  static String upperCaseFirstLetter(String name) {
+    int firstLetterIndex = 0;
+    int limit = name.length() - 1;
+    for(; !Character.isLetter(name.charAt(firstLetterIndex)) && firstLetterIndex < limit; ++firstLetterIndex);
+
+    char firstLetter = name.charAt(firstLetterIndex);
+    if(Character.isUpperCase(firstLetter)) { //The letter is already uppercased, return the original
+      return name;
+    }
+
+    char uppercased = Character.toUpperCase(firstLetter);
+    if(firstLetterIndex == 0) { //First character in the string is the first letter, saves 1 substring
+      return uppercased + name.substring(1);
+    }
+
+    return name.substring(0, firstLetterIndex) + uppercased + name.substring(firstLetterIndex + 1);
+  }
+}
diff --git a/gson/src/main/java/com/google/gson/FieldNamingStrategy.java b/gson/src/main/java/com/google/gson/FieldNamingStrategy.java
new file mode 100644
index 0000000000000000000000000000000000000000..f2f7c489ace000de7c2a28865c4d00aa395b24c2
--- /dev/null
+++ b/gson/src/main/java/com/google/gson/FieldNamingStrategy.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.gson;
+
+import java.lang.reflect.Field;
+
+/**
+ * A mechanism for providing custom field naming in Gson. This allows the client code to translate
+ * field names into a particular convention that is not supported as a normal Java field
+ * declaration rules. For example, Java does not support "-" characters in a field name.
+ *
+ * @author Inderjeet Singh
+ * @author Joel Leitch
+ * @since 1.3
+ */
+public interface FieldNamingStrategy {
+
+  /**
+   * Translates the field name into its JSON field name representation.
+   *
+   * @param f the field object that we are translating
+   * @return the translated field name.
+   * @since 1.3
+   */
+  public String translateName(Field f);
+}
diff --git a/gson/src/main/java/com/google/gson/Gson.java b/gson/src/main/java/com/google/gson/Gson.java
new file mode 100644
index 0000000000000000000000000000000000000000..a2cf2f68a159cb4cf7a00916257b4088dafc9d27
--- /dev/null
+++ b/gson/src/main/java/com/google/gson/Gson.java
@@ -0,0 +1,1040 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.gson;
+
+import java.io.EOFException;
+import java.io.IOException;
+import java.io.Reader;
+import java.io.StringReader;
+import java.io.StringWriter;
+import java.io.Writer;
+import java.lang.reflect.Type;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.text.DateFormat;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.concurrent.atomic.AtomicLongArray;
+
+import com.google.gson.internal.ConstructorConstructor;
+import com.google.gson.internal.Excluder;
+import com.google.gson.internal.GsonBuildConfig;
+import com.google.gson.internal.Primitives;
+import com.google.gson.internal.Streams;
+import com.google.gson.internal.bind.ArrayTypeAdapter;
+import com.google.gson.internal.bind.CollectionTypeAdapterFactory;
+import com.google.gson.internal.bind.DateTypeAdapter;
+import com.google.gson.internal.bind.JsonAdapterAnnotationTypeAdapterFactory;
+import com.google.gson.internal.bind.JsonTreeReader;
+import com.google.gson.internal.bind.JsonTreeWriter;
+import com.google.gson.internal.bind.MapTypeAdapterFactory;
+import com.google.gson.internal.bind.ObjectTypeAdapter;
+import com.google.gson.internal.bind.ReflectiveTypeAdapterFactory;
+import com.google.gson.internal.bind.SqlDateTypeAdapter;
+import com.google.gson.internal.bind.TimeTypeAdapter;
+import com.google.gson.internal.bind.TypeAdapters;
+import com.google.gson.reflect.TypeToken;
+import com.google.gson.stream.JsonReader;
+import com.google.gson.stream.JsonToken;
+import com.google.gson.stream.JsonWriter;
+import com.google.gson.stream.MalformedJsonException;
+
+/**
+ * This is the main class for using Gson. Gson is typically used by first constructing a
+ * Gson instance and then invoking {@link #toJson(Object)} or {@link #fromJson(String, Class)}
+ * methods on it. Gson instances are Thread-safe so you can reuse them freely across multiple
+ * threads.
+ *
+ * <p>You can create a Gson instance by invoking {@code new Gson()} if the default configuration
+ * is all you need. You can also use {@link GsonBuilder} to build a Gson instance with various
+ * configuration options such as versioning support, pretty printing, custom
+ * {@link JsonSerializer}s, {@link JsonDeserializer}s, and {@link InstanceCreator}s.</p>
+ *
+ * <p>Here is an example of how Gson is used for a simple Class:
+ *
+ * <pre>
+ * Gson gson = new Gson(); // Or use new GsonBuilder().create();
+ * MyType target = new MyType();
+ * String json = gson.toJson(target); // serializes target to Json
+ * MyType target2 = gson.fromJson(json, MyType.class); // deserializes json into target2
+ * </pre>
+ *
+ * <p>If the object that your are serializing/deserializing is a {@code ParameterizedType}
+ * (i.e. contains at least one type parameter and may be an array) then you must use the
+ * {@link #toJson(Object, Type)} or {@link #fromJson(String, Type)} method. Here is an
+ * example for serializing and deserializing a {@code ParameterizedType}:
+ *
+ * <pre>
+ * Type listType = new TypeToken&lt;List&lt;String&gt;&gt;() {}.getType();
+ * List&lt;String&gt; target = new LinkedList&lt;String&gt;();
+ * target.add("blah");
+ *
+ * Gson gson = new Gson();
+ * String json = gson.toJson(target, listType);
+ * List&lt;String&gt; target2 = gson.fromJson(json, listType);
+ * </pre>
+ *
+ * <p>See the <a href="https://sites.google.com/site/gson/gson-user-guide">Gson User Guide</a>
+ * for a more complete set of examples.</p>
+ *
+ * @see com.google.gson.reflect.TypeToken
+ *
+ * @author Inderjeet Singh
+ * @author Joel Leitch
+ * @author Jesse Wilson
+ */
+public final class Gson {
+  static final boolean DEFAULT_JSON_NON_EXECUTABLE = false;
+  static final boolean DEFAULT_LENIENT = false;
+  static final boolean DEFAULT_PRETTY_PRINT = false;
+  static final boolean DEFAULT_ESCAPE_HTML = true;
+  static final boolean DEFAULT_SERIALIZE_NULLS = false;
+  static final boolean DEFAULT_COMPLEX_MAP_KEYS = false;
+  static final boolean DEFAULT_SPECIALIZE_FLOAT_VALUES = false;
+
+  private static final TypeToken<?> NULL_KEY_SURROGATE = TypeToken.get(Object.class);
+  private static final String JSON_NON_EXECUTABLE_PREFIX = ")]}'\n";
+
+  /**
+   * This thread local guards against reentrant calls to getAdapter(). In
+   * certain object graphs, creating an adapter for a type may recursively
+   * require an adapter for the same type! Without intervention, the recursive
+   * lookup would stack overflow. We cheat by returning a proxy type adapter.
+   * The proxy is wired up once the initial adapter has been created.
+   */
+  private final ThreadLocal<Map<TypeToken<?>, FutureTypeAdapter<?>>> calls
+      = new ThreadLocal<Map<TypeToken<?>, FutureTypeAdapter<?>>>();
+
+  private final Map<TypeToken<?>, TypeAdapter<?>> typeTokenCache = new ConcurrentHashMap<TypeToken<?>, TypeAdapter<?>>();
+
+  private final ConstructorConstructor constructorConstructor;
+  private final JsonAdapterAnnotationTypeAdapterFactory jsonAdapterFactory;
+
+  final List<TypeAdapterFactory> factories;
+
+  final Excluder excluder;
+  final FieldNamingStrategy fieldNamingStrategy;
+  final Map<Type, InstanceCreator<?>> instanceCreators;
+  final boolean serializeNulls;
+  final boolean complexMapKeySerialization;
+  final boolean generateNonExecutableJson;
+  final boolean htmlSafe;
+  final boolean prettyPrinting;
+  final boolean lenient;
+  final boolean serializeSpecialFloatingPointValues;
+  final String datePattern;
+  final int dateStyle;
+  final int timeStyle;
+  final LongSerializationPolicy longSerializationPolicy;
+  final List<TypeAdapterFactory> builderFactories;
+  final List<TypeAdapterFactory> builderHierarchyFactories;
+
+  /**
+   * Constructs a Gson object with default configuration. The default configuration has the
+   * following settings:
+   * <ul>
+   *   <li>The JSON generated by <code>toJson</code> methods is in compact representation. This
+   *   means that all the unneeded white-space is removed. You can change this behavior with
+   *   {@link GsonBuilder#setPrettyPrinting()}. </li>
+   *   <li>The generated JSON omits all the fields that are null. Note that nulls in arrays are
+   *   kept as is since an array is an ordered list. Moreover, if a field is not null, but its
+   *   generated JSON is empty, the field is kept. You can configure Gson to serialize null values
+   *   by setting {@link GsonBuilder#serializeNulls()}.</li>
+   *   <li>Gson provides default serialization and deserialization for Enums, {@link Map},
+   *   {@link java.net.URL}, {@link java.net.URI}, {@link java.util.Locale}, {@link java.util.Date},
+   *   {@link java.math.BigDecimal}, and {@link java.math.BigInteger} classes. If you would prefer
+   *   to change the default representation, you can do so by registering a type adapter through
+   *   {@link GsonBuilder#registerTypeAdapter(Type, Object)}. </li>
+   *   <li>The default Date format is same as {@link java.text.DateFormat#DEFAULT}. This format
+   *   ignores the millisecond portion of the date during serialization. You can change
+   *   this by invoking {@link GsonBuilder#setDateFormat(int)} or
+   *   {@link GsonBuilder#setDateFormat(String)}. </li>
+   *   <li>By default, Gson ignores the {@link com.google.gson.annotations.Expose} annotation.
+   *   You can enable Gson to serialize/deserialize only those fields marked with this annotation
+   *   through {@link GsonBuilder#excludeFieldsWithoutExposeAnnotation()}. </li>
+   *   <li>By default, Gson ignores the {@link com.google.gson.annotations.Since} annotation. You
+   *   can enable Gson to use this annotation through {@link GsonBuilder#setVersion(double)}.</li>
+   *   <li>The default field naming policy for the output Json is same as in Java. So, a Java class
+   *   field <code>versionNumber</code> will be output as <code>&quot;versionNumber&quot;</code> in
+   *   Json. The same rules are applied for mapping incoming Json to the Java classes. You can
+   *   change this policy through {@link GsonBuilder#setFieldNamingPolicy(FieldNamingPolicy)}.</li>
+   *   <li>By default, Gson excludes <code>transient</code> or <code>static</code> fields from
+   *   consideration for serialization and deserialization. You can change this behavior through
+   *   {@link GsonBuilder#excludeFieldsWithModifiers(int...)}.</li>
+   * </ul>
+   */
+  public Gson() {
+    this(Excluder.DEFAULT, FieldNamingPolicy.IDENTITY,
+        Collections.<Type, InstanceCreator<?>>emptyMap(), DEFAULT_SERIALIZE_NULLS,
+        DEFAULT_COMPLEX_MAP_KEYS, DEFAULT_JSON_NON_EXECUTABLE, DEFAULT_ESCAPE_HTML,
+        DEFAULT_PRETTY_PRINT, DEFAULT_LENIENT, DEFAULT_SPECIALIZE_FLOAT_VALUES,
+        LongSerializationPolicy.DEFAULT, null, DateFormat.DEFAULT, DateFormat.DEFAULT,
+        Collections.<TypeAdapterFactory>emptyList(), Collections.<TypeAdapterFactory>emptyList(),
+        Collections.<TypeAdapterFactory>emptyList());
+  }
+
+  Gson(Excluder excluder, FieldNamingStrategy fieldNamingStrategy,
+      Map<Type, InstanceCreator<?>> instanceCreators, boolean serializeNulls,
+      boolean complexMapKeySerialization, boolean generateNonExecutableGson, boolean htmlSafe,
+      boolean prettyPrinting, boolean lenient, boolean serializeSpecialFloatingPointValues,
+      LongSerializationPolicy longSerializationPolicy, String datePattern, int dateStyle,
+      int timeStyle, List<TypeAdapterFactory> builderFactories,
+      List<TypeAdapterFactory> builderHierarchyFactories,
+      List<TypeAdapterFactory> factoriesToBeAdded) {
+    this.excluder = excluder;
+    this.fieldNamingStrategy = fieldNamingStrategy;
+    this.instanceCreators = instanceCreators;
+    this.constructorConstructor = new ConstructorConstructor(instanceCreators);
+    this.serializeNulls = serializeNulls;
+    this.complexMapKeySerialization = complexMapKeySerialization;
+    this.generateNonExecutableJson = generateNonExecutableGson;
+    this.htmlSafe = htmlSafe;
+    this.prettyPrinting = prettyPrinting;
+    this.lenient = lenient;
+    this.serializeSpecialFloatingPointValues = serializeSpecialFloatingPointValues;
+    this.longSerializationPolicy = longSerializationPolicy;
+    this.datePattern = datePattern;
+    this.dateStyle = dateStyle;
+    this.timeStyle = timeStyle;
+    this.builderFactories = builderFactories;
+    this.builderHierarchyFactories = builderHierarchyFactories;
+
+    List<TypeAdapterFactory> factories = new ArrayList<TypeAdapterFactory>();
+
+    // built-in type adapters that cannot be overridden
+    factories.add(TypeAdapters.JSON_ELEMENT_FACTORY);
+    factories.add(ObjectTypeAdapter.FACTORY);
+
+    // the excluder must precede all adapters that handle user-defined types
+    factories.add(excluder);
+
+    // users' type adapters
+    factories.addAll(factoriesToBeAdded);
+
+    // type adapters for basic platform types
+    factories.add(TypeAdapters.STRING_FACTORY);
+    factories.add(TypeAdapters.INTEGER_FACTORY);
+    factories.add(TypeAdapters.BOOLEAN_FACTORY);
+    factories.add(TypeAdapters.BYTE_FACTORY);
+    factories.add(TypeAdapters.SHORT_FACTORY);
+    TypeAdapter<Number> longAdapter = longAdapter(longSerializationPolicy);
+    factories.add(TypeAdapters.newFactory(long.class, Long.class, longAdapter));
+    factories.add(TypeAdapters.newFactory(double.class, Double.class,
+            doubleAdapter(serializeSpecialFloatingPointValues)));
+    factories.add(TypeAdapters.newFactory(float.class, Float.class,
+            floatAdapter(serializeSpecialFloatingPointValues)));
+    factories.add(TypeAdapters.NUMBER_FACTORY);
+    factories.add(TypeAdapters.ATOMIC_INTEGER_FACTORY);
+    factories.add(TypeAdapters.ATOMIC_BOOLEAN_FACTORY);
+    factories.add(TypeAdapters.newFactory(AtomicLong.class, atomicLongAdapter(longAdapter)));
+    factories.add(TypeAdapters.newFactory(AtomicLongArray.class, atomicLongArrayAdapter(longAdapter)));
+    factories.add(TypeAdapters.ATOMIC_INTEGER_ARRAY_FACTORY);
+    factories.add(TypeAdapters.CHARACTER_FACTORY);
+    factories.add(TypeAdapters.STRING_BUILDER_FACTORY);
+    factories.add(TypeAdapters.STRING_BUFFER_FACTORY);
+    factories.add(TypeAdapters.newFactory(BigDecimal.class, TypeAdapters.BIG_DECIMAL));
+    factories.add(TypeAdapters.newFactory(BigInteger.class, TypeAdapters.BIG_INTEGER));
+    factories.add(TypeAdapters.URL_FACTORY);
+    factories.add(TypeAdapters.URI_FACTORY);
+    factories.add(TypeAdapters.UUID_FACTORY);
+    factories.add(TypeAdapters.CURRENCY_FACTORY);
+    factories.add(TypeAdapters.LOCALE_FACTORY);
+    factories.add(TypeAdapters.INET_ADDRESS_FACTORY);
+    factories.add(TypeAdapters.BIT_SET_FACTORY);
+    factories.add(DateTypeAdapter.FACTORY);
+    factories.add(TypeAdapters.CALENDAR_FACTORY);
+    factories.add(TimeTypeAdapter.FACTORY);
+    factories.add(SqlDateTypeAdapter.FACTORY);
+    factories.add(TypeAdapters.TIMESTAMP_FACTORY);
+    factories.add(ArrayTypeAdapter.FACTORY);
+    factories.add(TypeAdapters.CLASS_FACTORY);
+
+    // type adapters for composite and user-defined types
+    factories.add(new CollectionTypeAdapterFactory(constructorConstructor));
+    factories.add(new MapTypeAdapterFactory(constructorConstructor, complexMapKeySerialization));
+    this.jsonAdapterFactory = new JsonAdapterAnnotationTypeAdapterFactory(constructorConstructor);
+    factories.add(jsonAdapterFactory);
+    factories.add(TypeAdapters.ENUM_FACTORY);
+    factories.add(new ReflectiveTypeAdapterFactory(
+        constructorConstructor, fieldNamingStrategy, excluder, jsonAdapterFactory));
+
+    this.factories = Collections.unmodifiableList(factories);
+  }
+
+  /**
+   * Returns a new GsonBuilder containing all custom factories and configuration used by the current
+   * instance.
+   *
+   * @return a GsonBuilder instance.
+   */
+  public GsonBuilder newBuilder() {
+    return new GsonBuilder(this);
+  }
+
+  public Excluder excluder() {
+    return excluder;
+  }
+
+  public FieldNamingStrategy fieldNamingStrategy() {
+    return fieldNamingStrategy;
+  }
+
+  public boolean serializeNulls() {
+    return serializeNulls;
+  }
+
+  public boolean htmlSafe() {
+    return htmlSafe;
+  }
+
+  private TypeAdapter<Number> doubleAdapter(boolean serializeSpecialFloatingPointValues) {
+    if (serializeSpecialFloatingPointValues) {
+      return TypeAdapters.DOUBLE;
+    }
+    return new TypeAdapter<Number>() {
+      @Override public Double read(JsonReader in) throws IOException {
+        if (in.peek() == JsonToken.NULL) {
+          in.nextNull();
+          return null;
+        }
+        return in.nextDouble();
+      }
+      @Override public void write(JsonWriter out, Number value) throws IOException {
+        if (value == null) {
+          out.nullValue();
+          return;
+        }
+        double doubleValue = value.doubleValue();
+        checkValidFloatingPoint(doubleValue);
+        out.value(value);
+      }
+    };
+  }
+
+  private TypeAdapter<Number> floatAdapter(boolean serializeSpecialFloatingPointValues) {
+    if (serializeSpecialFloatingPointValues) {
+      return TypeAdapters.FLOAT;
+    }
+    return new TypeAdapter<Number>() {
+      @Override public Float read(JsonReader in) throws IOException {
+        if (in.peek() == JsonToken.NULL) {
+          in.nextNull();
+          return null;
+        }
+        return (float) in.nextDouble();
+      }
+      @Override public void write(JsonWriter out, Number value) throws IOException {
+        if (value == null) {
+          out.nullValue();
+          return;
+        }
+        float floatValue = value.floatValue();
+        checkValidFloatingPoint(floatValue);
+        out.value(value);
+      }
+    };
+  }
+
+  static void checkValidFloatingPoint(double value) {
+    if (Double.isNaN(value) || Double.isInfinite(value)) {
+      throw new IllegalArgumentException(value
+          + " is not a valid double value as per JSON specification. To override this"
+          + " behavior, use GsonBuilder.serializeSpecialFloatingPointValues() method.");
+    }
+  }
+
+  private static TypeAdapter<Number> longAdapter(LongSerializationPolicy longSerializationPolicy) {
+    if (longSerializationPolicy == LongSerializationPolicy.DEFAULT) {
+      return TypeAdapters.LONG;
+    }
+    return new TypeAdapter<Number>() {
+      @Override public Number read(JsonReader in) throws IOException {
+        if (in.peek() == JsonToken.NULL) {
+          in.nextNull();
+          return null;
+        }
+        return in.nextLong();
+      }
+      @Override public void write(JsonWriter out, Number value) throws IOException {
+        if (value == null) {
+          out.nullValue();
+          return;
+        }
+        out.value(value.toString());
+      }
+    };
+  }
+
+  private static TypeAdapter<AtomicLong> atomicLongAdapter(final TypeAdapter<Number> longAdapter) {
+    return new TypeAdapter<AtomicLong>() {
+      @Override public void write(JsonWriter out, AtomicLong value) throws IOException {
+        longAdapter.write(out, value.get());
+      }
+      @Override public AtomicLong read(JsonReader in) throws IOException {
+        Number value = longAdapter.read(in);
+        return new AtomicLong(value.longValue());
+      }
+    }.nullSafe();
+  }
+
+  private static TypeAdapter<AtomicLongArray> atomicLongArrayAdapter(final TypeAdapter<Number> longAdapter) {
+    return new TypeAdapter<AtomicLongArray>() {
+      @Override public void write(JsonWriter out, AtomicLongArray value) throws IOException {
+        out.beginArray();
+        for (int i = 0, length = value.length(); i < length; i++) {
+          longAdapter.write(out, value.get(i));
+        }
+        out.endArray();
+      }
+      @Override public AtomicLongArray read(JsonReader in) throws IOException {
+        List<Long> list = new ArrayList<Long>();
+        in.beginArray();
+        while (in.hasNext()) {
+            long value = longAdapter.read(in).longValue();
+            list.add(value);
+        }
+        in.endArray();
+        int length = list.size();
+        AtomicLongArray array = new AtomicLongArray(length);
+        for (int i = 0; i < length; ++i) {
+          array.set(i, list.get(i));
+        }
+        return array;
+      }
+    }.nullSafe();
+  }
+
+  /**
+   * Returns the type adapter for {@code} type.
+   *
+   * @throws IllegalArgumentException if this GSON cannot serialize and
+   *     deserialize {@code type}.
+   */
+  @SuppressWarnings("unchecked")
+  public <T> TypeAdapter<T> getAdapter(TypeToken<T> type) {
+    TypeAdapter<?> cached = typeTokenCache.get(type == null ? NULL_KEY_SURROGATE : type);
+    if (cached != null) {
+      return (TypeAdapter<T>) cached;
+    }
+
+    Map<TypeToken<?>, FutureTypeAdapter<?>> threadCalls = calls.get();
+    boolean requiresThreadLocalCleanup = false;
+    if (threadCalls == null) {
+      threadCalls = new HashMap<TypeToken<?>, FutureTypeAdapter<?>>();
+      calls.set(threadCalls);
+      requiresThreadLocalCleanup = true;
+    }
+
+    // the key and value type parameters always agree
+    FutureTypeAdapter<T> ongoingCall = (FutureTypeAdapter<T>) threadCalls.get(type);
+    if (ongoingCall != null) {
+      return ongoingCall;
+    }
+
+    try {
+      FutureTypeAdapter<T> call = new FutureTypeAdapter<T>();
+      threadCalls.put(type, call);
+
+      for (TypeAdapterFactory factory : factories) {
+        TypeAdapter<T> candidate = factory.create(this, type);
+        if (candidate != null) {
+          call.setDelegate(candidate);
+          typeTokenCache.put(type, candidate);
+          return candidate;
+        }
+      }
+      throw new IllegalArgumentException("GSON (" + GsonBuildConfig.VERSION + ") cannot handle " + type);
+    } finally {
+      threadCalls.remove(type);
+
+      if (requiresThreadLocalCleanup) {
+        calls.remove();
+      }
+    }
+  }
+
+  /**
+   * This method is used to get an alternate type adapter for the specified type. This is used
+   * to access a type adapter that is overridden by a {@link TypeAdapterFactory} that you
+   * may have registered. This features is typically used when you want to register a type
+   * adapter that does a little bit of work but then delegates further processing to the Gson
+   * default type adapter. Here is an example:
+   * <p>Let's say we want to write a type adapter that counts the number of objects being read
+   *  from or written to JSON. We can achieve this by writing a type adapter factory that uses
+   *  the <code>getDelegateAdapter</code> method:
+   *  <pre> {@code
+   *  class StatsTypeAdapterFactory implements TypeAdapterFactory {
+   *    public int numReads = 0;
+   *    public int numWrites = 0;
+   *    public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
+   *      final TypeAdapter<T> delegate = gson.getDelegateAdapter(this, type);
+   *      return new TypeAdapter<T>() {
+   *        public void write(JsonWriter out, T value) throws IOException {
+   *          ++numWrites;
+   *          delegate.write(out, value);
+   *        }
+   *        public T read(JsonReader in) throws IOException {
+   *          ++numReads;
+   *          return delegate.read(in);
+   *        }
+   *      };
+   *    }
+   *  }
+   *  } </pre>
+   *  This factory can now be used like this:
+   *  <pre> {@code
+   *  StatsTypeAdapterFactory stats = new StatsTypeAdapterFactory();
+   *  Gson gson = new GsonBuilder().registerTypeAdapterFactory(stats).create();
+   *  // Call gson.toJson() and fromJson methods on objects
+   *  System.out.println("Num JSON reads" + stats.numReads);
+   *  System.out.println("Num JSON writes" + stats.numWrites);
+   *  }</pre>
+   *  Note that this call will skip all factories registered before {@code skipPast}. In case of
+   *  multiple TypeAdapterFactories registered it is up to the caller of this function to insure
+   *  that the order of registration does not prevent this method from reaching a factory they
+   *  would expect to reply from this call.
+   *  Note that since you can not override type adapter factories for String and Java primitive
+   *  types, our stats factory will not count the number of String or primitives that will be
+   *  read or written.
+   * @param skipPast The type adapter factory that needs to be skipped while searching for
+   *   a matching type adapter. In most cases, you should just pass <i>this</i> (the type adapter
+   *   factory from where {@link #getDelegateAdapter} method is being invoked).
+   * @param type Type for which the delegate adapter is being searched for.
+   *
+   * @since 2.2
+   */
+  public <T> TypeAdapter<T> getDelegateAdapter(TypeAdapterFactory skipPast, TypeToken<T> type) {
+    // Hack. If the skipPast factory isn't registered, assume the factory is being requested via
+    // our @JsonAdapter annotation.
+    if (!factories.contains(skipPast)) {
+      skipPast = jsonAdapterFactory;
+    }
+
+    boolean skipPastFound = false;
+    for (TypeAdapterFactory factory : factories) {
+      if (!skipPastFound) {
+        if (factory == skipPast) {
+          skipPastFound = true;
+        }
+        continue;
+      }
+
+      TypeAdapter<T> candidate = factory.create(this, type);
+      if (candidate != null) {
+        return candidate;
+      }
+    }
+    throw new IllegalArgumentException("GSON cannot serialize " + type);
+  }
+
+  /**
+   * Returns the type adapter for {@code} type.
+   *
+   * @throws IllegalArgumentException if this GSON cannot serialize and
+   *     deserialize {@code type}.
+   */
+  public <T> TypeAdapter<T> getAdapter(Class<T> type) {
+    return getAdapter(TypeToken.get(type));
+  }
+
+  /**
+   * This method serializes the specified object into its equivalent representation as a tree of
+   * {@link JsonElement}s. This method should be used when the specified object is not a generic
+   * type. This method uses {@link Class#getClass()} to get the type for the specified object, but
+   * the {@code getClass()} loses the generic type information because of the Type Erasure feature
+   * of Java. Note that this method works fine if the any of the object fields are of generic type,
+   * just the object itself should not be of a generic type. If the object is of generic type, use
+   * {@link #toJsonTree(Object, Type)} instead.
+   *
+   * @param src the object for which Json representation is to be created setting for Gson
+   * @return Json representation of {@code src}.
+   * @since 1.4
+   */
+  public JsonElement toJsonTree(Object src) {
+    if (src == null) {
+      return JsonNull.INSTANCE;
+    }
+    return toJsonTree(src, src.getClass());
+  }
+
+  /**
+   * This method serializes the specified object, including those of generic types, into its
+   * equivalent representation as a tree of {@link JsonElement}s. This method must be used if the
+   * specified object is a generic type. For non-generic objects, use {@link #toJsonTree(Object)}
+   * instead.
+   *
+   * @param src the object for which JSON representation is to be created
+   * @param typeOfSrc The specific genericized type of src. You can obtain
+   * this type by using the {@link com.google.gson.reflect.TypeToken} class. For example,
+   * to get the type for {@code Collection<Foo>}, you should use:
+   * <pre>
+   * Type typeOfSrc = new TypeToken&lt;Collection&lt;Foo&gt;&gt;(){}.getType();
+   * </pre>
+   * @return Json representation of {@code src}
+   * @since 1.4
+   */
+  public JsonElement toJsonTree(Object src, Type typeOfSrc) {
+    JsonTreeWriter writer = new JsonTreeWriter();
+    toJson(src, typeOfSrc, writer);
+    return writer.get();
+  }
+
+  /**
+   * This method serializes the specified object into its equivalent Json representation.
+   * This method should be used when the specified object is not a generic type. This method uses
+   * {@link Class#getClass()} to get the type for the specified object, but the
+   * {@code getClass()} loses the generic type information because of the Type Erasure feature
+   * of Java. Note that this method works fine if the any of the object fields are of generic type,
+   * just the object itself should not be of a generic type. If the object is of generic type, use
+   * {@link #toJson(Object, Type)} instead. If you want to write out the object to a
+   * {@link Writer}, use {@link #toJson(Object, Appendable)} instead.
+   *
+   * @param src the object for which Json representation is to be created setting for Gson
+   * @return Json representation of {@code src}.
+   */
+  public String toJson(Object src) {
+    if (src == null) {
+      return toJson(JsonNull.INSTANCE);
+    }
+    return toJson(src, src.getClass());
+  }
+
+  /**
+   * This method serializes the specified object, including those of generic types, into its
+   * equivalent Json representation. This method must be used if the specified object is a generic
+   * type. For non-generic objects, use {@link #toJson(Object)} instead. If you want to write out
+   * the object to a {@link Appendable}, use {@link #toJson(Object, Type, Appendable)} instead.
+   *
+   * @param src the object for which JSON representation is to be created
+   * @param typeOfSrc The specific genericized type of src. You can obtain
+   * this type by using the {@link com.google.gson.reflect.TypeToken} class. For example,
+   * to get the type for {@code Collection<Foo>}, you should use:
+   * <pre>
+   * Type typeOfSrc = new TypeToken&lt;Collection&lt;Foo&gt;&gt;(){}.getType();
+   * </pre>
+   * @return Json representation of {@code src}
+   */
+  public String toJson(Object src, Type typeOfSrc) {
+    StringWriter writer = new StringWriter();
+    toJson(src, typeOfSrc, writer);
+    return writer.toString();
+  }
+
+  /**
+   * This method serializes the specified object into its equivalent Json representation.
+   * This method should be used when the specified object is not a generic type. This method uses
+   * {@link Class#getClass()} to get the type for the specified object, but the
+   * {@code getClass()} loses the generic type information because of the Type Erasure feature
+   * of Java. Note that this method works fine if the any of the object fields are of generic type,
+   * just the object itself should not be of a generic type. If the object is of generic type, use
+   * {@link #toJson(Object, Type, Appendable)} instead.
+   *
+   * @param src the object for which Json representation is to be created setting for Gson
+   * @param writer Writer to which the Json representation needs to be written
+   * @throws JsonIOException if there was a problem writing to the writer
+   * @since 1.2
+   */
+  public void toJson(Object src, Appendable writer) throws JsonIOException {
+    if (src != null) {
+      toJson(src, src.getClass(), writer);
+    } else {
+      toJson(JsonNull.INSTANCE, writer);
+    }
+  }
+
+  /**
+   * This method serializes the specified object, including those of generic types, into its
+   * equivalent Json representation. This method must be used if the specified object is a generic
+   * type. For non-generic objects, use {@link #toJson(Object, Appendable)} instead.
+   *
+   * @param src the object for which JSON representation is to be created
+   * @param typeOfSrc The specific genericized type of src. You can obtain
+   * this type by using the {@link com.google.gson.reflect.TypeToken} class. For example,
+   * to get the type for {@code Collection<Foo>}, you should use:
+   * <pre>
+   * Type typeOfSrc = new TypeToken&lt;Collection&lt;Foo&gt;&gt;(){}.getType();
+   * </pre>
+   * @param writer Writer to which the Json representation of src needs to be written.
+   * @throws JsonIOException if there was a problem writing to the writer
+   * @since 1.2
+   */
+  public void toJson(Object src, Type typeOfSrc, Appendable writer) throws JsonIOException {
+    try {
+      JsonWriter jsonWriter = newJsonWriter(Streams.writerForAppendable(writer));
+      toJson(src, typeOfSrc, jsonWriter);
+    } catch (IOException e) {
+      throw new JsonIOException(e);
+    }
+  }
+
+  /**
+   * Writes the JSON representation of {@code src} of type {@code typeOfSrc} to
+   * {@code writer}.
+   * @throws JsonIOException if there was a problem writing to the writer
+   */
+  @SuppressWarnings("unchecked")
+  public void toJson(Object src, Type typeOfSrc, JsonWriter writer) throws JsonIOException {
+    TypeAdapter<?> adapter = getAdapter(TypeToken.get(typeOfSrc));
+    boolean oldLenient = writer.isLenient();
+    writer.setLenient(true);
+    boolean oldHtmlSafe = writer.isHtmlSafe();
+    writer.setHtmlSafe(htmlSafe);
+    boolean oldSerializeNulls = writer.getSerializeNulls();
+    writer.setSerializeNulls(serializeNulls);
+    try {
+      ((TypeAdapter<Object>) adapter).write(writer, src);
+    } catch (IOException e) {
+      throw new JsonIOException(e);
+    } catch (AssertionError e) {
+      AssertionError error = new AssertionError("AssertionError (GSON " + GsonBuildConfig.VERSION + "): " + e.getMessage());
+      error.initCause(e);
+      throw error;
+    } finally {
+      writer.setLenient(oldLenient);
+      writer.setHtmlSafe(oldHtmlSafe);
+      writer.setSerializeNulls(oldSerializeNulls);
+    }
+  }
+
+  /**
+   * Converts a tree of {@link JsonElement}s into its equivalent JSON representation.
+   *
+   * @param jsonElement root of a tree of {@link JsonElement}s
+   * @return JSON String representation of the tree
+   * @since 1.4
+   */
+  public String toJson(JsonElement jsonElement) {
+    StringWriter writer = new StringWriter();
+    toJson(jsonElement, writer);
+    return writer.toString();
+  }
+
+  /**
+   * Writes out the equivalent JSON for a tree of {@link JsonElement}s.
+   *
+   * @param jsonElement root of a tree of {@link JsonElement}s
+   * @param writer Writer to which the Json representation needs to be written
+   * @throws JsonIOException if there was a problem writing to the writer
+   * @since 1.4
+   */
+  public void toJson(JsonElement jsonElement, Appendable writer) throws JsonIOException {
+    try {
+      JsonWriter jsonWriter = newJsonWriter(Streams.writerForAppendable(writer));
+      toJson(jsonElement, jsonWriter);
+    } catch (IOException e) {
+      throw new JsonIOException(e);
+    }
+  }
+
+  /**
+   * Returns a new JSON writer configured for the settings on this Gson instance.
+   */
+  public JsonWriter newJsonWriter(Writer writer) throws IOException {
+    if (generateNonExecutableJson) {
+      writer.write(JSON_NON_EXECUTABLE_PREFIX);
+    }
+    JsonWriter jsonWriter = new JsonWriter(writer);
+    if (prettyPrinting) {
+      jsonWriter.setIndent("  ");
+    }
+    jsonWriter.setSerializeNulls(serializeNulls);
+    return jsonWriter;
+  }
+
+  /**
+   * Returns a new JSON reader configured for the settings on this Gson instance.
+   */
+  public JsonReader newJsonReader(Reader reader) {
+    JsonReader jsonReader = new JsonReader(reader);
+    jsonReader.setLenient(lenient);
+    return jsonReader;
+  }
+
+  /**
+   * Writes the JSON for {@code jsonElement} to {@code writer}.
+   * @throws JsonIOException if there was a problem writing to the writer
+   */
+  public void toJson(JsonElement jsonElement, JsonWriter writer) throws JsonIOException {
+    boolean oldLenient = writer.isLenient();
+    writer.setLenient(true);
+    boolean oldHtmlSafe = writer.isHtmlSafe();
+    writer.setHtmlSafe(htmlSafe);
+    boolean oldSerializeNulls = writer.getSerializeNulls();
+    writer.setSerializeNulls(serializeNulls);
+    try {
+      Streams.write(jsonElement, writer);
+    } catch (IOException e) {
+      throw new JsonIOException(e);
+    } catch (AssertionError e) {
+      AssertionError error = new AssertionError("AssertionError (GSON " + GsonBuildConfig.VERSION + "): " + e.getMessage());
+      error.initCause(e);
+      throw error;
+    } finally {
+      writer.setLenient(oldLenient);
+      writer.setHtmlSafe(oldHtmlSafe);
+      writer.setSerializeNulls(oldSerializeNulls);
+    }
+  }
+
+  /**
+   * This method deserializes the specified Json into an object of the specified class. It is not
+   * suitable to use if the specified class is a generic type since it will not have the generic
+   * type information because of the Type Erasure feature of Java. Therefore, this method should not
+   * be used if the desired type is a generic type. Note that this method works fine if the any of
+   * the fields of the specified object are generics, just the object itself should not be a
+   * generic type. For the cases when the object is of generic type, invoke
+   * {@link #fromJson(String, Type)}. If you have the Json in a {@link Reader} instead of
+   * a String, use {@link #fromJson(Reader, Class)} instead.
+   *
+   * @param <T> the type of the desired object
+   * @param json the string from which the object is to be deserialized
+   * @param classOfT the class of T
+   * @return an object of type T from the string. Returns {@code null} if {@code json} is {@code null}
+   * or if {@code json} is empty.
+   * @throws JsonSyntaxException if json is not a valid representation for an object of type
+   * classOfT
+   */
+  public <T> T fromJson(String json, Class<T> classOfT) throws JsonSyntaxException {
+    Object object = fromJson(json, (Type) classOfT);
+    return Primitives.wrap(classOfT).cast(object);
+  }
+
+  /**
+   * This method deserializes the specified Json into an object of the specified type. This method
+   * is useful if the specified object is a generic type. For non-generic objects, use
+   * {@link #fromJson(String, Class)} instead. If you have the Json in a {@link Reader} instead of
+   * a String, use {@link #fromJson(Reader, Type)} instead.
+   *
+   * @param <T> the type of the desired object
+   * @param json the string from which the object is to be deserialized
+   * @param typeOfT The specific genericized type of src. You can obtain this type by using the
+   * {@link com.google.gson.reflect.TypeToken} class. For example, to get the type for
+   * {@code Collection<Foo>}, you should use:
+   * <pre>
+   * Type typeOfT = new TypeToken&lt;Collection&lt;Foo&gt;&gt;(){}.getType();
+   * </pre>
+   * @return an object of type T from the string. Returns {@code null} if {@code json} is {@code null}
+   * or if {@code json} is empty.
+   * @throws JsonParseException if json is not a valid representation for an object of type typeOfT
+   * @throws JsonSyntaxException if json is not a valid representation for an object of type
+   */
+  @SuppressWarnings("unchecked")
+  public <T> T fromJson(String json, Type typeOfT) throws JsonSyntaxException {
+    if (json == null) {
+      return null;
+    }
+    StringReader reader = new StringReader(json);
+    T target = (T) fromJson(reader, typeOfT);
+    return target;
+  }
+
+  /**
+   * This method deserializes the Json read from the specified reader into an object of the
+   * specified class. It is not suitable to use if the specified class is a generic type since it
+   * will not have the generic type information because of the Type Erasure feature of Java.
+   * Therefore, this method should not be used if the desired type is a generic type. Note that
+   * this method works fine if the any of the fields of the specified object are generics, just the
+   * object itself should not be a generic type. For the cases when the object is of generic type,
+   * invoke {@link #fromJson(Reader, Type)}. If you have the Json in a String form instead of a
+   * {@link Reader}, use {@link #fromJson(String, Class)} instead.
+   *
+   * @param <T> the type of the desired object
+   * @param json the reader producing the Json from which the object is to be deserialized.
+   * @param classOfT the class of T
+   * @return an object of type T from the string. Returns {@code null} if {@code json} is at EOF.
+   * @throws JsonIOException if there was a problem reading from the Reader
+   * @throws JsonSyntaxException if json is not a valid representation for an object of type
+   * @since 1.2
+   */
+  public <T> T fromJson(Reader json, Class<T> classOfT) throws JsonSyntaxException, JsonIOException {
+    JsonReader jsonReader = newJsonReader(json);
+    Object object = fromJson(jsonReader, classOfT);
+    assertFullConsumption(object, jsonReader);
+    return Primitives.wrap(classOfT).cast(object);
+  }
+
+  /**
+   * This method deserializes the Json read from the specified reader into an object of the
+   * specified type. This method is useful if the specified object is a generic type. For
+   * non-generic objects, use {@link #fromJson(Reader, Class)} instead. If you have the Json in a
+   * String form instead of a {@link Reader}, use {@link #fromJson(String, Type)} instead.
+   *
+   * @param <T> the type of the desired object
+   * @param json the reader producing Json from which the object is to be deserialized
+   * @param typeOfT The specific genericized type of src. You can obtain this type by using the
+   * {@link com.google.gson.reflect.TypeToken} class. For example, to get the type for
+   * {@code Collection<Foo>}, you should use:
+   * <pre>
+   * Type typeOfT = new TypeToken&lt;Collection&lt;Foo&gt;&gt;(){}.getType();
+   * </pre>
+   * @return an object of type T from the json. Returns {@code null} if {@code json} is at EOF.
+   * @throws JsonIOException if there was a problem reading from the Reader
+   * @throws JsonSyntaxException if json is not a valid representation for an object of type
+   * @since 1.2
+   */
+  @SuppressWarnings("unchecked")
+  public <T> T fromJson(Reader json, Type typeOfT) throws JsonIOException, JsonSyntaxException {
+    JsonReader jsonReader = newJsonReader(json);
+    T object = (T) fromJson(jsonReader, typeOfT);
+    assertFullConsumption(object, jsonReader);
+    return object;
+  }
+
+  private static void assertFullConsumption(Object obj, JsonReader reader) {
+    try {
+      if (obj != null && reader.peek() != JsonToken.END_DOCUMENT) {
+        throw new JsonIOException("JSON document was not fully consumed.");
+      }
+    } catch (MalformedJsonException e) {
+      throw new JsonSyntaxException(e);
+    } catch (IOException e) {
+      throw new JsonIOException(e);
+    }
+  }
+
+  /**
+   * Reads the next JSON value from {@code reader} and convert it to an object
+   * of type {@code typeOfT}. Returns {@code null}, if the {@code reader} is at EOF.
+   * Since Type is not parameterized by T, this method is type unsafe and should be used carefully
+   *
+   * @throws JsonIOException if there was a problem writing to the Reader
+   * @throws JsonSyntaxException if json is not a valid representation for an object of type
+   */
+  @SuppressWarnings("unchecked")
+  public <T> T fromJson(JsonReader reader, Type typeOfT) throws JsonIOException, JsonSyntaxException {
+    boolean isEmpty = true;
+    boolean oldLenient = reader.isLenient();
+    reader.setLenient(true);
+    try {
+      reader.peek();
+      isEmpty = false;
+      TypeToken<T> typeToken = (TypeToken<T>) TypeToken.get(typeOfT);
+      TypeAdapter<T> typeAdapter = getAdapter(typeToken);
+      T object = typeAdapter.read(reader);
+      return object;
+    } catch (EOFException e) {
+      /*
+       * For compatibility with JSON 1.5 and earlier, we return null for empty
+       * documents instead of throwing.
+       */
+      if (isEmpty) {
+        return null;
+      }
+      throw new JsonSyntaxException(e);
+    } catch (IllegalStateException e) {
+      throw new JsonSyntaxException(e);
+    } catch (IOException e) {
+      // TODO(inder): Figure out whether it is indeed right to rethrow this as JsonSyntaxException
+      throw new JsonSyntaxException(e);
+    } catch (AssertionError e) {
+      AssertionError error = new AssertionError("AssertionError (GSON " + GsonBuildConfig.VERSION + "): " + e.getMessage());
+      error.initCause(e);
+      throw error;
+    } finally {
+      reader.setLenient(oldLenient);
+    }
+  }
+
+  /**
+   * This method deserializes the Json read from the specified parse tree into an object of the
+   * specified type. It is not suitable to use if the specified class is a generic type since it
+   * will not have the generic type information because of the Type Erasure feature of Java.
+   * Therefore, this method should not be used if the desired type is a generic type. Note that
+   * this method works fine if the any of the fields of the specified object are generics, just the
+   * object itself should not be a generic type. For the cases when the object is of generic type,
+   * invoke {@link #fromJson(JsonElement, Type)}.
+   * @param <T> the type of the desired object
+   * @param json the root of the parse tree of {@link JsonElement}s from which the object is to
+   * be deserialized
+   * @param classOfT The class of T
+   * @return an object of type T from the json. Returns {@code null} if {@code json} is {@code null}
+   * or if {@code json} is empty.
+   * @throws JsonSyntaxException if json is not a valid representation for an object of type typeOfT
+   * @since 1.3
+   */
+  public <T> T fromJson(JsonElement json, Class<T> classOfT) throws JsonSyntaxException {
+    Object object = fromJson(json, (Type) classOfT);
+    return Primitives.wrap(classOfT).cast(object);
+  }
+
+  /**
+   * This method deserializes the Json read from the specified parse tree into an object of the
+   * specified type. This method is useful if the specified object is a generic type. For
+   * non-generic objects, use {@link #fromJson(JsonElement, Class)} instead.
+   *
+   * @param <T> the type of the desired object
+   * @param json the root of the parse tree of {@link JsonElement}s from which the object is to
+   * be deserialized
+   * @param typeOfT The specific genericized type of src. You can obtain this type by using the
+   * {@link com.google.gson.reflect.TypeToken} class. For example, to get the type for
+   * {@code Collection<Foo>}, you should use:
+   * <pre>
+   * Type typeOfT = new TypeToken&lt;Collection&lt;Foo&gt;&gt;(){}.getType();
+   * </pre>
+   * @return an object of type T from the json. Returns {@code null} if {@code json} is {@code null}
+   * or if {@code json} is empty.
+   * @throws JsonSyntaxException if json is not a valid representation for an object of type typeOfT
+   * @since 1.3
+   */
+  @SuppressWarnings("unchecked")
+  public <T> T fromJson(JsonElement json, Type typeOfT) throws JsonSyntaxException {
+    if (json == null) {
+      return null;
+    }
+    return (T) fromJson(new JsonTreeReader(json), typeOfT);
+  }
+
+  static class FutureTypeAdapter<T> extends TypeAdapter<T> {
+    private TypeAdapter<T> delegate;
+
+    public void setDelegate(TypeAdapter<T> typeAdapter) {
+      if (delegate != null) {
+        throw new AssertionError();
+      }
+      delegate = typeAdapter;
+    }
+
+    @Override public T read(JsonReader in) throws IOException {
+      if (delegate == null) {
+        throw new IllegalStateException();
+      }
+      return delegate.read(in);
+    }
+
+    @Override public void write(JsonWriter out, T value) throws IOException {
+      if (delegate == null) {
+        throw new IllegalStateException();
+      }
+      delegate.write(out, value);
+    }
+  }
+
+  @Override
+  public String toString() {
+    return new StringBuilder("{serializeNulls:")
+        .append(serializeNulls)
+        .append(",factories:").append(factories)
+        .append(",instanceCreators:").append(constructorConstructor)
+        .append("}")
+        .toString();
+  }
+}
diff --git a/gson/src/main/java/com/google/gson/GsonBuilder.java b/gson/src/main/java/com/google/gson/GsonBuilder.java
new file mode 100644
index 0000000000000000000000000000000000000000..f029edb62d70106b113ffcebefdfa02c740dc9db
--- /dev/null
+++ b/gson/src/main/java/com/google/gson/GsonBuilder.java
@@ -0,0 +1,628 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.gson;
+
+import java.lang.reflect.Type;
+import java.sql.Timestamp;
+import java.text.DateFormat;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import com.google.gson.internal.$Gson$Preconditions;
+import com.google.gson.internal.Excluder;
+import com.google.gson.internal.bind.TreeTypeAdapter;
+import com.google.gson.internal.bind.TypeAdapters;
+import com.google.gson.reflect.TypeToken;
+import com.google.gson.stream.JsonReader;
+
+import static com.google.gson.Gson.DEFAULT_COMPLEX_MAP_KEYS;
+import static com.google.gson.Gson.DEFAULT_ESCAPE_HTML;
+import static com.google.gson.Gson.DEFAULT_JSON_NON_EXECUTABLE;
+import static com.google.gson.Gson.DEFAULT_LENIENT;
+import static com.google.gson.Gson.DEFAULT_PRETTY_PRINT;
+import static com.google.gson.Gson.DEFAULT_SERIALIZE_NULLS;
+import static com.google.gson.Gson.DEFAULT_SPECIALIZE_FLOAT_VALUES;
+
+/**
+ * <p>Use this builder to construct a {@link Gson} instance when you need to set configuration
+ * options other than the default. For {@link Gson} with default configuration, it is simpler to
+ * use {@code new Gson()}. {@code GsonBuilder} is best used by creating it, and then invoking its
+ * various configuration methods, and finally calling create.</p>
+ *
+ * <p>The following is an example shows how to use the {@code GsonBuilder} to construct a Gson
+ * instance:
+ *
+ * <pre>
+ * Gson gson = new GsonBuilder()
+ *     .registerTypeAdapter(Id.class, new IdTypeAdapter())
+ *     .enableComplexMapKeySerialization()
+ *     .serializeNulls()
+ *     .setDateFormat(DateFormat.LONG)
+ *     .setFieldNamingPolicy(FieldNamingPolicy.UPPER_CAMEL_CASE)
+ *     .setPrettyPrinting()
+ *     .setVersion(1.0)
+ *     .create();
+ * </pre>
+ *
+ * <p>NOTES:
+ * <ul>
+ * <li> the order of invocation of configuration methods does not matter.</li>
+ * <li> The default serialization of {@link Date} and its subclasses in Gson does
+ *  not contain time-zone information. So, if you are using date/time instances,
+ *  use {@code GsonBuilder} and its {@code setDateFormat} methods.</li>
+ *  </ul>
+ * 
+ *
+ * @author Inderjeet Singh
+ * @author Joel Leitch
+ * @author Jesse Wilson
+ */
+public final class GsonBuilder {
+  private Excluder excluder = Excluder.DEFAULT;
+  private LongSerializationPolicy longSerializationPolicy = LongSerializationPolicy.DEFAULT;
+  private FieldNamingStrategy fieldNamingPolicy = FieldNamingPolicy.IDENTITY;
+  private final Map<Type, InstanceCreator<?>> instanceCreators
+      = new HashMap<Type, InstanceCreator<?>>();
+  private final List<TypeAdapterFactory> factories = new ArrayList<TypeAdapterFactory>();
+  /** tree-style hierarchy factories. These come after factories for backwards compatibility. */
+  private final List<TypeAdapterFactory> hierarchyFactories = new ArrayList<TypeAdapterFactory>();
+  private boolean serializeNulls = DEFAULT_SERIALIZE_NULLS;
+  private String datePattern;
+  private int dateStyle = DateFormat.DEFAULT;
+  private int timeStyle = DateFormat.DEFAULT;
+  private boolean complexMapKeySerialization = DEFAULT_COMPLEX_MAP_KEYS;
+  private boolean serializeSpecialFloatingPointValues = DEFAULT_SPECIALIZE_FLOAT_VALUES;
+  private boolean escapeHtmlChars = DEFAULT_ESCAPE_HTML;
+  private boolean prettyPrinting = DEFAULT_PRETTY_PRINT;
+  private boolean generateNonExecutableJson = DEFAULT_JSON_NON_EXECUTABLE;
+  private boolean lenient = DEFAULT_LENIENT;
+
+  /**
+   * Creates a GsonBuilder instance that can be used to build Gson with various configuration
+   * settings. GsonBuilder follows the builder pattern, and it is typically used by first
+   * invoking various configuration methods to set desired options, and finally calling
+   * {@link #create()}.
+   */
+  public GsonBuilder() {
+  }
+
+  /**
+   * Constructs a GsonBuilder instance from a Gson instance. The newly constructed GsonBuilder
+   * has the same configuration as the previously built Gson instance.
+   *
+   * @param gson the gson instance whose configuration should by applied to a new GsonBuilder.
+   */
+  GsonBuilder(Gson gson) {
+    this.excluder = gson.excluder;
+    this.fieldNamingPolicy = gson.fieldNamingStrategy;
+    this.instanceCreators.putAll(gson.instanceCreators);
+    this.serializeNulls = gson.serializeNulls;
+    this.complexMapKeySerialization = gson.complexMapKeySerialization;
+    this.generateNonExecutableJson = gson.generateNonExecutableJson;
+    this.escapeHtmlChars = gson.htmlSafe;
+    this.prettyPrinting = gson.prettyPrinting;
+    this.lenient = gson.lenient;
+    this.serializeSpecialFloatingPointValues = gson.serializeSpecialFloatingPointValues;
+    this.longSerializationPolicy = gson.longSerializationPolicy;
+    this.datePattern = gson.datePattern;
+    this.dateStyle = gson.dateStyle;
+    this.timeStyle = gson.timeStyle;
+    this.factories.addAll(gson.builderFactories);
+    this.hierarchyFactories.addAll(gson.builderHierarchyFactories);
+  }
+
+  /**
+   * Configures Gson to enable versioning support.
+   *
+   * @param ignoreVersionsAfter any field or type marked with a version higher than this value
+   * are ignored during serialization or deserialization.
+   * @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern
+   */
+  public GsonBuilder setVersion(double ignoreVersionsAfter) {
+    excluder = excluder.withVersion(ignoreVersionsAfter);
+    return this;
+  }
+
+  /**
+   * Configures Gson to excludes all class fields that have the specified modifiers. By default,
+   * Gson will exclude all fields marked transient or static. This method will override that
+   * behavior.
+   *
+   * @param modifiers the field modifiers. You must use the modifiers specified in the
+   * {@link java.lang.reflect.Modifier} class. For example,
+   * {@link java.lang.reflect.Modifier#TRANSIENT},
+   * {@link java.lang.reflect.Modifier#STATIC}.
+   * @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern
+   */
+  public GsonBuilder excludeFieldsWithModifiers(int... modifiers) {
+    excluder = excluder.withModifiers(modifiers);
+    return this;
+  }
+
+  /**
+   * Makes the output JSON non-executable in Javascript by prefixing the generated JSON with some
+   * special text. This prevents attacks from third-party sites through script sourcing. See
+   * <a href="http://code.google.com/p/google-gson/issues/detail?id=42">Gson Issue 42</a>
+   * for details.
+   *
+   * @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern
+   * @since 1.3
+   */
+  public GsonBuilder generateNonExecutableJson() {
+    this.generateNonExecutableJson = true;
+    return this;
+  }
+
+  /**
+   * Configures Gson to exclude all fields from consideration for serialization or deserialization
+   * that do not have the {@link com.google.gson.annotations.Expose} annotation.
+   *
+   * @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern
+   */
+  public GsonBuilder excludeFieldsWithoutExposeAnnotation() {
+    excluder = excluder.excludeFieldsWithoutExposeAnnotation();
+    return this;
+  }
+
+  /**
+   * Configure Gson to serialize null fields. By default, Gson omits all fields that are null
+   * during serialization.
+   *
+   * @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern
+   * @since 1.2
+   */
+  public GsonBuilder serializeNulls() {
+    this.serializeNulls = true;
+    return this;
+  }
+
+  /**
+   * Enabling this feature will only change the serialized form if the map key is
+   * a complex type (i.e. non-primitive) in its <strong>serialized</strong> JSON
+   * form. The default implementation of map serialization uses {@code toString()}
+   * on the key; however, when this is called then one of the following cases
+   * apply:
+   *
+   * <h3>Maps as JSON objects</h3>
+   * For this case, assume that a type adapter is registered to serialize and
+   * deserialize some {@code Point} class, which contains an x and y coordinate,
+   * to/from the JSON Primitive string value {@code "(x,y)"}. The Java map would
+   * then be serialized as a {@link JsonObject}.
+   *
+   * <p>Below is an example:
+   * <pre>  {@code
+   *   Gson gson = new GsonBuilder()
+   *       .register(Point.class, new MyPointTypeAdapter())
+   *       .enableComplexMapKeySerialization()
+   *       .create();
+   *
+   *   Map<Point, String> original = new LinkedHashMap<Point, String>();
+   *   original.put(new Point(5, 6), "a");
+   *   original.put(new Point(8, 8), "b");
+   *   System.out.println(gson.toJson(original, type));
+   * }</pre>
+   * The above code prints this JSON object:<pre>  {@code
+   *   {
+   *     "(5,6)": "a",
+   *     "(8,8)": "b"
+   *   }
+   * }</pre>
+   *
+   * <h3>Maps as JSON arrays</h3>
+   * For this case, assume that a type adapter was NOT registered for some
+   * {@code Point} class, but rather the default Gson serialization is applied.
+   * In this case, some {@code new Point(2,3)} would serialize as {@code
+   * {"x":2,"y":5}}.
+   *
+   * <p>Given the assumption above, a {@code Map<Point, String>} will be
+   * serialize as an array of arrays (can be viewed as an entry set of pairs).
+   *
+   * <p>Below is an example of serializing complex types as JSON arrays:
+   * <pre> {@code
+   *   Gson gson = new GsonBuilder()
+   *       .enableComplexMapKeySerialization()
+   *       .create();
+   *
+   *   Map<Point, String> original = new LinkedHashMap<Point, String>();
+   *   original.put(new Point(5, 6), "a");
+   *   original.put(new Point(8, 8), "b");
+   *   System.out.println(gson.toJson(original, type));
+   * }
+   * </pre>
+   *
+   * The JSON output would look as follows:
+   * <pre>   {@code
+   *   [
+   *     [
+   *       {
+   *         "x": 5,
+   *         "y": 6
+   *       },
+   *       "a"
+   *     ],
+   *     [
+   *       {
+   *         "x": 8,
+   *         "y": 8
+   *       },
+   *       "b"
+   *     ]
+   *   ]
+   * }</pre>
+   *
+   * @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern
+   * @since 1.7
+   */
+  public GsonBuilder enableComplexMapKeySerialization() {
+    complexMapKeySerialization = true;
+    return this;
+  }
+
+  /**
+   * Configures Gson to exclude inner classes during serialization.
+   *
+   * @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern
+   * @since 1.3
+   */
+  public GsonBuilder disableInnerClassSerialization() {
+    excluder = excluder.disableInnerClassSerialization();
+    return this;
+  }
+
+  /**
+   * Configures Gson to apply a specific serialization policy for {@code Long} and {@code long}
+   * objects.
+   *
+   * @param serializationPolicy the particular policy to use for serializing longs.
+   * @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern
+   * @since 1.3
+   */
+  public GsonBuilder setLongSerializationPolicy(LongSerializationPolicy serializationPolicy) {
+    this.longSerializationPolicy = serializationPolicy;
+    return this;
+  }
+
+  /**
+   * Configures Gson to apply a specific naming policy to an object's field during serialization
+   * and deserialization.
+   *
+   * @param namingConvention the JSON field naming convention to use for serialization and
+   * deserialization.
+   * @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern
+   */
+  public GsonBuilder setFieldNamingPolicy(FieldNamingPolicy namingConvention) {
+    this.fieldNamingPolicy = namingConvention;
+    return this;
+  }
+
+  /**
+   * Configures Gson to apply a specific naming policy strategy to an object's field during
+   * serialization and deserialization.
+   *
+   * @param fieldNamingStrategy the actual naming strategy to apply to the fields
+   * @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern
+   * @since 1.3
+   */
+  public GsonBuilder setFieldNamingStrategy(FieldNamingStrategy fieldNamingStrategy) {
+    this.fieldNamingPolicy = fieldNamingStrategy;
+    return this;
+  }
+
+  /**
+   * Configures Gson to apply a set of exclusion strategies during both serialization and
+   * deserialization. Each of the {@code strategies} will be applied as a disjunction rule.
+   * This means that if one of the {@code strategies} suggests that a field (or class) should be
+   * skipped then that field (or object) is skipped during serialization/deserialization.
+   *
+   * @param strategies the set of strategy object to apply during object (de)serialization.
+   * @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern
+   * @since 1.4
+   */
+  public GsonBuilder setExclusionStrategies(ExclusionStrategy... strategies) {
+    for (ExclusionStrategy strategy : strategies) {
+      excluder = excluder.withExclusionStrategy(strategy, true, true);
+    }
+    return this;
+  }
+
+  /**
+   * Configures Gson to apply the passed in exclusion strategy during serialization.
+   * If this method is invoked numerous times with different exclusion strategy objects
+   * then the exclusion strategies that were added will be applied as a disjunction rule.
+   * This means that if one of the added exclusion strategies suggests that a field (or
+   * class) should be skipped then that field (or object) is skipped during its
+   * serialization.
+   *
+   * @param strategy an exclusion strategy to apply during serialization.
+   * @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern
+   * @since 1.7
+   */
+  public GsonBuilder addSerializationExclusionStrategy(ExclusionStrategy strategy) {
+    excluder = excluder.withExclusionStrategy(strategy, true, false);
+    return this;
+  }
+
+  /**
+   * Configures Gson to apply the passed in exclusion strategy during deserialization.
+   * If this method is invoked numerous times with different exclusion strategy objects
+   * then the exclusion strategies that were added will be applied as a disjunction rule.
+   * This means that if one of the added exclusion strategies suggests that a field (or
+   * class) should be skipped then that field (or object) is skipped during its
+   * deserialization.
+   *
+   * @param strategy an exclusion strategy to apply during deserialization.
+   * @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern
+   * @since 1.7
+   */
+  public GsonBuilder addDeserializationExclusionStrategy(ExclusionStrategy strategy) {
+    excluder = excluder.withExclusionStrategy(strategy, false, true);
+    return this;
+  }
+
+  /**
+   * Configures Gson to output Json that fits in a page for pretty printing. This option only
+   * affects Json serialization.
+   *
+   * @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern
+   */
+  public GsonBuilder setPrettyPrinting() {
+    prettyPrinting = true;
+    return this;
+  }
+
+  /**
+   * By default, Gson is strict and only accepts JSON as specified by
+   * <a href="http://www.ietf.org/rfc/rfc4627.txt">RFC 4627</a>. This option makes the parser
+   * liberal in what it accepts.
+   *
+   * @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern
+   * @see JsonReader#setLenient(boolean)
+   */
+  public GsonBuilder setLenient() {
+    lenient = true;
+    return this;
+  }
+
+  /**
+   * By default, Gson escapes HTML characters such as &lt; &gt; etc. Use this option to configure
+   * Gson to pass-through HTML characters as is.
+   *
+   * @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern
+   * @since 1.3
+   */
+  public GsonBuilder disableHtmlEscaping() {
+    this.escapeHtmlChars = false;
+    return this;
+  }
+
+  /**
+   * Configures Gson to serialize {@code Date} objects according to the pattern provided. You can
+   * call this method or {@link #setDateFormat(int)} multiple times, but only the last invocation
+   * will be used to decide the serialization format.
+   *
+   * <p>The date format will be used to serialize and deserialize {@link java.util.Date}, {@link
+   * java.sql.Timestamp} and {@link java.sql.Date}.
+   *
+   * <p>Note that this pattern must abide by the convention provided by {@code SimpleDateFormat}
+   * class. See the documentation in {@link java.text.SimpleDateFormat} for more information on
+   * valid date and time patterns.</p>
+   *
+   * @param pattern the pattern that dates will be serialized/deserialized to/from
+   * @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern
+   * @since 1.2
+   */
+  public GsonBuilder setDateFormat(String pattern) {
+    // TODO(Joel): Make this fail fast if it is an invalid date format
+    this.datePattern = pattern;
+    return this;
+  }
+
+  /**
+   * Configures Gson to to serialize {@code Date} objects according to the style value provided.
+   * You can call this method or {@link #setDateFormat(String)} multiple times, but only the last
+   * invocation will be used to decide the serialization format.
+   *
+   * <p>Note that this style value should be one of the predefined constants in the
+   * {@code DateFormat} class. See the documentation in {@link java.text.DateFormat} for more
+   * information on the valid style constants.</p>
+   *
+   * @param style the predefined date style that date objects will be serialized/deserialized
+   * to/from
+   * @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern
+   * @since 1.2
+   */
+  public GsonBuilder setDateFormat(int style) {
+    this.dateStyle = style;
+    this.datePattern = null;
+    return this;
+  }
+
+  /**
+   * Configures Gson to to serialize {@code Date} objects according to the style value provided.
+   * You can call this method or {@link #setDateFormat(String)} multiple times, but only the last
+   * invocation will be used to decide the serialization format.
+   *
+   * <p>Note that this style value should be one of the predefined constants in the
+   * {@code DateFormat} class. See the documentation in {@link java.text.DateFormat} for more
+   * information on the valid style constants.</p>
+   *
+   * @param dateStyle the predefined date style that date objects will be serialized/deserialized
+   * to/from
+   * @param timeStyle the predefined style for the time portion of the date objects
+   * @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern
+   * @since 1.2
+   */
+  public GsonBuilder setDateFormat(int dateStyle, int timeStyle) {
+    this.dateStyle = dateStyle;
+    this.timeStyle = timeStyle;
+    this.datePattern = null;
+    return this;
+  }
+
+  /**
+   * Configures Gson for custom serialization or deserialization. This method combines the
+   * registration of an {@link TypeAdapter}, {@link InstanceCreator}, {@link JsonSerializer}, and a
+   * {@link JsonDeserializer}. It is best used when a single object {@code typeAdapter} implements
+   * all the required interfaces for custom serialization with Gson. If a type adapter was
+   * previously registered for the specified {@code type}, it is overwritten.
+   *
+   * <p>This registers the type specified and no other types: you must manually register related
+   * types! For example, applications registering {@code boolean.class} should also register {@code
+   * Boolean.class}.
+   *
+   * @param type the type definition for the type adapter being registered
+   * @param typeAdapter This object must implement at least one of the {@link TypeAdapter},
+   * {@link InstanceCreator}, {@link JsonSerializer}, and a {@link JsonDeserializer} interfaces.
+   * @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern
+   */
+  @SuppressWarnings({"unchecked", "rawtypes"})
+  public GsonBuilder registerTypeAdapter(Type type, Object typeAdapter) {
+    $Gson$Preconditions.checkArgument(typeAdapter instanceof JsonSerializer<?>
+        || typeAdapter instanceof JsonDeserializer<?>
+        || typeAdapter instanceof InstanceCreator<?>
+        || typeAdapter instanceof TypeAdapter<?>);
+    if (typeAdapter instanceof InstanceCreator<?>) {
+      instanceCreators.put(type, (InstanceCreator) typeAdapter);
+    }
+    if (typeAdapter instanceof JsonSerializer<?> || typeAdapter instanceof JsonDeserializer<?>) {
+      TypeToken<?> typeToken = TypeToken.get(type);
+      factories.add(TreeTypeAdapter.newFactoryWithMatchRawType(typeToken, typeAdapter));
+    }
+    if (typeAdapter instanceof TypeAdapter<?>) {
+      factories.add(TypeAdapters.newFactory(TypeToken.get(type), (TypeAdapter)typeAdapter));
+    }
+    return this;
+  }
+
+  /**
+   * Register a factory for type adapters. Registering a factory is useful when the type
+   * adapter needs to be configured based on the type of the field being processed. Gson
+   * is designed to handle a large number of factories, so you should consider registering
+   * them to be at par with registering an individual type adapter.
+   *
+   * @since 2.1
+   */
+  public GsonBuilder registerTypeAdapterFactory(TypeAdapterFactory factory) {
+    factories.add(factory);
+    return this;
+  }
+
+  /**
+   * Configures Gson for custom serialization or deserialization for an inheritance type hierarchy.
+   * This method combines the registration of a {@link TypeAdapter}, {@link JsonSerializer} and
+   * a {@link JsonDeserializer}. If a type adapter was previously registered for the specified
+   * type hierarchy, it is overridden. If a type adapter is registered for a specific type in
+   * the type hierarchy, it will be invoked instead of the one registered for the type hierarchy.
+   *
+   * @param baseType the class definition for the type adapter being registered for the base class
+   *        or interface
+   * @param typeAdapter This object must implement at least one of {@link TypeAdapter},
+   *        {@link JsonSerializer} or {@link JsonDeserializer} interfaces.
+   * @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern
+   * @since 1.7
+   */
+  @SuppressWarnings({"unchecked", "rawtypes"})
+  public GsonBuilder registerTypeHierarchyAdapter(Class<?> baseType, Object typeAdapter) {
+    $Gson$Preconditions.checkArgument(typeAdapter instanceof JsonSerializer<?>
+        || typeAdapter instanceof JsonDeserializer<?>
+        || typeAdapter instanceof TypeAdapter<?>);
+    if (typeAdapter instanceof JsonDeserializer || typeAdapter instanceof JsonSerializer) {
+      hierarchyFactories.add(TreeTypeAdapter.newTypeHierarchyFactory(baseType, typeAdapter));
+    }
+    if (typeAdapter instanceof TypeAdapter<?>) {
+      factories.add(TypeAdapters.newTypeHierarchyFactory(baseType, (TypeAdapter)typeAdapter));
+    }
+    return this;
+  }
+
+  /**
+   * Section 2.4 of <a href="http://www.ietf.org/rfc/rfc4627.txt">JSON specification</a> disallows
+   * special double values (NaN, Infinity, -Infinity). However,
+   * <a href="http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-262.pdf">Javascript
+   * specification</a> (see section 4.3.20, 4.3.22, 4.3.23) allows these values as valid Javascript
+   * values. Moreover, most JavaScript engines will accept these special values in JSON without
+   * problem. So, at a practical level, it makes sense to accept these values as valid JSON even
+   * though JSON specification disallows them.
+   *
+   * <p>Gson always accepts these special values during deserialization. However, it outputs
+   * strictly compliant JSON. Hence, if it encounters a float value {@link Float#NaN},
+   * {@link Float#POSITIVE_INFINITY}, {@link Float#NEGATIVE_INFINITY}, or a double value
+   * {@link Double#NaN}, {@link Double#POSITIVE_INFINITY}, {@link Double#NEGATIVE_INFINITY}, it
+   * will throw an {@link IllegalArgumentException}. This method provides a way to override the
+   * default behavior when you know that the JSON receiver will be able to handle these special
+   * values.
+   *
+   * @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern
+   * @since 1.3
+   */
+  public GsonBuilder serializeSpecialFloatingPointValues() {
+    this.serializeSpecialFloatingPointValues = true;
+    return this;
+  }
+
+  /**
+   * Creates a {@link Gson} instance based on the current configuration. This method is free of
+   * side-effects to this {@code GsonBuilder} instance and hence can be called multiple times.
+   *
+   * @return an instance of Gson configured with the options currently set in this builder
+   */
+  public Gson create() {
+    List<TypeAdapterFactory> factories = new ArrayList<TypeAdapterFactory>(this.factories.size() + this.hierarchyFactories.size() + 3);
+    factories.addAll(this.factories);
+    Collections.reverse(factories);
+
+    List<TypeAdapterFactory> hierarchyFactories = new ArrayList<TypeAdapterFactory>(this.hierarchyFactories);
+    Collections.reverse(hierarchyFactories);
+    factories.addAll(hierarchyFactories);
+
+    addTypeAdaptersForDate(datePattern, dateStyle, timeStyle, factories);
+
+    return new Gson(excluder, fieldNamingPolicy, instanceCreators,
+        serializeNulls, complexMapKeySerialization,
+        generateNonExecutableJson, escapeHtmlChars, prettyPrinting, lenient,
+        serializeSpecialFloatingPointValues, longSerializationPolicy,
+        datePattern, dateStyle, timeStyle,
+        this.factories, this.hierarchyFactories, factories);
+  }
+
+  @SuppressWarnings("unchecked")
+  private void addTypeAdaptersForDate(String datePattern, int dateStyle, int timeStyle,
+      List<TypeAdapterFactory> factories) {
+    DefaultDateTypeAdapter dateTypeAdapter;
+    TypeAdapter<Timestamp> timestampTypeAdapter;
+    TypeAdapter<java.sql.Date> javaSqlDateTypeAdapter;
+    if (datePattern != null && !"".equals(datePattern.trim())) {
+      dateTypeAdapter = new DefaultDateTypeAdapter(Date.class, datePattern);
+      timestampTypeAdapter = (TypeAdapter) new DefaultDateTypeAdapter(Timestamp.class, datePattern);
+      javaSqlDateTypeAdapter = (TypeAdapter) new DefaultDateTypeAdapter(java.sql.Date.class, datePattern);
+    } else if (dateStyle != DateFormat.DEFAULT && timeStyle != DateFormat.DEFAULT) {
+      dateTypeAdapter = new DefaultDateTypeAdapter(Date.class, dateStyle, timeStyle);
+      timestampTypeAdapter = (TypeAdapter) new DefaultDateTypeAdapter(Timestamp.class, dateStyle, timeStyle);
+      javaSqlDateTypeAdapter = (TypeAdapter) new DefaultDateTypeAdapter(java.sql.Date.class, dateStyle, timeStyle);
+    } else {
+      return;
+    }
+
+    factories.add(TypeAdapters.newFactory(Date.class, dateTypeAdapter));
+    factories.add(TypeAdapters.newFactory(Timestamp.class, timestampTypeAdapter));
+    factories.add(TypeAdapters.newFactory(java.sql.Date.class, javaSqlDateTypeAdapter));
+  }
+}
diff --git a/gson/src/main/java/com/google/gson/InstanceCreator.java b/gson/src/main/java/com/google/gson/InstanceCreator.java
new file mode 100644
index 0000000000000000000000000000000000000000..d5096a07a3dd15334585f14960bfa1da5c3fdb03
--- /dev/null
+++ b/gson/src/main/java/com/google/gson/InstanceCreator.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.gson;
+
+import java.lang.reflect.Type;
+
+/**
+ * This interface is implemented to create instances of a class that does not define a no-args
+ * constructor. If you can modify the class, you should instead add a private, or public
+ * no-args constructor. However, that is not possible for library classes, such as JDK classes, or
+ * a third-party library that you do not have source-code of. In such cases, you should define an
+ * instance creator for the class. Implementations of this interface should be registered with
+ * {@link GsonBuilder#registerTypeAdapter(Type, Object)} method before Gson will be able to use
+ * them.
+ * <p>Let us look at an example where defining an InstanceCreator might be useful. The
+ * {@code Id} class defined below does not have a default no-args constructor.</p>
+ *
+ * <pre>
+ * public class Id&lt;T&gt; {
+ *   private final Class&lt;T&gt; clazz;
+ *   private final long value;
+ *   public Id(Class&lt;T&gt; clazz, long value) {
+ *     this.clazz = clazz;
+ *     this.value = value;
+ *   }
+ * }
+ * </pre>
+ *
+ * <p>If Gson encounters an object of type {@code Id} during deserialization, it will throw an
+ * exception. The easiest way to solve this problem will be to add a (public or private) no-args
+ * constructor as follows:</p>
+ *
+ * <pre>
+ * private Id() {
+ *   this(Object.class, 0L);
+ * }
+ * </pre>
+ *
+ * <p>However, let us assume that the developer does not have access to the source-code of the
+ * {@code Id} class, or does not want to define a no-args constructor for it. The developer
+ * can solve this problem by defining an {@code InstanceCreator} for {@code Id}:</p>
+ *
+ * <pre>
+ * class IdInstanceCreator implements InstanceCreator&lt;Id&gt; {
+ *   public Id createInstance(Type type) {
+ *     return new Id(Object.class, 0L);
+ *   }
+ * }
+ * </pre>
+ *
+ * <p>Note that it does not matter what the fields of the created instance contain since Gson will
+ * overwrite them with the deserialized values specified in Json. You should also ensure that a
+ * <i>new</i> object is returned, not a common object since its fields will be overwritten.
+ * The developer will need to register {@code IdInstanceCreator} with Gson as follows:</p>
+ *
+ * <pre>
+ * Gson gson = new GsonBuilder().registerTypeAdapter(Id.class, new IdInstanceCreator()).create();
+ * </pre>
+ *
+ * @param <T> the type of object that will be created by this implementation.
+ *
+ * @author Inderjeet Singh
+ * @author Joel Leitch
+ */
+public interface InstanceCreator<T> {
+
+  /**
+   * Gson invokes this call-back method during deserialization to create an instance of the
+   * specified type. The fields of the returned instance are overwritten with the data present
+   * in the Json. Since the prior contents of the object are destroyed and overwritten, do not
+   * return an instance that is useful elsewhere. In particular, do not return a common instance,
+   * always use {@code new} to create a new instance.
+   *
+   * @param type the parameterized T represented as a {@link Type}.
+   * @return a default object instance of type T.
+   */
+  public T createInstance(Type type);
+}
diff --git a/gson/src/main/java/com/google/gson/JsonArray.java b/gson/src/main/java/com/google/gson/JsonArray.java
new file mode 100644
index 0000000000000000000000000000000000000000..f2b3b31f5a6415f312aacaa6fff13e7312a4008d
--- /dev/null
+++ b/gson/src/main/java/com/google/gson/JsonArray.java
@@ -0,0 +1,384 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.gson;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * A class representing an array type in Json. An array is a list of {@link JsonElement}s each of
+ * which can be of a different type. This is an ordered list, meaning that the order in which
+ * elements are added is preserved.
+ *
+ * @author Inderjeet Singh
+ * @author Joel Leitch
+ */
+public final class JsonArray extends JsonElement implements Iterable<JsonElement> {
+  private final List<JsonElement> elements;
+
+  /**
+   * Creates an empty JsonArray.
+   */
+  public JsonArray() {
+    elements = new ArrayList<JsonElement>();
+  }
+  
+  public JsonArray(int capacity) {
+    elements = new ArrayList<JsonElement>(capacity);
+  }
+
+  /**
+   * Creates a deep copy of this element and all its children
+   * @since 2.8.2
+   */
+  @Override
+  public JsonArray deepCopy() {
+    if (!elements.isEmpty()) {
+      JsonArray result = new JsonArray(elements.size());
+      for (JsonElement element : elements) {
+        result.add(element.deepCopy());
+      }
+      return result;
+    }
+    return new JsonArray();
+  }
+
+  /**
+   * Adds the specified boolean to self.
+   *
+   * @param bool the boolean that needs to be added to the array.
+   */
+  public void add(Boolean bool) {
+    elements.add(bool == null ? JsonNull.INSTANCE : new JsonPrimitive(bool));
+  }
+
+  /**
+   * Adds the specified character to self.
+   *
+   * @param character the character that needs to be added to the array.
+   */
+  public void add(Character character) {
+    elements.add(character == null ? JsonNull.INSTANCE : new JsonPrimitive(character));
+  }
+
+  /**
+   * Adds the specified number to self.
+   *
+   * @param number the number that needs to be added to the array.
+   */
+  public void add(Number number) {
+    elements.add(number == null ? JsonNull.INSTANCE : new JsonPrimitive(number));
+  }
+
+  /**
+   * Adds the specified string to self.
+   *
+   * @param string the string that needs to be added to the array.
+   */
+  public void add(String string) {
+    elements.add(string == null ? JsonNull.INSTANCE : new JsonPrimitive(string));
+  }
+
+  /**
+   * Adds the specified element to self.
+   *
+   * @param element the element that needs to be added to the array.
+   */
+  public void add(JsonElement element) {
+    if (element == null) {
+      element = JsonNull.INSTANCE;
+    }
+    elements.add(element);
+  }
+
+  /**
+   * Adds all the elements of the specified array to self.
+   *
+   * @param array the array whose elements need to be added to the array.
+   */
+  public void addAll(JsonArray array) {
+    elements.addAll(array.elements);
+  }
+
+  /**
+   * Replaces the element at the specified position in this array with the specified element.
+   *   Element can be null.
+   * @param index index of the element to replace
+   * @param element element to be stored at the specified position
+   * @return the element previously at the specified position
+   * @throws IndexOutOfBoundsException if the specified index is outside the array bounds
+   */
+  public JsonElement set(int index, JsonElement element) {
+    return elements.set(index, element);
+  }
+
+  /**
+   * Removes the first occurrence of the specified element from this array, if it is present.
+   * If the array does not contain the element, it is unchanged.
+   * @param element element to be removed from this array, if present
+   * @return true if this array contained the specified element, false otherwise
+   * @since 2.3
+   */
+  public boolean remove(JsonElement element) {
+    return elements.remove(element);
+  }
+
+  /**
+   * Removes the element at the specified position in this array. Shifts any subsequent elements
+   * to the left (subtracts one from their indices). Returns the element that was removed from
+   * the array.
+   * @param index index the index of the element to be removed
+   * @return the element previously at the specified position
+   * @throws IndexOutOfBoundsException if the specified index is outside the array bounds
+   * @since 2.3
+   */
+  public JsonElement remove(int index) {
+    return elements.remove(index);
+  }
+
+  /**
+   * Returns true if this array contains the specified element.
+   * @return true if this array contains the specified element.
+   * @param element whose presence in this array is to be tested
+   * @since 2.3
+   */
+  public boolean contains(JsonElement element) {
+    return elements.contains(element);
+  }
+
+  /**
+   * Returns the number of elements in the array.
+   *
+   * @return the number of elements in the array.
+   */
+  public int size() {
+    return elements.size();
+  }
+
+  /**
+   * Returns an iterator to navigate the elements of the array. Since the array is an ordered list,
+   * the iterator navigates the elements in the order they were inserted.
+   *
+   * @return an iterator to navigate the elements of the array.
+   */
+  public Iterator<JsonElement> iterator() {
+    return elements.iterator();
+  }
+
+  /**
+   * Returns the ith element of the array.
+   *
+   * @param i the index of the element that is being sought.
+   * @return the element present at the ith index.
+   * @throws IndexOutOfBoundsException if i is negative or greater than or equal to the
+   * {@link #size()} of the array.
+   */
+  public JsonElement get(int i) {
+    return elements.get(i);
+  }
+
+  /**
+   * convenience method to get this array as a {@link Number} if it contains a single element.
+   *
+   * @return get this element as a number if it is single element array.
+   * @throws ClassCastException if the element in the array is of not a {@link JsonPrimitive} and
+   * is not a valid Number.
+   * @throws IllegalStateException if the array has more than one element.
+   */
+  @Override
+  public Number getAsNumber() {
+    if (elements.size() == 1) {
+      return elements.get(0).getAsNumber();
+    }
+    throw new IllegalStateException();
+  }
+
+  /**
+   * convenience method to get this array as a {@link String} if it contains a single element.
+   *
+   * @return get this element as a String if it is single element array.
+   * @throws ClassCastException if the element in the array is of not a {@link JsonPrimitive} and
+   * is not a valid String.
+   * @throws IllegalStateException if the array has more than one element.
+   */
+  @Override
+  public String getAsString() {
+    if (elements.size() == 1) {
+      return elements.get(0).getAsString();
+    }
+    throw new IllegalStateException();
+  }
+
+  /**
+   * convenience method to get this array as a double if it contains a single element.
+   *
+   * @return get this element as a double if it is single element array.
+   * @throws ClassCastException if the element in the array is of not a {@link JsonPrimitive} and
+   * is not a valid double.
+   * @throws IllegalStateException if the array has more than one element.
+   */
+  @Override
+  public double getAsDouble() {
+    if (elements.size() == 1) {
+      return elements.get(0).getAsDouble();
+    }
+    throw new IllegalStateException();
+  }
+
+  /**
+   * convenience method to get this array as a {@link BigDecimal} if it contains a single element.
+   *
+   * @return get this element as a {@link BigDecimal} if it is single element array.
+   * @throws ClassCastException if the element in the array is of not a {@link JsonPrimitive}.
+   * @throws NumberFormatException if the element at index 0 is not a valid {@link BigDecimal}.
+   * @throws IllegalStateException if the array has more than one element.
+   * @since 1.2
+   */
+  @Override
+  public BigDecimal getAsBigDecimal() {
+    if (elements.size() == 1) {
+      return elements.get(0).getAsBigDecimal();
+    }
+    throw new IllegalStateException();
+  }
+
+  /**
+   * convenience method to get this array as a {@link BigInteger} if it contains a single element.
+   *
+   * @return get this element as a {@link BigInteger} if it is single element array.
+   * @throws ClassCastException if the element in the array is of not a {@link JsonPrimitive}.
+   * @throws NumberFormatException if the element at index 0 is not a valid {@link BigInteger}.
+   * @throws IllegalStateException if the array has more than one element.
+   * @since 1.2
+   */
+  @Override
+  public BigInteger getAsBigInteger() {
+    if (elements.size() == 1) {
+      return elements.get(0).getAsBigInteger();
+    }
+    throw new IllegalStateException();
+  }
+
+  /**
+   * convenience method to get this array as a float if it contains a single element.
+   *
+   * @return get this element as a float if it is single element array.
+   * @throws ClassCastException if the element in the array is of not a {@link JsonPrimitive} and
+   * is not a valid float.
+   * @throws IllegalStateException if the array has more than one element.
+   */
+  @Override
+  public float getAsFloat() {
+    if (elements.size() == 1) {
+      return elements.get(0).getAsFloat();
+    }
+    throw new IllegalStateException();
+  }
+
+  /**
+   * convenience method to get this array as a long if it contains a single element.
+   *
+   * @return get this element as a long if it is single element array.
+   * @throws ClassCastException if the element in the array is of not a {@link JsonPrimitive} and
+   * is not a valid long.
+   * @throws IllegalStateException if the array has more than one element.
+   */
+  @Override
+  public long getAsLong() {
+    if (elements.size() == 1) {
+      return elements.get(0).getAsLong();
+    }
+    throw new IllegalStateException();
+  }
+
+  /**
+   * convenience method to get this array as an integer if it contains a single element.
+   *
+   * @return get this element as an integer if it is single element array.
+   * @throws ClassCastException if the element in the array is of not a {@link JsonPrimitive} and
+   * is not a valid integer.
+   * @throws IllegalStateException if the array has more than one element.
+   */
+  @Override
+  public int getAsInt() {
+    if (elements.size() == 1) {
+      return elements.get(0).getAsInt();
+    }
+    throw new IllegalStateException();
+  }
+
+  @Override
+  public byte getAsByte() {
+    if (elements.size() == 1) {
+      return elements.get(0).getAsByte();
+    }
+    throw new IllegalStateException();
+  }
+
+  @Override
+  public char getAsCharacter() {
+    if (elements.size() == 1) {
+      return elements.get(0).getAsCharacter();
+    }
+    throw new IllegalStateException();
+  }
+
+  /**
+   * convenience method to get this array as a primitive short if it contains a single element.
+   *
+   * @return get this element as a primitive short if it is single element array.
+   * @throws ClassCastException if the element in the array is of not a {@link JsonPrimitive} and
+   * is not a valid short.
+   * @throws IllegalStateException if the array has more than one element.
+   */
+  @Override
+  public short getAsShort() {
+    if (elements.size() == 1) {
+      return elements.get(0).getAsShort();
+    }
+    throw new IllegalStateException();
+  }
+
+  /**
+   * convenience method to get this array as a boolean if it contains a single element.
+   *
+   * @return get this element as a boolean if it is single element array.
+   * @throws ClassCastException if the element in the array is of not a {@link JsonPrimitive} and
+   * is not a valid boolean.
+   * @throws IllegalStateException if the array has more than one element.
+   */
+  @Override
+  public boolean getAsBoolean() {
+    if (elements.size() == 1) {
+      return elements.get(0).getAsBoolean();
+    }
+    throw new IllegalStateException();
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    return (o == this) || (o instanceof JsonArray && ((JsonArray) o).elements.equals(elements));
+  }
+
+  @Override
+  public int hashCode() {
+    return elements.hashCode();
+  }
+}
diff --git a/gson/src/main/java/com/google/gson/JsonDeserializationContext.java b/gson/src/main/java/com/google/gson/JsonDeserializationContext.java
new file mode 100644
index 0000000000000000000000000000000000000000..00c7505432f2fd9e57cf95de802944e683ad3798
--- /dev/null
+++ b/gson/src/main/java/com/google/gson/JsonDeserializationContext.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.gson;
+
+import java.lang.reflect.Type;
+
+/**
+ * Context for deserialization that is passed to a custom deserializer during invocation of its
+ * {@link JsonDeserializer#deserialize(JsonElement, Type, JsonDeserializationContext)}
+ * method.
+ *
+ * @author Inderjeet Singh
+ * @author Joel Leitch
+ */
+public interface JsonDeserializationContext {
+
+  /**
+   * Invokes default deserialization on the specified object. It should never be invoked on
+   * the element received as a parameter of the
+   * {@link JsonDeserializer#deserialize(JsonElement, Type, JsonDeserializationContext)} method. Doing
+   * so will result in an infinite loop since Gson will in-turn call the custom deserializer again.
+   *
+   * @param json the parse tree.
+   * @param typeOfT type of the expected return value.
+   * @param <T> The type of the deserialized object.
+   * @return An object of type typeOfT.
+   * @throws JsonParseException if the parse tree does not contain expected data.
+   */
+  public <T> T deserialize(JsonElement json, Type typeOfT) throws JsonParseException;
+}
\ No newline at end of file
diff --git a/gson/src/main/java/com/google/gson/JsonDeserializer.java b/gson/src/main/java/com/google/gson/JsonDeserializer.java
new file mode 100644
index 0000000000000000000000000000000000000000..0589eb284085c90c31ed4dcaefb13775c9626b6d
--- /dev/null
+++ b/gson/src/main/java/com/google/gson/JsonDeserializer.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.gson;
+
+import java.lang.reflect.Type;
+
+/**
+ * <p>Interface representing a custom deserializer for Json. You should write a custom
+ * deserializer, if you are not happy with the default deserialization done by Gson. You will
+ * also need to register this deserializer through
+ * {@link GsonBuilder#registerTypeAdapter(Type, Object)}.</p>
+ *
+ * <p>Let us look at example where defining a deserializer will be useful. The {@code Id} class
+ * defined below has two fields: {@code clazz} and {@code value}.</p>
+ *
+ * <pre>
+ * public class Id&lt;T&gt; {
+ *   private final Class&lt;T&gt; clazz;
+ *   private final long value;
+ *   public Id(Class&lt;T&gt; clazz, long value) {
+ *     this.clazz = clazz;
+ *     this.value = value;
+ *   }
+ *   public long getValue() {
+ *     return value;
+ *   }
+ * }
+ * </pre>
+ *
+ * <p>The default deserialization of {@code Id(com.foo.MyObject.class, 20L)} will require the
+ * Json string to be <code>{"clazz":com.foo.MyObject,"value":20}</code>. Suppose, you already know
+ * the type of the field that the {@code Id} will be deserialized into, and hence just want to
+ * deserialize it from a Json string {@code 20}. You can achieve that by writing a custom
+ * deserializer:</p>
+ *
+ * <pre>
+ * class IdDeserializer implements JsonDeserializer&lt;Id&gt;() {
+ *   public Id deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
+ *       throws JsonParseException {
+ *     return new Id((Class)typeOfT, id.getValue());
+ *   }
+ * </pre>
+ *
+ * <p>You will also need to register {@code IdDeserializer} with Gson as follows:</p>
+ *
+ * <pre>
+ * Gson gson = new GsonBuilder().registerTypeAdapter(Id.class, new IdDeserializer()).create();
+ * </pre>
+ *
+ * <p>New applications should prefer {@link TypeAdapter}, whose streaming API
+ * is more efficient than this interface's tree API.
+ *
+ * @author Inderjeet Singh
+ * @author Joel Leitch
+ *
+ * @param <T> type for which the deserializer is being registered. It is possible that a
+ * deserializer may be asked to deserialize a specific generic type of the T.
+ */
+public interface JsonDeserializer<T> {
+
+  /**
+   * Gson invokes this call-back method during deserialization when it encounters a field of the
+   * specified type.
+   * <p>In the implementation of this call-back method, you should consider invoking
+   * {@link JsonDeserializationContext#deserialize(JsonElement, Type)} method to create objects
+   * for any non-trivial field of the returned object. However, you should never invoke it on the
+   * the same type passing {@code json} since that will cause an infinite loop (Gson will call your
+   * call-back method again).
+   *
+   * @param json The Json data being deserialized
+   * @param typeOfT The type of the Object to deserialize to
+   * @return a deserialized object of the specified type typeOfT which is a subclass of {@code T}
+   * @throws JsonParseException if json is not in the expected format of {@code typeofT}
+   */
+  public T deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
+      throws JsonParseException;
+}
diff --git a/gson/src/main/java/com/google/gson/JsonElement.java b/gson/src/main/java/com/google/gson/JsonElement.java
new file mode 100644
index 0000000000000000000000000000000000000000..2ab1a16095ab319281f16d0499452a8f38c40feb
--- /dev/null
+++ b/gson/src/main/java/com/google/gson/JsonElement.java
@@ -0,0 +1,322 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.gson;
+
+import com.google.gson.internal.Streams;
+import com.google.gson.stream.JsonWriter;
+import java.io.IOException;
+import java.io.StringWriter;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+
+/**
+ * A class representing an element of Json. It could either be a {@link JsonObject}, a
+ * {@link JsonArray}, a {@link JsonPrimitive} or a {@link JsonNull}.
+ *
+ * @author Inderjeet Singh
+ * @author Joel Leitch
+ */
+public abstract class JsonElement {
+  /**
+   * Returns a deep copy of this element. Immutable elements like primitives
+   * and nulls are not copied.
+   * @since 2.8.2
+   */
+  public abstract JsonElement deepCopy();
+
+  /**
+   * provides check for verifying if this element is an array or not.
+   *
+   * @return true if this element is of type {@link JsonArray}, false otherwise.
+   */
+  public boolean isJsonArray() {
+    return this instanceof JsonArray;
+  }
+
+  /**
+   * provides check for verifying if this element is a Json object or not.
+   *
+   * @return true if this element is of type {@link JsonObject}, false otherwise.
+   */
+  public boolean isJsonObject() {
+    return this instanceof JsonObject;
+  }
+
+  /**
+   * provides check for verifying if this element is a primitive or not.
+   *
+   * @return true if this element is of type {@link JsonPrimitive}, false otherwise.
+   */
+  public boolean isJsonPrimitive() {
+    return this instanceof JsonPrimitive;
+  }
+
+  /**
+   * provides check for verifying if this element represents a null value or not.
+   *
+   * @return true if this element is of type {@link JsonNull}, false otherwise.
+   * @since 1.2
+   */
+  public boolean isJsonNull() {
+    return this instanceof JsonNull;
+  }
+
+  /**
+   * convenience method to get this element as a {@link JsonObject}. If the element is of some
+   * other type, a {@link IllegalStateException} will result. Hence it is best to use this method
+   * after ensuring that this element is of the desired type by calling {@link #isJsonObject()}
+   * first.
+   *
+   * @return get this element as a {@link JsonObject}.
+   * @throws IllegalStateException if the element is of another type.
+   */
+  public JsonObject getAsJsonObject() {
+    if (isJsonObject()) {
+      return (JsonObject) this;
+    }
+    throw new IllegalStateException("Not a JSON Object: " + this);
+  }
+
+  /**
+   * convenience method to get this element as a {@link JsonArray}. If the element is of some
+   * other type, a {@link IllegalStateException} will result. Hence it is best to use this method
+   * after ensuring that this element is of the desired type by calling {@link #isJsonArray()}
+   * first.
+   *
+   * @return get this element as a {@link JsonArray}.
+   * @throws IllegalStateException if the element is of another type.
+   */
+  public JsonArray getAsJsonArray() {
+    if (isJsonArray()) {
+      return (JsonArray) this;
+    }
+    throw new IllegalStateException("Not a JSON Array: " + this);
+  }
+
+  /**
+   * convenience method to get this element as a {@link JsonPrimitive}. If the element is of some
+   * other type, a {@link IllegalStateException} will result. Hence it is best to use this method
+   * after ensuring that this element is of the desired type by calling {@link #isJsonPrimitive()}
+   * first.
+   *
+   * @return get this element as a {@link JsonPrimitive}.
+   * @throws IllegalStateException if the element is of another type.
+   */
+  public JsonPrimitive getAsJsonPrimitive() {
+    if (isJsonPrimitive()) {
+      return (JsonPrimitive) this;
+    }
+    throw new IllegalStateException("Not a JSON Primitive: " + this);
+  }
+
+  /**
+   * convenience method to get this element as a {@link JsonNull}. If the element is of some
+   * other type, a {@link IllegalStateException} will result. Hence it is best to use this method
+   * after ensuring that this element is of the desired type by calling {@link #isJsonNull()}
+   * first.
+   *
+   * @return get this element as a {@link JsonNull}.
+   * @throws IllegalStateException if the element is of another type.
+   * @since 1.2
+   */
+  public JsonNull getAsJsonNull() {
+    if (isJsonNull()) {
+      return (JsonNull) this;
+    }
+    throw new IllegalStateException("Not a JSON Null: " + this);
+  }
+
+  /**
+   * convenience method to get this element as a boolean value.
+   *
+   * @return get this element as a primitive boolean value.
+   * @throws ClassCastException if the element is of not a {@link JsonPrimitive} and is not a valid
+   * boolean value.
+   * @throws IllegalStateException if the element is of the type {@link JsonArray} but contains
+   * more than a single element.
+   */
+  public boolean getAsBoolean() {
+    throw new UnsupportedOperationException(getClass().getSimpleName());
+  }
+
+  /**
+   * convenience method to get this element as a {@link Number}.
+   *
+   * @return get this element as a {@link Number}.
+   * @throws ClassCastException if the element is of not a {@link JsonPrimitive} and is not a valid
+   * number.
+   * @throws IllegalStateException if the element is of the type {@link JsonArray} but contains
+   * more than a single element.
+   */
+  public Number getAsNumber() {
+    throw new UnsupportedOperationException(getClass().getSimpleName());
+  }
+
+  /**
+   * convenience method to get this element as a string value.
+   *
+   * @return get this element as a string value.
+   * @throws ClassCastException if the element is of not a {@link JsonPrimitive} and is not a valid
+   * string value.
+   * @throws IllegalStateException if the element is of the type {@link JsonArray} but contains
+   * more than a single element.
+   */
+  public String getAsString() {
+    throw new UnsupportedOperationException(getClass().getSimpleName());
+  }
+
+  /**
+   * convenience method to get this element as a primitive double value.
+   *
+   * @return get this element as a primitive double value.
+   * @throws ClassCastException if the element is of not a {@link JsonPrimitive} and is not a valid
+   * double value.
+   * @throws IllegalStateException if the element is of the type {@link JsonArray} but contains
+   * more than a single element.
+   */
+  public double getAsDouble() {
+    throw new UnsupportedOperationException(getClass().getSimpleName());
+  }
+
+  /**
+   * convenience method to get this element as a primitive float value.
+   *
+   * @return get this element as a primitive float value.
+   * @throws ClassCastException if the element is of not a {@link JsonPrimitive} and is not a valid
+   * float value.
+   * @throws IllegalStateException if the element is of the type {@link JsonArray} but contains
+   * more than a single element.
+   */
+  public float getAsFloat() {
+    throw new UnsupportedOperationException(getClass().getSimpleName());
+  }
+
+  /**
+   * convenience method to get this element as a primitive long value.
+   *
+   * @return get this element as a primitive long value.
+   * @throws ClassCastException if the element is of not a {@link JsonPrimitive} and is not a valid
+   * long value.
+   * @throws IllegalStateException if the element is of the type {@link JsonArray} but contains
+   * more than a single element.
+   */
+  public long getAsLong() {
+    throw new UnsupportedOperationException(getClass().getSimpleName());
+  }
+
+  /**
+   * convenience method to get this element as a primitive integer value.
+   *
+   * @return get this element as a primitive integer value.
+   * @throws ClassCastException if the element is of not a {@link JsonPrimitive} and is not a valid
+   * integer value.
+   * @throws IllegalStateException if the element is of the type {@link JsonArray} but contains
+   * more than a single element.
+   */
+  public int getAsInt() {
+    throw new UnsupportedOperationException(getClass().getSimpleName());
+  }
+
+  /**
+   * convenience method to get this element as a primitive byte value.
+   *
+   * @return get this element as a primitive byte value.
+   * @throws ClassCastException if the element is of not a {@link JsonPrimitive} and is not a valid
+   * byte value.
+   * @throws IllegalStateException if the element is of the type {@link JsonArray} but contains
+   * more than a single element.
+   * @since 1.3
+   */
+  public byte getAsByte() {
+    throw new UnsupportedOperationException(getClass().getSimpleName());
+  }
+
+  /**
+   * convenience method to get the first character of this element as a string or the first
+   * character of this array's first element as a string.
+   *
+   * @return the first character of the string.
+   * @throws ClassCastException if the element is of not a {@link JsonPrimitive} and is not a valid
+   * string value.
+   * @throws IllegalStateException if the element is of the type {@link JsonArray} but contains
+   * more than a single element.
+   * @since 1.3
+   * @deprecated This method is misleading, as it does not get this element as a char but rather as
+   * a string's first character.
+   */
+  @Deprecated
+  public char getAsCharacter() {
+    throw new UnsupportedOperationException(getClass().getSimpleName());
+  }
+
+  /**
+   * convenience method to get this element as a {@link BigDecimal}.
+   *
+   * @return get this element as a {@link BigDecimal}.
+   * @throws ClassCastException if the element is of not a {@link JsonPrimitive}.
+   * * @throws NumberFormatException if the element is not a valid {@link BigDecimal}.
+   * @throws IllegalStateException if the element is of the type {@link JsonArray} but contains
+   * more than a single element.
+   * @since 1.2
+   */
+  public BigDecimal getAsBigDecimal() {
+    throw new UnsupportedOperationException(getClass().getSimpleName());
+  }
+
+  /**
+   * convenience method to get this element as a {@link BigInteger}.
+   *
+   * @return get this element as a {@link BigInteger}.
+   * @throws ClassCastException if the element is of not a {@link JsonPrimitive}.
+   * @throws NumberFormatException if the element is not a valid {@link BigInteger}.
+   * @throws IllegalStateException if the element is of the type {@link JsonArray} but contains
+   * more than a single element.
+   * @since 1.2
+   */
+  public BigInteger getAsBigInteger() {
+    throw new UnsupportedOperationException(getClass().getSimpleName());
+  }
+
+  /**
+   * convenience method to get this element as a primitive short value.
+   *
+   * @return get this element as a primitive short value.
+   * @throws ClassCastException if the element is of not a {@link JsonPrimitive} and is not a valid
+   * short value.
+   * @throws IllegalStateException if the element is of the type {@link JsonArray} but contains
+   * more than a single element.
+   */
+  public short getAsShort() {
+    throw new UnsupportedOperationException(getClass().getSimpleName());
+  }
+
+  /**
+   * Returns a String representation of this element.
+   */
+  @Override
+  public String toString() {
+    try {
+      StringWriter stringWriter = new StringWriter();
+      JsonWriter jsonWriter = new JsonWriter(stringWriter);
+      jsonWriter.setLenient(true);
+      Streams.write(this, jsonWriter);
+      return stringWriter.toString();
+    } catch (IOException e) {
+      throw new AssertionError(e);
+    }
+  }
+}
diff --git a/gson/src/main/java/com/google/gson/JsonIOException.java b/gson/src/main/java/com/google/gson/JsonIOException.java
new file mode 100644
index 0000000000000000000000000000000000000000..dfeccd8ede6ce8563bfa4e6c642ae16cb1a54e40
--- /dev/null
+++ b/gson/src/main/java/com/google/gson/JsonIOException.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.google.gson;
+
+/**
+ * This exception is raised when Gson was unable to read an input stream
+ * or write to one.
+ * 
+ * @author Inderjeet Singh
+ * @author Joel Leitch
+ */
+public final class JsonIOException extends JsonParseException {
+  private static final long serialVersionUID = 1L;
+
+  public JsonIOException(String msg) {
+    super(msg);
+  }
+
+  public JsonIOException(String msg, Throwable cause) {
+    super(msg, cause);
+  }
+
+  /**
+   * Creates exception with the specified cause. Consider using
+   * {@link #JsonIOException(String, Throwable)} instead if you can describe what happened.
+   *
+   * @param cause root exception that caused this exception to be thrown.
+   */
+  public JsonIOException(Throwable cause) {
+    super(cause);
+  }
+}
diff --git a/gson/src/main/java/com/google/gson/JsonNull.java b/gson/src/main/java/com/google/gson/JsonNull.java
new file mode 100644
index 0000000000000000000000000000000000000000..dcc10a75f0cb011d5bf1293fd27e5dd4fab2409f
--- /dev/null
+++ b/gson/src/main/java/com/google/gson/JsonNull.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.gson;
+
+/**
+ * A class representing a Json {@code null} value.
+ *
+ * @author Inderjeet Singh
+ * @author Joel Leitch
+ * @since 1.2
+ */
+public final class JsonNull extends JsonElement {
+  /**
+   * singleton for JsonNull
+   *
+   * @since 1.8
+   */
+  public static final JsonNull INSTANCE = new JsonNull();
+
+  /**
+   * Creates a new JsonNull object.
+   * Deprecated since Gson version 1.8. Use {@link #INSTANCE} instead
+   */
+  @Deprecated
+  public JsonNull() {
+    // Do nothing
+  }
+
+  /**
+   * Returns the same instance since it is an immutable value
+   * @since 2.8.2
+   */
+  @Override
+  public JsonNull deepCopy() {
+    return INSTANCE;
+  }
+
+  /**
+   * All instances of JsonNull have the same hash code since they are indistinguishable
+   */
+  @Override
+  public int hashCode() {
+    return JsonNull.class.hashCode();
+  }
+
+  /**
+   * All instances of JsonNull are the same
+   */
+  @Override
+  public boolean equals(Object other) {
+    return this == other || other instanceof JsonNull;
+  }
+}
diff --git a/gson/src/main/java/com/google/gson/JsonObject.java b/gson/src/main/java/com/google/gson/JsonObject.java
new file mode 100644
index 0000000000000000000000000000000000000000..d4feb96e8440521866aaa45fd84467a6e463650b
--- /dev/null
+++ b/gson/src/main/java/com/google/gson/JsonObject.java
@@ -0,0 +1,205 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.gson;
+
+import com.google.gson.internal.LinkedTreeMap;
+
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * A class representing an object type in Json. An object consists of name-value pairs where names
+ * are strings, and values are any other type of {@link JsonElement}. This allows for a creating a
+ * tree of JsonElements. The member elements of this object are maintained in order they were added.
+ *
+ * @author Inderjeet Singh
+ * @author Joel Leitch
+ */
+public final class JsonObject extends JsonElement {
+  private final LinkedTreeMap<String, JsonElement> members =
+      new LinkedTreeMap<String, JsonElement>();
+
+  /**
+   * Creates a deep copy of this element and all its children
+   * @since 2.8.2
+   */
+  @Override
+  public JsonObject deepCopy() {
+    JsonObject result = new JsonObject();
+    for (Map.Entry<String, JsonElement> entry : members.entrySet()) {
+      result.add(entry.getKey(), entry.getValue().deepCopy());
+    }
+    return result;
+  }
+
+  /**
+   * Adds a member, which is a name-value pair, to self. The name must be a String, but the value
+   * can be an arbitrary JsonElement, thereby allowing you to build a full tree of JsonElements
+   * rooted at this node.
+   *
+   * @param property name of the member.
+   * @param value the member object.
+   */
+  public void add(String property, JsonElement value) {
+    members.put(property, value == null ? JsonNull.INSTANCE : value);
+  }
+
+  /**
+   * Removes the {@code property} from this {@link JsonObject}.
+   *
+   * @param property name of the member that should be removed.
+   * @return the {@link JsonElement} object that is being removed.
+   * @since 1.3
+   */
+  public JsonElement remove(String property) {
+    return members.remove(property);
+  }
+
+  /**
+   * Convenience method to add a primitive member. The specified value is converted to a
+   * JsonPrimitive of String.
+   *
+   * @param property name of the member.
+   * @param value the string value associated with the member.
+   */
+  public void addProperty(String property, String value) {
+    add(property, value == null ? JsonNull.INSTANCE : new JsonPrimitive(value));
+  }
+
+  /**
+   * Convenience method to add a primitive member. The specified value is converted to a
+   * JsonPrimitive of Number.
+   *
+   * @param property name of the member.
+   * @param value the number value associated with the member.
+   */
+  public void addProperty(String property, Number value) {
+    add(property, value == null ? JsonNull.INSTANCE : new JsonPrimitive(value));
+  }
+
+  /**
+   * Convenience method to add a boolean member. The specified value is converted to a
+   * JsonPrimitive of Boolean.
+   *
+   * @param property name of the member.
+   * @param value the number value associated with the member.
+   */
+  public void addProperty(String property, Boolean value) {
+    add(property, value == null ? JsonNull.INSTANCE : new JsonPrimitive(value));
+  }
+
+  /**
+   * Convenience method to add a char member. The specified value is converted to a
+   * JsonPrimitive of Character.
+   *
+   * @param property name of the member.
+   * @param value the number value associated with the member.
+   */
+  public void addProperty(String property, Character value) {
+    add(property, value == null ? JsonNull.INSTANCE : new JsonPrimitive(value));
+  }
+
+  /**
+   * Returns a set of members of this object. The set is ordered, and the order is in which the
+   * elements were added.
+   *
+   * @return a set of members of this object.
+   */
+  public Set<Map.Entry<String, JsonElement>> entrySet() {
+    return members.entrySet();
+  }
+
+  /**
+   * Returns a set of members key values.
+   *
+   * @return a set of member keys as Strings
+   * @since 2.8.1
+   */
+  public Set<String> keySet() {
+    return members.keySet();
+  }
+
+  /**
+   * Returns the number of key/value pairs in the object.
+   *
+   * @return the number of key/value pairs in the object.
+   */
+  public int size() {
+    return members.size();
+  }
+
+  /**
+   * Convenience method to check if a member with the specified name is present in this object.
+   *
+   * @param memberName name of the member that is being checked for presence.
+   * @return true if there is a member with the specified name, false otherwise.
+   */
+  public boolean has(String memberName) {
+    return members.containsKey(memberName);
+  }
+
+  /**
+   * Returns the member with the specified name.
+   *
+   * @param memberName name of the member that is being requested.
+   * @return the member matching the name. Null if no such member exists.
+   */
+  public JsonElement get(String memberName) {
+    return members.get(memberName);
+  }
+
+  /**
+   * Convenience method to get the specified member as a JsonPrimitive element.
+   *
+   * @param memberName name of the member being requested.
+   * @return the JsonPrimitive corresponding to the specified member.
+   */
+  public JsonPrimitive getAsJsonPrimitive(String memberName) {
+    return (JsonPrimitive) members.get(memberName);
+  }
+
+  /**
+   * Convenience method to get the specified member as a JsonArray.
+   *
+   * @param memberName name of the member being requested.
+   * @return the JsonArray corresponding to the specified member.
+   */
+  public JsonArray getAsJsonArray(String memberName) {
+    return (JsonArray) members.get(memberName);
+  }
+
+  /**
+   * Convenience method to get the specified member as a JsonObject.
+   *
+   * @param memberName name of the member being requested.
+   * @return the JsonObject corresponding to the specified member.
+   */
+  public JsonObject getAsJsonObject(String memberName) {
+    return (JsonObject) members.get(memberName);
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    return (o == this) || (o instanceof JsonObject
+        && ((JsonObject) o).members.equals(members));
+  }
+
+  @Override
+  public int hashCode() {
+    return members.hashCode();
+  }
+}
diff --git a/gson/src/main/java/com/google/gson/JsonParseException.java b/gson/src/main/java/com/google/gson/JsonParseException.java
new file mode 100644
index 0000000000000000000000000000000000000000..c1f264d76cfcc7da29553da2703cd1d4ca11b014
--- /dev/null
+++ b/gson/src/main/java/com/google/gson/JsonParseException.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.gson;
+
+/**
+ * This exception is raised if there is a serious issue that occurs during parsing of a Json
+ * string. One of the main usages for this class is for the Gson infrastructure. If the incoming
+ * Json is bad/malicious, an instance of this exception is raised.
+ *
+ * <p>This exception is a {@link RuntimeException} because it is exposed to the client. Using a
+ * {@link RuntimeException} avoids bad coding practices on the client side where they catch the
+ * exception and do nothing. It is often the case that you want to blow up if there is a parsing
+ * error (i.e. often clients do not know how to recover from a {@link JsonParseException}.</p>
+ *
+ * @author Inderjeet Singh
+ * @author Joel Leitch
+ */
+public class JsonParseException extends RuntimeException {
+  static final long serialVersionUID = -4086729973971783390L;
+
+  /**
+   * Creates exception with the specified message. If you are wrapping another exception, consider
+   * using {@link #JsonParseException(String, Throwable)} instead.
+   *
+   * @param msg error message describing a possible cause of this exception.
+   */
+  public JsonParseException(String msg) {
+    super(msg);
+  }
+
+  /**
+   * Creates exception with the specified message and cause.
+   *
+   * @param msg error message describing what happened.
+   * @param cause root exception that caused this exception to be thrown.
+   */
+  public JsonParseException(String msg, Throwable cause) {
+    super(msg, cause);
+  }
+
+  /**
+   * Creates exception with the specified cause. Consider using
+   * {@link #JsonParseException(String, Throwable)} instead if you can describe what happened.
+   *
+   * @param cause root exception that caused this exception to be thrown.
+   */
+  public JsonParseException(Throwable cause) {
+    super(cause);
+  }
+}
diff --git a/gson/src/main/java/com/google/gson/JsonParser.java b/gson/src/main/java/com/google/gson/JsonParser.java
new file mode 100644
index 0000000000000000000000000000000000000000..5f7dd1fc7547bf635e158077d987adb5c5c132f3
--- /dev/null
+++ b/gson/src/main/java/com/google/gson/JsonParser.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2009 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.google.gson;
+
+import java.io.IOException;
+import java.io.Reader;
+import java.io.StringReader;
+
+import com.google.gson.internal.Streams;
+import com.google.gson.stream.JsonReader;
+import com.google.gson.stream.JsonToken;
+import com.google.gson.stream.MalformedJsonException;
+
+/**
+ * A parser to parse Json into a parse tree of {@link JsonElement}s
+ *
+ * @author Inderjeet Singh
+ * @author Joel Leitch
+ * @since 1.3
+ */
+public final class JsonParser {
+  /** @deprecated No need to instantiate this class, use the static methods instead. */
+  @Deprecated
+  public JsonParser() {}
+
+  /**
+   * Parses the specified JSON string into a parse tree
+   *
+   * @param json JSON text
+   * @return a parse tree of {@link JsonElement}s corresponding to the specified JSON
+   * @throws JsonParseException if the specified text is not valid JSON
+   */
+  public static JsonElement parseString(String json) throws JsonSyntaxException {
+    return parseReader(new StringReader(json));
+  }
+
+  /**
+   * Parses the specified JSON string into a parse tree
+   *
+   * @param reader JSON text
+   * @return a parse tree of {@link JsonElement}s corresponding to the specified JSON
+   * @throws JsonParseException if the specified text is not valid JSON
+   */
+  public static JsonElement parseReader(Reader reader) throws JsonIOException, JsonSyntaxException {
+    try {
+      JsonReader jsonReader = new JsonReader(reader);
+      JsonElement element = parseReader(jsonReader);
+      if (!element.isJsonNull() && jsonReader.peek() != JsonToken.END_DOCUMENT) {
+        throw new JsonSyntaxException("Did not consume the entire document.");
+      }
+      return element;
+    } catch (MalformedJsonException e) {
+      throw new JsonSyntaxException(e);
+    } catch (IOException e) {
+      throw new JsonIOException(e);
+    } catch (NumberFormatException e) {
+      throw new JsonSyntaxException(e);
+    }
+  }
+
+  /**
+   * Returns the next value from the JSON stream as a parse tree.
+   *
+   * @throws JsonParseException if there is an IOException or if the specified
+   *     text is not valid JSON
+   */
+  public static JsonElement parseReader(JsonReader reader)
+      throws JsonIOException, JsonSyntaxException {
+    boolean lenient = reader.isLenient();
+    reader.setLenient(true);
+    try {
+      return Streams.parse(reader);
+    } catch (StackOverflowError e) {
+      throw new JsonParseException("Failed parsing JSON source: " + reader + " to Json", e);
+    } catch (OutOfMemoryError e) {
+      throw new JsonParseException("Failed parsing JSON source: " + reader + " to Json", e);
+    } finally {
+      reader.setLenient(lenient);
+    }
+  }
+
+  /** @deprecated Use {@link JsonParser#parseString} */
+  @Deprecated
+  public JsonElement parse(String json) throws JsonSyntaxException {
+    return parseString(json);
+  }
+
+  /** @deprecated Use {@link JsonParser#parseReader(Reader)} */
+  @Deprecated
+  public JsonElement parse(Reader json) throws JsonIOException, JsonSyntaxException {
+    return parseReader(json);
+  }
+
+  /** @deprecated Use {@link JsonParser#parseReader(JsonReader)} */
+  @Deprecated
+  public JsonElement parse(JsonReader json) throws JsonIOException, JsonSyntaxException {
+    return parseReader(json);
+  }
+}
diff --git a/gson/src/main/java/com/google/gson/JsonPrimitive.java b/gson/src/main/java/com/google/gson/JsonPrimitive.java
new file mode 100644
index 0000000000000000000000000000000000000000..5e95d5a82b0e1fd928c7030fe8ef3e4dee4390a0
--- /dev/null
+++ b/gson/src/main/java/com/google/gson/JsonPrimitive.java
@@ -0,0 +1,295 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.gson;
+
+import com.google.gson.internal.$Gson$Preconditions;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+
+import com.google.gson.internal.LazilyParsedNumber;
+
+/**
+ * A class representing a Json primitive value. A primitive value
+ * is either a String, a Java primitive, or a Java primitive
+ * wrapper type.
+ *
+ * @author Inderjeet Singh
+ * @author Joel Leitch
+ */
+public final class JsonPrimitive extends JsonElement {
+
+  private final Object value;
+
+  /**
+   * Create a primitive containing a boolean value.
+   *
+   * @param bool the value to create the primitive with.
+   */
+  public JsonPrimitive(Boolean bool) {
+    value = $Gson$Preconditions.checkNotNull(bool);
+  }
+
+  /**
+   * Create a primitive containing a {@link Number}.
+   *
+   * @param number the value to create the primitive with.
+   */
+  public JsonPrimitive(Number number) {
+    value = $Gson$Preconditions.checkNotNull(number);
+  }
+
+  /**
+   * Create a primitive containing a String value.
+   *
+   * @param string the value to create the primitive with.
+   */
+  public JsonPrimitive(String string) {
+    value = $Gson$Preconditions.checkNotNull(string);
+  }
+
+  /**
+   * Create a primitive containing a character. The character is turned into a one character String
+   * since Json only supports String.
+   *
+   * @param c the value to create the primitive with.
+   */
+  public JsonPrimitive(Character c) {
+    // convert characters to strings since in JSON, characters are represented as a single
+    // character string
+    value = $Gson$Preconditions.checkNotNull(c).toString();
+  }
+
+  /**
+   * Returns the same value as primitives are immutable.
+   * @since 2.8.2
+   */
+  @Override
+  public JsonPrimitive deepCopy() {
+    return this;
+  }
+
+  /**
+   * Check whether this primitive contains a boolean value.
+   *
+   * @return true if this primitive contains a boolean value, false otherwise.
+   */
+  public boolean isBoolean() {
+    return value instanceof Boolean;
+  }
+
+  /**
+   * convenience method to get this element as a boolean value.
+   *
+   * @return get this element as a primitive boolean value.
+   */
+  @Override
+  public boolean getAsBoolean() {
+    if (isBoolean()) {
+      return ((Boolean) value).booleanValue();
+    }
+	// Check to see if the value as a String is "true" in any case.
+    return Boolean.parseBoolean(getAsString());
+  }
+
+  /**
+   * Check whether this primitive contains a Number.
+   *
+   * @return true if this primitive contains a Number, false otherwise.
+   */
+  public boolean isNumber() {
+    return value instanceof Number;
+  }
+
+  /**
+   * convenience method to get this element as a Number.
+   *
+   * @return get this element as a Number.
+   * @throws NumberFormatException if the value contained is not a valid Number.
+   */
+  @Override
+  public Number getAsNumber() {
+    return value instanceof String ? new LazilyParsedNumber((String) value) : (Number) value;
+  }
+
+  /**
+   * Check whether this primitive contains a String value.
+   *
+   * @return true if this primitive contains a String value, false otherwise.
+   */
+  public boolean isString() {
+    return value instanceof String;
+  }
+
+  /**
+   * convenience method to get this element as a String.
+   *
+   * @return get this element as a String.
+   */
+  @Override
+  public String getAsString() {
+    if (isNumber()) {
+      return getAsNumber().toString();
+    } else if (isBoolean()) {
+      return ((Boolean) value).toString();
+    } else {
+      return (String) value;
+    }
+  }
+
+  /**
+   * convenience method to get this element as a primitive double.
+   *
+   * @return get this element as a primitive double.
+   * @throws NumberFormatException if the value contained is not a valid double.
+   */
+  @Override
+  public double getAsDouble() {
+    return isNumber() ? getAsNumber().doubleValue() : Double.parseDouble(getAsString());
+  }
+
+  /**
+   * convenience method to get this element as a {@link BigDecimal}.
+   *
+   * @return get this element as a {@link BigDecimal}.
+   * @throws NumberFormatException if the value contained is not a valid {@link BigDecimal}.
+   */
+  @Override
+  public BigDecimal getAsBigDecimal() {
+    return value instanceof BigDecimal ? (BigDecimal) value : new BigDecimal(value.toString());
+  }
+
+  /**
+   * convenience method to get this element as a {@link BigInteger}.
+   *
+   * @return get this element as a {@link BigInteger}.
+   * @throws NumberFormatException if the value contained is not a valid {@link BigInteger}.
+   */
+  @Override
+  public BigInteger getAsBigInteger() {
+    return value instanceof BigInteger ?
+        (BigInteger) value : new BigInteger(value.toString());
+  }
+
+  /**
+   * convenience method to get this element as a float.
+   *
+   * @return get this element as a float.
+   * @throws NumberFormatException if the value contained is not a valid float.
+   */
+  @Override
+  public float getAsFloat() {
+    return isNumber() ? getAsNumber().floatValue() : Float.parseFloat(getAsString());
+  }
+
+  /**
+   * convenience method to get this element as a primitive long.
+   *
+   * @return get this element as a primitive long.
+   * @throws NumberFormatException if the value contained is not a valid long.
+   */
+  @Override
+  public long getAsLong() {
+    return isNumber() ? getAsNumber().longValue() : Long.parseLong(getAsString());
+  }
+
+  /**
+   * convenience method to get this element as a primitive short.
+   *
+   * @return get this element as a primitive short.
+   * @throws NumberFormatException if the value contained is not a valid short value.
+   */
+  @Override
+  public short getAsShort() {
+    return isNumber() ? getAsNumber().shortValue() : Short.parseShort(getAsString());
+  }
+
+ /**
+  * convenience method to get this element as a primitive integer.
+  *
+  * @return get this element as a primitive integer.
+  * @throws NumberFormatException if the value contained is not a valid integer.
+  */
+  @Override
+  public int getAsInt() {
+    return isNumber() ? getAsNumber().intValue() : Integer.parseInt(getAsString());
+  }
+
+  @Override
+  public byte getAsByte() {
+    return isNumber() ? getAsNumber().byteValue() : Byte.parseByte(getAsString());
+  }
+
+  @Override
+  public char getAsCharacter() {
+    return getAsString().charAt(0);
+  }
+
+  @Override
+  public int hashCode() {
+    if (value == null) {
+      return 31;
+    }
+    // Using recommended hashing algorithm from Effective Java for longs and doubles
+    if (isIntegral(this)) {
+      long value = getAsNumber().longValue();
+      return (int) (value ^ (value >>> 32));
+    }
+    if (value instanceof Number) {
+      long value = Double.doubleToLongBits(getAsNumber().doubleValue());
+      return (int) (value ^ (value >>> 32));
+    }
+    return value.hashCode();
+  }
+
+  @Override
+  public boolean equals(Object obj) {
+    if (this == obj) {
+      return true;
+    }
+    if (obj == null || getClass() != obj.getClass()) {
+      return false;
+    }
+    JsonPrimitive other = (JsonPrimitive)obj;
+    if (value == null) {
+      return other.value == null;
+    }
+    if (isIntegral(this) && isIntegral(other)) {
+      return getAsNumber().longValue() == other.getAsNumber().longValue();
+    }
+    if (value instanceof Number && other.value instanceof Number) {
+      double a = getAsNumber().doubleValue();
+      // Java standard types other than double return true for two NaN. So, need
+      // special handling for double.
+      double b = other.getAsNumber().doubleValue();
+      return a == b || (Double.isNaN(a) && Double.isNaN(b));
+    }
+    return value.equals(other.value);
+  }
+
+  /**
+   * Returns true if the specified number is an integral type
+   * (Long, Integer, Short, Byte, BigInteger)
+   */
+  private static boolean isIntegral(JsonPrimitive primitive) {
+    if (primitive.value instanceof Number) {
+      Number number = (Number) primitive.value;
+      return number instanceof BigInteger || number instanceof Long || number instanceof Integer
+          || number instanceof Short || number instanceof Byte;
+    }
+    return false;
+  }
+}
diff --git a/gson/src/main/java/com/google/gson/JsonSerializationContext.java b/gson/src/main/java/com/google/gson/JsonSerializationContext.java
new file mode 100644
index 0000000000000000000000000000000000000000..ca3ec4f901669298b749aea11397c2686f5be569
--- /dev/null
+++ b/gson/src/main/java/com/google/gson/JsonSerializationContext.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.gson;
+
+import java.lang.reflect.Type;
+
+/**
+ * Context for serialization that is passed to a custom serializer during invocation of its
+ * {@link JsonSerializer#serialize(Object, Type, JsonSerializationContext)} method.
+ *
+ * @author Inderjeet Singh
+ * @author Joel Leitch
+ */
+public interface JsonSerializationContext {
+
+  /**
+   * Invokes default serialization on the specified object.
+   *
+   * @param src the object that needs to be serialized.
+   * @return a tree of {@link JsonElement}s corresponding to the serialized form of {@code src}.
+   */
+  public JsonElement serialize(Object src);
+
+  /**
+   * Invokes default serialization on the specified object passing the specific type information.
+   * It should never be invoked on the element received as a parameter of the
+   * {@link JsonSerializer#serialize(Object, Type, JsonSerializationContext)} method. Doing
+   * so will result in an infinite loop since Gson will in-turn call the custom serializer again.
+   *
+   * @param src the object that needs to be serialized.
+   * @param typeOfSrc the actual genericized type of src object.
+   * @return a tree of {@link JsonElement}s corresponding to the serialized form of {@code src}.
+   */
+  public JsonElement serialize(Object src, Type typeOfSrc);
+}
diff --git a/gson/src/main/java/com/google/gson/JsonSerializer.java b/gson/src/main/java/com/google/gson/JsonSerializer.java
new file mode 100644
index 0000000000000000000000000000000000000000..1babeb794ff4c3dc905045b284518532405fb097
--- /dev/null
+++ b/gson/src/main/java/com/google/gson/JsonSerializer.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.gson;
+
+import java.lang.reflect.Type;
+
+/**
+ * Interface representing a custom serializer for Json. You should write a custom serializer, if
+ * you are not happy with the default serialization done by Gson. You will also need to register
+ * this serializer through {@link com.google.gson.GsonBuilder#registerTypeAdapter(Type, Object)}.
+ *
+ * <p>Let us look at example where defining a serializer will be useful. The {@code Id} class
+ * defined below has two fields: {@code clazz} and {@code value}.</p>
+ *
+ * <p><pre>
+ * public class Id&lt;T&gt; {
+ *   private final Class&lt;T&gt; clazz;
+ *   private final long value;
+ *
+ *   public Id(Class&lt;T&gt; clazz, long value) {
+ *     this.clazz = clazz;
+ *     this.value = value;
+ *   }
+ *
+ *   public long getValue() {
+ *     return value;
+ *   }
+ * }
+ * </pre>
+ *
+ * <p>The default serialization of {@code Id(com.foo.MyObject.class, 20L)} will be
+ * <code>{"clazz":com.foo.MyObject,"value":20}</code>. Suppose, you just want the output to be
+ * the value instead, which is {@code 20} in this case. You can achieve that by writing a custom
+ * serializer:</p>
+ *
+ * <p><pre>
+ * class IdSerializer implements JsonSerializer&lt;Id&gt;() {
+ *   public JsonElement serialize(Id id, Type typeOfId, JsonSerializationContext context) {
+ *     return new JsonPrimitive(id.getValue());
+ *   }
+ * }
+ * </pre>
+ *
+ * <p>You will also need to register {@code IdSerializer} with Gson as follows:</p>
+ * <pre>
+ * Gson gson = new GsonBuilder().registerTypeAdapter(Id.class, new IdSerializer()).create();
+ * </pre>
+ *
+ * <p>New applications should prefer {@link TypeAdapter}, whose streaming API
+ * is more efficient than this interface's tree API.
+ *
+ * @author Inderjeet Singh
+ * @author Joel Leitch
+ *
+ * @param <T> type for which the serializer is being registered. It is possible that a serializer
+ *        may be asked to serialize a specific generic type of the T.
+ */
+public interface JsonSerializer<T> {
+
+  /**
+   * Gson invokes this call-back method during serialization when it encounters a field of the
+   * specified type.
+   *
+   * <p>In the implementation of this call-back method, you should consider invoking
+   * {@link JsonSerializationContext#serialize(Object, Type)} method to create JsonElements for any
+   * non-trivial field of the {@code src} object. However, you should never invoke it on the
+   * {@code src} object itself since that will cause an infinite loop (Gson will call your
+   * call-back method again).</p>
+   *
+   * @param src the object that needs to be converted to Json.
+   * @param typeOfSrc the actual type (fully genericized version) of the source object.
+   * @return a JsonElement corresponding to the specified object.
+   */
+  public JsonElement serialize(T src, Type typeOfSrc, JsonSerializationContext context);
+}
diff --git a/gson/src/main/java/com/google/gson/JsonStreamParser.java b/gson/src/main/java/com/google/gson/JsonStreamParser.java
new file mode 100644
index 0000000000000000000000000000000000000000..1c0c9b9d6dd7d26cff0230b4a7bc368096bfd2aa
--- /dev/null
+++ b/gson/src/main/java/com/google/gson/JsonStreamParser.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2009 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.google.gson;
+
+import java.io.EOFException;
+import java.io.IOException;
+import java.io.Reader;
+import java.io.StringReader;
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+
+import com.google.gson.internal.Streams;
+import com.google.gson.stream.JsonReader;
+import com.google.gson.stream.JsonToken;
+import com.google.gson.stream.MalformedJsonException;
+
+/**
+ * A streaming parser that allows reading of multiple {@link JsonElement}s from the specified reader
+ * asynchronously.
+ * 
+ * <p>This class is conditionally thread-safe (see Item 70, Effective Java second edition). To
+ * properly use this class across multiple threads, you will need to add some external
+ * synchronization. For example:
+ * 
+ * <pre>
+ * JsonStreamParser parser = new JsonStreamParser("['first'] {'second':10} 'third'");
+ * JsonElement element;
+ * synchronized (parser) {  // synchronize on an object shared by threads
+ *   if (parser.hasNext()) {
+ *     element = parser.next();
+ *   }
+ * }
+ * </pre>
+ *
+ * @author Inderjeet Singh
+ * @author Joel Leitch
+ * @since 1.4
+ */
+public final class JsonStreamParser implements Iterator<JsonElement> {
+  private final JsonReader parser;
+  private final Object lock;
+
+  /**
+   * @param json The string containing JSON elements concatenated to each other.
+   * @since 1.4
+   */
+  public JsonStreamParser(String json) {
+    this(new StringReader(json));      
+  }
+  
+  /**
+   * @param reader The data stream containing JSON elements concatenated to each other.
+   * @since 1.4
+   */
+  public JsonStreamParser(Reader reader) {
+    parser = new JsonReader(reader);
+    parser.setLenient(true);
+    lock = new Object();
+  }
+  
+  /**
+   * Returns the next available {@link JsonElement} on the reader. Null if none available.
+   * 
+   * @return the next available {@link JsonElement} on the reader. Null if none available.
+   * @throws JsonParseException if the incoming stream is malformed JSON.
+   * @since 1.4
+   */
+  public JsonElement next() throws JsonParseException {
+    if (!hasNext()) {
+      throw new NoSuchElementException();
+    }
+    
+    try {
+      return Streams.parse(parser);
+    } catch (StackOverflowError e) {
+      throw new JsonParseException("Failed parsing JSON source to Json", e);
+    } catch (OutOfMemoryError e) {
+      throw new JsonParseException("Failed parsing JSON source to Json", e);
+    } catch (JsonParseException e) {
+      throw e.getCause() instanceof EOFException ? new NoSuchElementException() : e;
+    }
+  }
+
+  /**
+   * Returns true if a {@link JsonElement} is available on the input for consumption
+   * @return true if a {@link JsonElement} is available on the input, false otherwise
+   * @since 1.4
+   */
+  public boolean hasNext() {
+    synchronized (lock) {
+      try {
+        return parser.peek() != JsonToken.END_DOCUMENT;
+      } catch (MalformedJsonException e) {
+        throw new JsonSyntaxException(e);
+      } catch (IOException e) {
+        throw new JsonIOException(e);
+      }
+    }
+  }
+
+  /**
+   * This optional {@link Iterator} method is not relevant for stream parsing and hence is not
+   * implemented.
+   * @since 1.4
+   */
+  public void remove() {
+    throw new UnsupportedOperationException();
+  }
+}
diff --git a/gson/src/main/java/com/google/gson/JsonSyntaxException.java b/gson/src/main/java/com/google/gson/JsonSyntaxException.java
new file mode 100644
index 0000000000000000000000000000000000000000..17c1d3d3a6233a01f99931c5fb5026117a602d42
--- /dev/null
+++ b/gson/src/main/java/com/google/gson/JsonSyntaxException.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.google.gson;
+
+/**
+ * This exception is raised when Gson attempts to read (or write) a malformed
+ * JSON element.
+ *
+ * @author Inderjeet Singh
+ * @author Joel Leitch
+ */
+public final class JsonSyntaxException extends JsonParseException {
+
+  private static final long serialVersionUID = 1L;
+
+  public JsonSyntaxException(String msg) {
+    super(msg);
+  }
+
+  public JsonSyntaxException(String msg, Throwable cause) {
+    super(msg, cause);
+  }
+
+  /**
+   * Creates exception with the specified cause. Consider using
+   * {@link #JsonSyntaxException(String, Throwable)} instead if you can
+   * describe what actually happened.
+   *
+   * @param cause root exception that caused this exception to be thrown.
+   */
+  public JsonSyntaxException(Throwable cause) {
+    super(cause);
+  }
+}
diff --git a/gson/src/main/java/com/google/gson/LongSerializationPolicy.java b/gson/src/main/java/com/google/gson/LongSerializationPolicy.java
new file mode 100644
index 0000000000000000000000000000000000000000..bb2b6666ea3bed352b204e5a402fe139a3625320
--- /dev/null
+++ b/gson/src/main/java/com/google/gson/LongSerializationPolicy.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2009 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.gson;
+
+/**
+ * Defines the expected format for a {@code long} or {@code Long} type when its serialized.
+ *
+ * @since 1.3
+ *
+ * @author Inderjeet Singh
+ * @author Joel Leitch
+ */
+public enum LongSerializationPolicy {
+  /**
+   * This is the "default" serialization policy that will output a {@code long} object as a JSON
+   * number. For example, assume an object has a long field named "f" then the serialized output
+   * would be:
+   * {@code {"f":123}}.
+   */
+  DEFAULT() {
+    @Override public JsonElement serialize(Long value) {
+      return new JsonPrimitive(value);
+    }
+  },
+  
+  /**
+   * Serializes a long value as a quoted string. For example, assume an object has a long field 
+   * named "f" then the serialized output would be:
+   * {@code {"f":"123"}}.
+   */
+  STRING() {
+    @Override public JsonElement serialize(Long value) {
+      return new JsonPrimitive(String.valueOf(value));
+    }
+  };
+  
+  /**
+   * Serialize this {@code value} using this serialization policy.
+   *
+   * @param value the long value to be serialized into a {@link JsonElement}
+   * @return the serialized version of {@code value}
+   */
+  public abstract JsonElement serialize(Long value);
+}
diff --git a/gson/src/main/java/com/google/gson/TypeAdapter.java b/gson/src/main/java/com/google/gson/TypeAdapter.java
new file mode 100644
index 0000000000000000000000000000000000000000..4646d271d7e5446a69907bc724db6f5f34118347
--- /dev/null
+++ b/gson/src/main/java/com/google/gson/TypeAdapter.java
@@ -0,0 +1,290 @@
+/*
+ * Copyright (C) 2011 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.gson;
+
+import com.google.gson.internal.bind.JsonTreeWriter;
+import com.google.gson.internal.bind.JsonTreeReader;
+import com.google.gson.stream.JsonReader;
+import com.google.gson.stream.JsonToken;
+import com.google.gson.stream.JsonWriter;
+import java.io.IOException;
+import java.io.Reader;
+import java.io.StringReader;
+import java.io.StringWriter;
+import java.io.Writer;
+
+/**
+ * Converts Java objects to and from JSON.
+ *
+ * <h3>Defining a type's JSON form</h3>
+ * By default Gson converts application classes to JSON using its built-in type
+ * adapters. If Gson's default JSON conversion isn't appropriate for a type,
+ * extend this class to customize the conversion. Here's an example of a type
+ * adapter for an (X,Y) coordinate point: <pre>   {@code
+ *
+ *   public class PointAdapter extends TypeAdapter<Point> {
+ *     public Point read(JsonReader reader) throws IOException {
+ *       if (reader.peek() == JsonToken.NULL) {
+ *         reader.nextNull();
+ *         return null;
+ *       }
+ *       String xy = reader.nextString();
+ *       String[] parts = xy.split(",");
+ *       int x = Integer.parseInt(parts[0]);
+ *       int y = Integer.parseInt(parts[1]);
+ *       return new Point(x, y);
+ *     }
+ *     public void write(JsonWriter writer, Point value) throws IOException {
+ *       if (value == null) {
+ *         writer.nullValue();
+ *         return;
+ *       }
+ *       String xy = value.getX() + "," + value.getY();
+ *       writer.value(xy);
+ *     }
+ *   }}</pre>
+ * With this type adapter installed, Gson will convert {@code Points} to JSON as
+ * strings like {@code "5,8"} rather than objects like {@code {"x":5,"y":8}}. In
+ * this case the type adapter binds a rich Java class to a compact JSON value.
+ *
+ * <p>The {@link #read(JsonReader) read()} method must read exactly one value
+ * and {@link #write(JsonWriter,Object) write()} must write exactly one value.
+ * For primitive types this is means readers should make exactly one call to
+ * {@code nextBoolean()}, {@code nextDouble()}, {@code nextInt()}, {@code
+ * nextLong()}, {@code nextString()} or {@code nextNull()}. Writers should make
+ * exactly one call to one of <code>value()</code> or <code>nullValue()</code>.
+ * For arrays, type adapters should start with a call to {@code beginArray()},
+ * convert all elements, and finish with a call to {@code endArray()}. For
+ * objects, they should start with {@code beginObject()}, convert the object,
+ * and finish with {@code endObject()}. Failing to convert a value or converting
+ * too many values may cause the application to crash.
+ *
+ * <p>Type adapters should be prepared to read null from the stream and write it
+ * to the stream. Alternatively, they should use {@link #nullSafe()} method while
+ * registering the type adapter with Gson. If your {@code Gson} instance
+ * has been configured to {@link GsonBuilder#serializeNulls()}, these nulls will be
+ * written to the final document. Otherwise the value (and the corresponding name
+ * when writing to a JSON object) will be omitted automatically. In either case
+ * your type adapter must handle null.
+ *
+ * <p>To use a custom type adapter with Gson, you must <i>register</i> it with a
+ * {@link GsonBuilder}: <pre>   {@code
+ *
+ *   GsonBuilder builder = new GsonBuilder();
+ *   builder.registerTypeAdapter(Point.class, new PointAdapter());
+ *   // if PointAdapter didn't check for nulls in its read/write methods, you should instead use
+ *   // builder.registerTypeAdapter(Point.class, new PointAdapter().nullSafe());
+ *   ...
+ *   Gson gson = builder.create();
+ * }</pre>
+ *
+ * @since 2.1
+ */
+// non-Javadoc:
+//
+// <h3>JSON Conversion</h3>
+// <p>A type adapter registered with Gson is automatically invoked while serializing
+// or deserializing JSON. However, you can also use type adapters directly to serialize
+// and deserialize JSON. Here is an example for deserialization: <pre>   {@code
+//
+//   String json = "{'origin':'0,0','points':['1,2','3,4']}";
+//   TypeAdapter<Graph> graphAdapter = gson.getAdapter(Graph.class);
+//   Graph graph = graphAdapter.fromJson(json);
+// }</pre>
+// And an example for serialization: <pre>   {@code
+//
+//   Graph graph = new Graph(...);
+//   TypeAdapter<Graph> graphAdapter = gson.getAdapter(Graph.class);
+//   String json = graphAdapter.toJson(graph);
+// }</pre>
+//
+// <p>Type adapters are <strong>type-specific</strong>. For example, a {@code
+// TypeAdapter<Date>} can convert {@code Date} instances to JSON and JSON to
+// instances of {@code Date}, but cannot convert any other types.
+//
+public abstract class TypeAdapter<T> {
+
+  /**
+   * Writes one JSON value (an array, object, string, number, boolean or null)
+   * for {@code value}.
+   *
+   * @param value the Java object to write. May be null.
+   */
+  public abstract void write(JsonWriter out, T value) throws IOException;
+
+  /**
+   * Converts {@code value} to a JSON document and writes it to {@code out}.
+   * Unlike Gson's similar {@link Gson#toJson(JsonElement, Appendable) toJson}
+   * method, this write is strict. Create a {@link
+   * JsonWriter#setLenient(boolean) lenient} {@code JsonWriter} and call
+   * {@link #write(com.google.gson.stream.JsonWriter, Object)} for lenient
+   * writing.
+   *
+   * @param value the Java object to convert. May be null.
+   * @since 2.2
+   */
+  public final void toJson(Writer out, T value) throws IOException {
+    JsonWriter writer = new JsonWriter(out);
+    write(writer, value);
+  }
+
+  /**
+   * This wrapper method is used to make a type adapter null tolerant. In general, a
+   * type adapter is required to handle nulls in write and read methods. Here is how this
+   * is typically done:<br>
+   * <pre>   {@code
+   *
+   * Gson gson = new GsonBuilder().registerTypeAdapter(Foo.class,
+   *   new TypeAdapter<Foo>() {
+   *     public Foo read(JsonReader in) throws IOException {
+   *       if (in.peek() == JsonToken.NULL) {
+   *         in.nextNull();
+   *         return null;
+   *       }
+   *       // read a Foo from in and return it
+   *     }
+   *     public void write(JsonWriter out, Foo src) throws IOException {
+   *       if (src == null) {
+   *         out.nullValue();
+   *         return;
+   *       }
+   *       // write src as JSON to out
+   *     }
+   *   }).create();
+   * }</pre>
+   * You can avoid this boilerplate handling of nulls by wrapping your type adapter with
+   * this method. Here is how we will rewrite the above example:
+   * <pre>   {@code
+   *
+   * Gson gson = new GsonBuilder().registerTypeAdapter(Foo.class,
+   *   new TypeAdapter<Foo>() {
+   *     public Foo read(JsonReader in) throws IOException {
+   *       // read a Foo from in and return it
+   *     }
+   *     public void write(JsonWriter out, Foo src) throws IOException {
+   *       // write src as JSON to out
+   *     }
+   *   }.nullSafe()).create();
+   * }</pre>
+   * Note that we didn't need to check for nulls in our type adapter after we used nullSafe.
+   */
+  public final TypeAdapter<T> nullSafe() {
+    return new TypeAdapter<T>() {
+      @Override public void write(JsonWriter out, T value) throws IOException {
+        if (value == null) {
+          out.nullValue();
+        } else {
+          TypeAdapter.this.write(out, value);
+        }
+      }
+      @Override public T read(JsonReader reader) throws IOException {
+        if (reader.peek() == JsonToken.NULL) {
+          reader.nextNull();
+          return null;
+        }
+        return TypeAdapter.this.read(reader);
+      }
+    };
+  }
+
+  /**
+   * Converts {@code value} to a JSON document. Unlike Gson's similar {@link
+   * Gson#toJson(Object) toJson} method, this write is strict. Create a {@link
+   * JsonWriter#setLenient(boolean) lenient} {@code JsonWriter} and call
+   * {@link #write(com.google.gson.stream.JsonWriter, Object)} for lenient
+   * writing.
+   *
+   * @param value the Java object to convert. May be null.
+   * @since 2.2
+   */
+  public final String toJson(T value) {
+    StringWriter stringWriter = new StringWriter();
+    try {
+      toJson(stringWriter, value);
+    } catch (IOException e) {
+      throw new AssertionError(e); // No I/O writing to a StringWriter.
+    }
+    return stringWriter.toString();
+  }
+
+  /**
+   * Converts {@code value} to a JSON tree.
+   *
+   * @param value the Java object to convert. May be null.
+   * @return the converted JSON tree. May be {@link JsonNull}.
+   * @since 2.2
+   */
+  public final JsonElement toJsonTree(T value) {
+    try {
+      JsonTreeWriter jsonWriter = new JsonTreeWriter();
+      write(jsonWriter, value);
+      return jsonWriter.get();
+    } catch (IOException e) {
+      throw new JsonIOException(e);
+    }
+  }
+
+  /**
+   * Reads one JSON value (an array, object, string, number, boolean or null)
+   * and converts it to a Java object. Returns the converted object.
+   *
+   * @return the converted Java object. May be null.
+   */
+  public abstract T read(JsonReader in) throws IOException;
+
+  /**
+   * Converts the JSON document in {@code in} to a Java object. Unlike Gson's
+   * similar {@link Gson#fromJson(java.io.Reader, Class) fromJson} method, this
+   * read is strict. Create a {@link JsonReader#setLenient(boolean) lenient}
+   * {@code JsonReader} and call {@link #read(JsonReader)} for lenient reading.
+   *
+   * @return the converted Java object. May be null.
+   * @since 2.2
+   */
+  public final T fromJson(Reader in) throws IOException {
+    JsonReader reader = new JsonReader(in);
+    return read(reader);
+  }
+
+  /**
+   * Converts the JSON document in {@code json} to a Java object. Unlike Gson's
+   * similar {@link Gson#fromJson(String, Class) fromJson} method, this read is
+   * strict. Create a {@link JsonReader#setLenient(boolean) lenient} {@code
+   * JsonReader} and call {@link #read(JsonReader)} for lenient reading.
+   *
+   * @return the converted Java object. May be null.
+   * @since 2.2
+   */
+  public final T fromJson(String json) throws IOException {
+    return fromJson(new StringReader(json));
+  }
+
+  /**
+   * Converts {@code jsonTree} to a Java object.
+   *
+   * @param jsonTree the Java object to convert. May be {@link JsonNull}.
+   * @since 2.2
+   */
+  public final T fromJsonTree(JsonElement jsonTree) {
+    try {
+      JsonReader jsonReader = new JsonTreeReader(jsonTree);
+      return read(jsonReader);
+    } catch (IOException e) {
+      throw new JsonIOException(e);
+    }
+  }
+}
diff --git a/gson/src/main/java/com/google/gson/TypeAdapterFactory.java b/gson/src/main/java/com/google/gson/TypeAdapterFactory.java
new file mode 100644
index 0000000000000000000000000000000000000000..e12a72dccd05d2d6578f7b7e1c116023a78ea624
--- /dev/null
+++ b/gson/src/main/java/com/google/gson/TypeAdapterFactory.java
@@ -0,0 +1,170 @@
+/*
+ * Copyright (C) 2011 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.gson;
+
+import com.google.gson.reflect.TypeToken;
+
+/**
+ * Creates type adapters for set of related types. Type adapter factories are
+ * most useful when several types share similar structure in their JSON form.
+ *
+ * <h3>Example: Converting enums to lowercase</h3>
+ * In this example, we implement a factory that creates type adapters for all
+ * enums. The type adapters will write enums in lowercase, despite the fact
+ * that they're defined in {@code CONSTANT_CASE} in the corresponding Java
+ * model: <pre>   {@code
+ *
+ *   public class LowercaseEnumTypeAdapterFactory implements TypeAdapterFactory {
+ *     public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
+ *       Class<T> rawType = (Class<T>) type.getRawType();
+ *       if (!rawType.isEnum()) {
+ *         return null;
+ *       }
+ *
+ *       final Map<String, T> lowercaseToConstant = new HashMap<String, T>();
+ *       for (T constant : rawType.getEnumConstants()) {
+ *         lowercaseToConstant.put(toLowercase(constant), constant);
+ *       }
+ *
+ *       return new TypeAdapter<T>() {
+ *         public void write(JsonWriter out, T value) throws IOException {
+ *           if (value == null) {
+ *             out.nullValue();
+ *           } else {
+ *             out.value(toLowercase(value));
+ *           }
+ *         }
+ *
+ *         public T read(JsonReader reader) throws IOException {
+ *           if (reader.peek() == JsonToken.NULL) {
+ *             reader.nextNull();
+ *             return null;
+ *           } else {
+ *             return lowercaseToConstant.get(reader.nextString());
+ *           }
+ *         }
+ *       };
+ *     }
+ *
+ *     private String toLowercase(Object o) {
+ *       return o.toString().toLowerCase(Locale.US);
+ *     }
+ *   }
+ * }</pre>
+ *
+ * <p>Type adapter factories select which types they provide type adapters
+ * for. If a factory cannot support a given type, it must return null when
+ * that type is passed to {@link #create}. Factories should expect {@code
+ * create()} to be called on them for many types and should return null for
+ * most of those types. In the above example the factory returns null for
+ * calls to {@code create()} where {@code type} is not an enum.
+ *
+ * <p>A factory is typically called once per type, but the returned type
+ * adapter may be used many times. It is most efficient to do expensive work
+ * like reflection in {@code create()} so that the type adapter's {@code
+ * read()} and {@code write()} methods can be very fast. In this example the
+ * mapping from lowercase name to enum value is computed eagerly.
+ *
+ * <p>As with type adapters, factories must be <i>registered</i> with a {@link
+ * com.google.gson.GsonBuilder} for them to take effect: <pre>   {@code
+ *
+ *  GsonBuilder builder = new GsonBuilder();
+ *  builder.registerTypeAdapterFactory(new LowercaseEnumTypeAdapterFactory());
+ *  ...
+ *  Gson gson = builder.create();
+ * }</pre>
+ * If multiple factories support the same type, the factory registered earlier
+ * takes precedence.
+ *
+ * <h3>Example: composing other type adapters</h3>
+ * In this example we implement a factory for Guava's {@code Multiset}
+ * collection type. The factory can be used to create type adapters for
+ * multisets of any element type: the type adapter for {@code
+ * Multiset<String>} is different from the type adapter for {@code
+ * Multiset<URL>}.
+ *
+ * <p>The type adapter <i>delegates</i> to another type adapter for the
+ * multiset elements. It figures out the element type by reflecting on the
+ * multiset's type token. A {@code Gson} is passed in to {@code create} for
+ * just this purpose: <pre>   {@code
+ *
+ *   public class MultisetTypeAdapterFactory implements TypeAdapterFactory {
+ *     public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) {
+ *       Type type = typeToken.getType();
+ *       if (typeToken.getRawType() != Multiset.class
+ *           || !(type instanceof ParameterizedType)) {
+ *         return null;
+ *       }
+ *
+ *       Type elementType = ((ParameterizedType) type).getActualTypeArguments()[0];
+ *       TypeAdapter<?> elementAdapter = gson.getAdapter(TypeToken.get(elementType));
+ *       return (TypeAdapter<T>) newMultisetAdapter(elementAdapter);
+ *     }
+ *
+ *     private <E> TypeAdapter<Multiset<E>> newMultisetAdapter(
+ *         final TypeAdapter<E> elementAdapter) {
+ *       return new TypeAdapter<Multiset<E>>() {
+ *         public void write(JsonWriter out, Multiset<E> value) throws IOException {
+ *           if (value == null) {
+ *             out.nullValue();
+ *             return;
+ *           }
+ *
+ *           out.beginArray();
+ *           for (Multiset.Entry<E> entry : value.entrySet()) {
+ *             out.value(entry.getCount());
+ *             elementAdapter.write(out, entry.getElement());
+ *           }
+ *           out.endArray();
+ *         }
+ *
+ *         public Multiset<E> read(JsonReader in) throws IOException {
+ *           if (in.peek() == JsonToken.NULL) {
+ *             in.nextNull();
+ *             return null;
+ *           }
+ *
+ *           Multiset<E> result = LinkedHashMultiset.create();
+ *           in.beginArray();
+ *           while (in.hasNext()) {
+ *             int count = in.nextInt();
+ *             E element = elementAdapter.read(in);
+ *             result.add(element, count);
+ *           }
+ *           in.endArray();
+ *           return result;
+ *         }
+ *       };
+ *     }
+ *   }
+ * }</pre>
+ * Delegating from one type adapter to another is extremely powerful; it's
+ * the foundation of how Gson converts Java objects and collections. Whenever
+ * possible your factory should retrieve its delegate type adapter in the
+ * {@code create()} method; this ensures potentially-expensive type adapter
+ * creation happens only once.
+ *
+ * @since 2.1
+ */
+public interface TypeAdapterFactory {
+
+  /**
+   * Returns a type adapter for {@code type}, or null if this factory doesn't
+   * support {@code type}.
+   */
+  <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type);
+}
diff --git a/gson/src/main/java/com/google/gson/annotations/Expose.java b/gson/src/main/java/com/google/gson/annotations/Expose.java
new file mode 100644
index 0000000000000000000000000000000000000000..cbf20e7f7e6f8ed125355e5e72197933dfc47f50
--- /dev/null
+++ b/gson/src/main/java/com/google/gson/annotations/Expose.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.gson.annotations;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * An annotation that indicates this member should be exposed for JSON
+ * serialization or deserialization.
+ *
+ * <p>This annotation has no effect unless you build {@link com.google.gson.Gson}
+ * with a {@link com.google.gson.GsonBuilder} and invoke
+ * {@link com.google.gson.GsonBuilder#excludeFieldsWithoutExposeAnnotation()}
+ * method.</p>
+ *
+ * <p>Here is an example of how this annotation is meant to be used:
+ * <p><pre>
+ * public class User {
+ *   &#64;Expose private String firstName;
+ *   &#64;Expose(serialize = false) private String lastName;
+ *   &#64;Expose (serialize = false, deserialize = false) private String emailAddress;
+ *   private String password;
+ * }
+ * </pre>
+ * If you created Gson with {@code new Gson()}, the {@code toJson()} and {@code fromJson()}
+ * methods will use the {@code password} field along-with {@code firstName}, {@code lastName},
+ * and {@code emailAddress} for serialization and deserialization. However, if you created Gson
+ * with {@code Gson gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create()}
+ * then the {@code toJson()} and {@code fromJson()} methods of Gson will exclude the
+ * {@code password} field. This is because the {@code password} field is not marked with the
+ * {@code @Expose} annotation. Gson will also exclude {@code lastName} and {@code emailAddress}
+ * from serialization since {@code serialize} is set to {@code false}. Similarly, Gson will
+ * exclude {@code emailAddress} from deserialization since {@code deserialize} is set to false.
+ *
+ * <p>Note that another way to achieve the same effect would have been to just mark the
+ * {@code password} field as {@code transient}, and Gson would have excluded it even with default
+ * settings. The {@code @Expose} annotation is useful in a style of programming where you want to
+ * explicitly specify all fields that should get considered for serialization or deserialization.
+ *
+ * @author Inderjeet Singh
+ * @author Joel Leitch
+ */
+@Documented
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.FIELD)
+public @interface Expose {
+  
+  /**
+   * If {@code true}, the field marked with this annotation is written out in the JSON while
+   * serializing. If {@code false}, the field marked with this annotation is skipped from the
+   * serialized output. Defaults to {@code true}.
+   * @since 1.4
+   */
+  public boolean serialize() default true;
+
+  /**
+   * If {@code true}, the field marked with this annotation is deserialized from the JSON.
+   * If {@code false}, the field marked with this annotation is skipped during deserialization. 
+   * Defaults to {@code true}.
+   * @since 1.4
+   */
+  public boolean deserialize() default true;
+}
diff --git a/gson/src/main/java/com/google/gson/annotations/JsonAdapter.java b/gson/src/main/java/com/google/gson/annotations/JsonAdapter.java
new file mode 100644
index 0000000000000000000000000000000000000000..a01d77a6ca6ff57a42a0b8ffc0eabd4d3d301e4f
--- /dev/null
+++ b/gson/src/main/java/com/google/gson/annotations/JsonAdapter.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2014 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.gson.annotations;
+
+import com.google.gson.JsonDeserializer;
+import com.google.gson.JsonSerializer;
+import com.google.gson.TypeAdapter;
+import com.google.gson.TypeAdapterFactory;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * An annotation that indicates the Gson {@link TypeAdapter} to use with a class
+ * or field.
+ *
+ * <p>Here is an example of how this annotation is used:</p>
+ * <pre>
+ * &#64;JsonAdapter(UserJsonAdapter.class)
+ * public class User {
+ *   public final String firstName, lastName;
+ *   private User(String firstName, String lastName) {
+ *     this.firstName = firstName;
+ *     this.lastName = lastName;
+ *   }
+ * }
+ * public class UserJsonAdapter extends TypeAdapter&lt;User&gt; {
+ *   &#64;Override public void write(JsonWriter out, User user) throws IOException {
+ *     // implement write: combine firstName and lastName into name
+ *     out.beginObject();
+ *     out.name("name");
+ *     out.value(user.firstName + " " + user.lastName);
+ *     out.endObject();
+ *     // implement the write method
+ *   }
+ *   &#64;Override public User read(JsonReader in) throws IOException {
+ *     // implement read: split name into firstName and lastName
+ *     in.beginObject();
+ *     in.nextName();
+ *     String[] nameParts = in.nextString().split(" ");
+ *     in.endObject();
+ *     return new User(nameParts[0], nameParts[1]);
+ *   }
+ * }
+ * </pre>
+ *
+ * Since User class specified UserJsonAdapter.class in &#64;JsonAdapter annotation, it
+ * will automatically be invoked to serialize/deserialize User instances. <br>
+ *
+ * <p> Here is an example of how to apply this annotation to a field.
+ * <pre>
+ * private static final class Gadget {
+ *   &#64;JsonAdapter(UserJsonAdapter2.class)
+ *   final User user;
+ *   Gadget(User user) {
+ *     this.user = user;
+ *   }
+ * }
+ * </pre>
+ *
+ * It's possible to specify different type adapters on a field, that
+ * field's type, and in the {@link com.google.gson.GsonBuilder}. Field
+ * annotations take precedence over {@code GsonBuilder}-registered type
+ * adapters, which in turn take precedence over annotated types.
+ *
+ * <p>The class referenced by this annotation must be either a {@link
+ * TypeAdapter} or a {@link TypeAdapterFactory}, or must implement one
+ * or both of {@link JsonDeserializer} or {@link JsonSerializer}. 
+ * Using {@link TypeAdapterFactory} makes it possible to delegate 
+ * to the enclosing {@code Gson} instance.
+ *
+ * @since 2.3
+ *
+ * @author Inderjeet Singh
+ * @author Joel Leitch
+ * @author Jesse Wilson
+ */
+// Note that the above example is taken from AdaptAnnotationTest.
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.TYPE, ElementType.FIELD})
+public @interface JsonAdapter {
+
+  /** Either a {@link TypeAdapter} or {@link TypeAdapterFactory}, or one or both of {@link JsonDeserializer} or {@link JsonSerializer}. */
+  Class<?> value();
+
+  /** false, to be able to handle {@code null} values within the adapter, default value is true. */
+  boolean nullSafe() default true;
+
+}
diff --git a/gson/src/main/java/com/google/gson/annotations/SerializedName.java b/gson/src/main/java/com/google/gson/annotations/SerializedName.java
new file mode 100644
index 0000000000000000000000000000000000000000..e50ad55c234a1b4e5250a76278a26dc9b58c7126
--- /dev/null
+++ b/gson/src/main/java/com/google/gson/annotations/SerializedName.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.gson.annotations;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * An annotation that indicates this member should be serialized to JSON with
+ * the provided name value as its field name.
+ *
+ * <p>This annotation will override any {@link com.google.gson.FieldNamingPolicy}, including
+ * the default field naming policy, that may have been set on the {@link com.google.gson.Gson}
+ * instance. A different naming policy can set using the {@code GsonBuilder} class. See
+ * {@link com.google.gson.GsonBuilder#setFieldNamingPolicy(com.google.gson.FieldNamingPolicy)}
+ * for more information.</p>
+ *
+ * <p>Here is an example of how this annotation is meant to be used:</p>
+ * <pre>
+ * public class MyClass {
+ *   &#64;SerializedName("name") String a;
+ *   &#64;SerializedName(value="name1", alternate={"name2", "name3"}) String b;
+ *   String c;
+ *
+ *   public MyClass(String a, String b, String c) {
+ *     this.a = a;
+ *     this.b = b;
+ *     this.c = c;
+ *   }
+ * }
+ * </pre>
+ *
+ * <p>The following shows the output that is generated when serializing an instance of the
+ * above example class:</p>
+ * <pre>
+ * MyClass target = new MyClass("v1", "v2", "v3");
+ * Gson gson = new Gson();
+ * String json = gson.toJson(target);
+ * System.out.println(json);
+ *
+ * ===== OUTPUT =====
+ * {"name":"v1","name1":"v2","c":"v3"}
+ * </pre>
+ *
+ * <p>NOTE: The value you specify in this annotation must be a valid JSON field name.</p>
+ * While deserializing, all values specified in the annotation will be deserialized into the field.
+ * For example:
+ * <pre>
+ *   MyClass target = gson.fromJson("{'name1':'v1'}", MyClass.class);
+ *   assertEquals("v1", target.b);
+ *   target = gson.fromJson("{'name2':'v2'}", MyClass.class);
+ *   assertEquals("v2", target.b);
+ *   target = gson.fromJson("{'name3':'v3'}", MyClass.class);
+ *   assertEquals("v3", target.b);
+ * </pre>
+ * Note that MyClass.b is now deserialized from either name1, name2 or name3.
+ *
+ * @see com.google.gson.FieldNamingPolicy
+ *
+ * @author Inderjeet Singh
+ * @author Joel Leitch
+ */
+@Documented
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.FIELD, ElementType.METHOD})
+public @interface SerializedName {
+
+  /**
+   * @return the desired name of the field when it is serialized or deserialized
+   */
+  String value();
+  /**
+   * @return the alternative names of the field when it is deserialized
+   */
+  String[] alternate() default {};
+}
diff --git a/gson/src/main/java/com/google/gson/annotations/Since.java b/gson/src/main/java/com/google/gson/annotations/Since.java
new file mode 100644
index 0000000000000000000000000000000000000000..e23b6ec9997d344f3ce9c7e386404b2f67734d95
--- /dev/null
+++ b/gson/src/main/java/com/google/gson/annotations/Since.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.gson.annotations;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * An annotation that indicates the version number since a member or a type has been present.
+ * This annotation is useful to manage versioning of your Json classes for a web-service.
+ *
+ * <p>
+ * This annotation has no effect unless you build {@link com.google.gson.Gson} with a
+ * {@link com.google.gson.GsonBuilder} and invoke
+ * {@link com.google.gson.GsonBuilder#setVersion(double)} method.
+ *
+ * <p>Here is an example of how this annotation is meant to be used:</p>
+ * <pre>
+ * public class User {
+ *   private String firstName;
+ *   private String lastName;
+ *   &#64;Since(1.0) private String emailAddress;
+ *   &#64;Since(1.0) private String password;
+ *   &#64;Since(1.1) private Address address;
+ * }
+ * </pre>
+ *
+ * <p>If you created Gson with {@code new Gson()}, the {@code toJson()} and {@code fromJson()}
+ * methods will use all the fields for serialization and deserialization. However, if you created
+ * Gson with {@code Gson gson = new GsonBuilder().setVersion(1.0).create()} then the
+ * {@code toJson()} and {@code fromJson()} methods of Gson will exclude the {@code address} field
+ * since it's version number is set to {@code 1.1}.</p>
+ *
+ * @author Inderjeet Singh
+ * @author Joel Leitch
+ */
+@Documented
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.FIELD, ElementType.TYPE})
+public @interface Since {
+  /**
+   * the value indicating a version number since this member
+   * or type has been present.
+   */
+  double value();
+}
diff --git a/gson/src/main/java/com/google/gson/annotations/Until.java b/gson/src/main/java/com/google/gson/annotations/Until.java
new file mode 100644
index 0000000000000000000000000000000000000000..7c61d104b58894b0ff54933d6203c53ba249c8f3
--- /dev/null
+++ b/gson/src/main/java/com/google/gson/annotations/Until.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.gson.annotations;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * An annotation that indicates the version number until a member or a type should be present.
+ * Basically, if Gson is created with a version number that exceeds the value stored in the
+ * {@code Until} annotation then the field will be ignored from the JSON output. This annotation
+ * is useful to manage versioning of your JSON classes for a web-service.
+ *
+ * <p>
+ * This annotation has no effect unless you build {@link com.google.gson.Gson} with a
+ * {@link com.google.gson.GsonBuilder} and invoke
+ * {@link com.google.gson.GsonBuilder#setVersion(double)} method.
+ *
+ * <p>Here is an example of how this annotation is meant to be used:</p>
+ * <pre>
+ * public class User {
+ *   private String firstName;
+ *   private String lastName;
+ *   &#64;Until(1.1) private String emailAddress;
+ *   &#64;Until(1.1) private String password;
+ * }
+ * </pre>
+ *
+ * <p>If you created Gson with {@code new Gson()}, the {@code toJson()} and {@code fromJson()}
+ * methods will use all the fields for serialization and deserialization. However, if you created
+ * Gson with {@code Gson gson = new GsonBuilder().setVersion(1.2).create()} then the
+ * {@code toJson()} and {@code fromJson()} methods of Gson will exclude the {@code emailAddress}
+ * and {@code password} fields from the example above, because the version number passed to the 
+ * GsonBuilder, {@code 1.2}, exceeds the version number set on the {@code Until} annotation,
+ * {@code 1.1}, for those fields.
+ *
+ * @author Inderjeet Singh
+ * @author Joel Leitch
+ * @since 1.3
+ */
+@Documented
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.FIELD, ElementType.TYPE})
+public @interface Until {
+
+  /**
+   * the value indicating a version number until this member
+   * or type should be ignored.
+   */
+  double value();
+}
diff --git a/gson/src/main/java/com/google/gson/annotations/package-info.java b/gson/src/main/java/com/google/gson/annotations/package-info.java
new file mode 100644
index 0000000000000000000000000000000000000000..1c461fd685385c40ab0eb20029dbe9330bcfaea9
--- /dev/null
+++ b/gson/src/main/java/com/google/gson/annotations/package-info.java
@@ -0,0 +1,6 @@
+/**
+ * This package provides annotations that can be used with {@link com.google.gson.Gson}.
+ * 
+ * @author Inderjeet Singh, Joel Leitch
+ */
+package com.google.gson.annotations;
\ No newline at end of file
diff --git a/gson/src/main/java/com/google/gson/internal/$Gson$Preconditions.java b/gson/src/main/java/com/google/gson/internal/$Gson$Preconditions.java
new file mode 100644
index 0000000000000000000000000000000000000000..83e5730c6e3babe76e9e7e8bddf683b94a994f0c
--- /dev/null
+++ b/gson/src/main/java/com/google/gson/internal/$Gson$Preconditions.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.gson.internal;
+
+/**
+ * A simple utility class used to check method Preconditions.
+ *
+ * <pre>
+ * public long divideBy(long value) {
+ *   Preconditions.checkArgument(value != 0);
+ *   return this.value / value;
+ * }
+ * </pre>
+ *
+ * @author Inderjeet Singh
+ * @author Joel Leitch
+ */
+public final class $Gson$Preconditions {
+  private $Gson$Preconditions() {
+    throw new UnsupportedOperationException();
+  }
+
+  public static <T> T checkNotNull(T obj) {
+    if (obj == null) {
+      throw new NullPointerException();
+    }
+    return obj;
+  }
+
+  public static void checkArgument(boolean condition) {
+    if (!condition) {
+      throw new IllegalArgumentException();
+    }
+  }
+}
diff --git a/gson/src/main/java/com/google/gson/internal/$Gson$Types.java b/gson/src/main/java/com/google/gson/internal/$Gson$Types.java
new file mode 100644
index 0000000000000000000000000000000000000000..4dfb623764aececa951e544ead9f07800e51eb28
--- /dev/null
+++ b/gson/src/main/java/com/google/gson/internal/$Gson$Types.java
@@ -0,0 +1,778 @@
+/**
+ * Copyright (C) 2008 Google Inc.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.gson.internal;
+
+import java.io.Serializable;
+import java.lang.reflect.Array;
+import java.lang.reflect.GenericArrayType;
+import java.lang.reflect.GenericDeclaration;
+import java.lang.reflect.Modifier;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.lang.reflect.TypeVariable;
+import java.lang.reflect.WildcardType;
+import java.util.*;
+
+import static com.google.gson.internal.$Gson$Preconditions.checkArgument;
+import static com.google.gson.internal.$Gson$Preconditions.checkNotNull;
+
+/**
+ * Static methods for working with types.
+ *
+ * @author Bob Lee
+ * @author Jesse Wilson
+ */
+public final class $Gson$Types
+{
+    static final Type[] EMPTY_TYPE_ARRAY = new Type[] {};
+
+    private $Gson$Types()
+    {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Returns a new parameterized type, applying {@code typeArguments} to
+     * {@code rawType} and enclosed by {@code ownerType}.
+     *
+     * @param ownerType
+     * @param rawType
+     * @param typeArguments
+     * @return a {@link java.io.Serializable serializable} parameterized type.
+     */
+    public static ParameterizedType newParameterizedTypeWithOwner(Type ownerType, Type rawType, Type... typeArguments)
+    {
+        return new ParameterizedTypeImpl(ownerType, rawType, typeArguments);
+    }
+
+    /**
+     * Returns an array type whose elements are all instances of
+     * {@code componentType}.
+     *
+     * @return a {@link java.io.Serializable serializable} generic array type.
+     */
+    public static GenericArrayType arrayOf(Type componentType)
+    {
+        return new GenericArrayTypeImpl(componentType);
+    }
+
+    /**
+     * Returns a type that represents an unknown type that extends {@code bound}.
+     * For example, if {@code bound} is {@code CharSequence.class}, this returns
+     * {@code ? extends CharSequence}. If {@code bound} is {@code Object.class},
+     * this returns {@code ?}, which is shorthand for {@code ? extends Object}.
+     * 
+     * @param bound
+     * @return
+     */
+    public static WildcardType subtypeOf(Type bound)
+    {
+        Type[] upperBounds;
+        if (bound instanceof WildcardType)
+        {
+            upperBounds = ((WildcardType) bound).getUpperBounds();
+        }
+        else
+        {
+            upperBounds = new Type[] {bound};
+        }
+        return new WildcardTypeImpl(upperBounds, EMPTY_TYPE_ARRAY);
+    }
+
+    /**
+     * Returns a type that represents an unknown supertype of {@code bound}. For
+     * example, if {@code bound} is {@code String.class}, this returns {@code ?
+     * super String}.
+     * 
+     * @param bound
+     * @return
+     */
+    public static WildcardType supertypeOf(Type bound)
+    {
+        Type[] lowerBounds;
+        if (bound instanceof WildcardType)
+        {
+            lowerBounds = ((WildcardType) bound).getLowerBounds();
+        }
+        else
+        {
+            lowerBounds = new Type[] {bound};
+        }
+        return new WildcardTypeImpl(new Type[] {Object.class}, lowerBounds);
+    }
+
+    /**
+     * Returns a type that is functionally equal but not necessarily equal
+     * according to {@link Object#equals(Object) Object.equals()}. The returned
+     * type is {@link java.io.Serializable}.
+     * 
+     * @param type
+     * @return
+     */
+    public static Type canonicalize(Type type)
+    {
+        if (type instanceof Class)
+        {
+            Class<?> c = (Class<?>) type;
+            return c.isArray() ? new GenericArrayTypeImpl(canonicalize(c.getComponentType())) : c;
+
+        }
+        else if (type instanceof ParameterizedType)
+        {
+            ParameterizedType p = (ParameterizedType) type;
+            return new ParameterizedTypeImpl(p.getOwnerType(), p.getRawType(), p.getActualTypeArguments());
+
+        }
+        else if (type instanceof GenericArrayType)
+        {
+            GenericArrayType g = (GenericArrayType) type;
+            return new GenericArrayTypeImpl(g.getGenericComponentType());
+
+        }
+        else if (type instanceof WildcardType)
+        {
+            WildcardType w = (WildcardType) type;
+            return new WildcardTypeImpl(w.getUpperBounds(), w.getLowerBounds());
+
+        }
+        else
+        {
+            // type is either serializable as-is or unsupported
+            return type;
+        }
+    }
+
+    public static Class<?> getRawType(Type type)
+    {
+        if (type instanceof Class<?>)
+        {
+            // type is a normal class.
+            return (Class<?>) type;
+
+        }
+        else if (type instanceof ParameterizedType)
+        {
+            ParameterizedType parameterizedType = (ParameterizedType) type;
+
+            // I'm not exactly sure why getRawType() returns Type instead of Class.
+            // Neal isn't either but suspects some pathological case related
+            // to nested classes exists.
+            Type rawType = parameterizedType.getRawType();
+            checkArgument(rawType instanceof Class);
+            return (Class<?>) rawType;
+
+        }
+        else if (type instanceof GenericArrayType)
+        {
+            Type componentType = ((GenericArrayType) type).getGenericComponentType();
+            return Array.newInstance(getRawType(componentType), 0).getClass();
+
+        }
+        else if (type instanceof TypeVariable)
+        {
+            // we could use the variable's bounds, but that won't work if there are multiple.
+            // having a raw type that's more general than necessary is okay
+            return Object.class;
+
+        }
+        else if (type instanceof WildcardType)
+        {
+            return getRawType(((WildcardType) type).getUpperBounds()[0]);
+
+        }
+        else
+        {
+            String className = type == null ? "null" : type.getClass().getName();
+            throw new IllegalArgumentException("Expected a Class, ParameterizedType, or " + "GenericArrayType, but <"
+                    + type + "> is of type " + className);
+        }
+    }
+
+    static boolean equal(Object a, Object b)
+    {
+        return a == b || (a != null && a.equals(b));
+    }
+
+    /**
+     * @param a
+     * @param b
+     * @return True if {@code a} and {@code b} are equal.
+     */
+    public static boolean equals(Type a, Type b)
+    {
+        if (a == b)
+        {
+            // also handles (a == null && b == null)
+            return true;
+
+        }
+        else if (a instanceof Class)
+        {
+            // Class already specifies equals().
+            return a.equals(b);
+
+        }
+        else if (a instanceof ParameterizedType)
+        {
+            if (!(b instanceof ParameterizedType))
+            {
+                return false;
+            }
+
+            // TODO: save a .clone() call
+            ParameterizedType pa = (ParameterizedType) a;
+            ParameterizedType pb = (ParameterizedType) b;
+            return equal(pa.getOwnerType(), pb.getOwnerType()) && pa.getRawType().equals(pb.getRawType())
+                    && Arrays.equals(pa.getActualTypeArguments(), pb.getActualTypeArguments());
+
+        }
+        else if (a instanceof GenericArrayType)
+        {
+            if (!(b instanceof GenericArrayType))
+            {
+                return false;
+            }
+
+            GenericArrayType ga = (GenericArrayType) a;
+            GenericArrayType gb = (GenericArrayType) b;
+            return equals(ga.getGenericComponentType(), gb.getGenericComponentType());
+
+        }
+        else if (a instanceof WildcardType)
+        {
+            if (!(b instanceof WildcardType))
+            {
+                return false;
+            }
+
+            WildcardType wa = (WildcardType) a;
+            WildcardType wb = (WildcardType) b;
+            return Arrays.equals(wa.getUpperBounds(), wb.getUpperBounds())
+                    && Arrays.equals(wa.getLowerBounds(), wb.getLowerBounds());
+
+        }
+        else if (a instanceof TypeVariable)
+        {
+            if (!(b instanceof TypeVariable))
+            {
+                return false;
+            }
+            TypeVariable<?> va = (TypeVariable<?>) a;
+            TypeVariable<?> vb = (TypeVariable<?>) b;
+            return va.getGenericDeclaration() == vb.getGenericDeclaration() && va.getName().equals(vb.getName());
+
+        }
+        else
+        {
+            // This isn't a type we support. Could be a generic array type, wildcard type, etc.
+            return false;
+        }
+    }
+
+    static int hashCodeOrZero(Object o)
+    {
+        return o != null ? o.hashCode() : 0;
+    }
+
+    public static String typeToString(Type type)
+    {
+        return type instanceof Class ? ((Class<?>) type).getName() : type.toString();
+    }
+
+    /**
+     * Returns the generic supertype for {@code supertype}. For example, given a class {@code
+     * IntegerSet}, the result for when supertype is {@code Set.class} is {@code Set<Integer>} and the
+     * result when the supertype is {@code Collection.class} is {@code Collection<Integer>}.
+     * 
+     * @param context
+     * @param rawType
+     * @param toResolve
+     * @return
+     */
+    static Type getGenericSupertype(Type context, Class<?> rawType, Class<?> toResolve)
+    {
+        if (toResolve == rawType)
+        {
+            return context;
+        }
+
+        // we skip searching through interfaces if unknown is an interface
+        if (toResolve.isInterface())
+        {
+            Class<?>[] interfaces = rawType.getInterfaces();
+            for (int i = 0, length = interfaces.length; i < length; i++)
+            {
+                if (interfaces[i] == toResolve)
+                {
+                    return rawType.getGenericInterfaces()[i];
+                }
+                else if (toResolve.isAssignableFrom(interfaces[i]))
+                {
+                    return getGenericSupertype(rawType.getGenericInterfaces()[i], interfaces[i], toResolve);
+                }
+            }
+        }
+
+        // check our supertypes
+        if (!rawType.isInterface())
+        {
+            while (rawType != Object.class)
+            {
+                Class<?> rawSupertype = rawType.getSuperclass();
+                if (rawSupertype == toResolve)
+                {
+                    return rawType.getGenericSuperclass();
+                }
+                else if (toResolve.isAssignableFrom(rawSupertype))
+                {
+                    return getGenericSupertype(rawType.getGenericSuperclass(), rawSupertype, toResolve);
+                }
+                rawType = rawSupertype;
+            }
+        }
+
+        // we can't resolve this further
+        return toResolve;
+    }
+
+    /**
+     * Returns the generic form of {@code supertype}. For example, if this is {@code
+     * ArrayList<String>}, this returns {@code Iterable<String>} given the input {@code
+     * Iterable.class}.
+     *
+     * @param context
+     * @param contextRawType
+     * @param supertype
+     *        a superclass of, or interface implemented by, this.
+     * @return
+     */
+    static Type getSupertype(Type context, Class<?> contextRawType, Class<?> supertype)
+    {
+        if (context instanceof WildcardType)
+        {
+            // wildcards are useless for resolving supertypes. As the upper bound has the same raw type, use it instead
+            context = ((WildcardType) context).getUpperBounds()[0];
+        }
+        checkArgument(supertype.isAssignableFrom(contextRawType));
+        return resolve(context, contextRawType, $Gson$Types.getGenericSupertype(context, contextRawType, supertype));
+    }
+
+    /**
+     * Returns the component type of this array type.
+     * 
+     * @throws ClassCastException
+     *         if this type is not an array.
+     * @param array
+     * @return
+     */
+    public static Type getArrayComponentType(Type array)
+    {
+        return array instanceof GenericArrayType ? ((GenericArrayType) array).getGenericComponentType()
+                : ((Class<?>) array).getComponentType();
+    }
+
+    /**
+     * Returns the element type of this collection type.
+     * 
+     * @throws IllegalArgumentException
+     *         if this type is not a collection.
+     * @param context
+     * @param contextRawType
+     * @return
+     */
+    public static Type getCollectionElementType(Type context, Class<?> contextRawType)
+    {
+        Type collectionType = getSupertype(context, contextRawType, Collection.class);
+
+        if (collectionType instanceof WildcardType)
+        {
+            collectionType = ((WildcardType) collectionType).getUpperBounds()[0];
+        }
+        if (collectionType instanceof ParameterizedType)
+        {
+            return ((ParameterizedType) collectionType).getActualTypeArguments()[0];
+        }
+        return Object.class;
+    }
+
+    /**
+     * Returns a two element array containing this map's key and value types in
+     * positions 0 and 1 respectively.
+     * 
+     * @param context
+     * @param contextRawType
+     * @return
+     */
+    public static Type[] getMapKeyAndValueTypes(Type context, Class<?> contextRawType)
+    {
+        /*
+         * Work around a problem with the declaration of java.util.Properties. That
+         * class should extend Hashtable<String, String>, but it's declared to
+         * extend Hashtable<Object, Object>.
+         */
+        if (context == Properties.class)
+        {
+            return new Type[] {String.class, String.class}; // TODO: test subclasses of Properties!
+        }
+
+        Type mapType = getSupertype(context, contextRawType, Map.class);
+        // TODO: strip wildcards?
+        if (mapType instanceof ParameterizedType)
+        {
+            ParameterizedType mapParameterizedType = (ParameterizedType) mapType;
+            return mapParameterizedType.getActualTypeArguments();
+        }
+        return new Type[] {Object.class, Object.class};
+    }
+
+    public static Type resolve(Type context, Class<?> contextRawType, Type toResolve)
+    {
+        return resolve(context, contextRawType, toResolve, new HashSet<TypeVariable>());
+    }
+
+    private static Type resolve(Type context, Class<?> contextRawType, Type toResolve,
+            Collection<TypeVariable> visitedTypeVariables)
+    {
+        // this implementation is made a little more complicated in an attempt to avoid object-creation
+        while (true)
+        {
+            if (toResolve instanceof TypeVariable)
+            {
+                TypeVariable<?> typeVariable = (TypeVariable<?>) toResolve;
+                if (visitedTypeVariables.contains(typeVariable))
+                {
+                    // cannot reduce due to infinite recursion
+                    return toResolve;
+                }
+                else
+                {
+                    visitedTypeVariables.add(typeVariable);
+                }
+                toResolve = resolveTypeVariable(context, contextRawType, typeVariable);
+                if (toResolve == typeVariable)
+                {
+                    return toResolve;
+                }
+
+            }
+            else if (toResolve instanceof Class && ((Class<?>) toResolve).isArray())
+            {
+                Class<?> original = (Class<?>) toResolve;
+                Type componentType = original.getComponentType();
+                Type newComponentType = resolve(context, contextRawType, componentType, visitedTypeVariables);
+                return componentType == newComponentType ? original : arrayOf(newComponentType);
+
+            }
+            else if (toResolve instanceof GenericArrayType)
+            {
+                GenericArrayType original = (GenericArrayType) toResolve;
+                Type componentType = original.getGenericComponentType();
+                Type newComponentType = resolve(context, contextRawType, componentType, visitedTypeVariables);
+                return componentType == newComponentType ? original : arrayOf(newComponentType);
+
+            }
+            else if (toResolve instanceof ParameterizedType)
+            {
+                ParameterizedType original = (ParameterizedType) toResolve;
+                Type ownerType = original.getOwnerType();
+                Type newOwnerType = resolve(context, contextRawType, ownerType, visitedTypeVariables);
+                boolean changed = newOwnerType != ownerType;
+
+                Type[] args = original.getActualTypeArguments();
+                for (int t = 0, length = args.length; t < length; t++)
+                {
+                    Type resolvedTypeArgument = resolve(context, contextRawType, args[t], visitedTypeVariables);
+                    if (resolvedTypeArgument != args[t])
+                    {
+                        if (!changed)
+                        {
+                            args = args.clone();
+                            changed = true;
+                        }
+                        args[t] = resolvedTypeArgument;
+                    }
+                }
+
+                return changed ? newParameterizedTypeWithOwner(newOwnerType, original.getRawType(), args) : original;
+
+            }
+            else if (toResolve instanceof WildcardType)
+            {
+                WildcardType original = (WildcardType) toResolve;
+                Type[] originalLowerBound = original.getLowerBounds();
+                Type[] originalUpperBound = original.getUpperBounds();
+
+                if (originalLowerBound.length == 1)
+                {
+                    Type lowerBound = resolve(context, contextRawType, originalLowerBound[0], visitedTypeVariables);
+                    if (lowerBound != originalLowerBound[0])
+                    {
+                        return supertypeOf(lowerBound);
+                    }
+                }
+                else if (originalUpperBound.length == 1)
+                {
+                    Type upperBound = resolve(context, contextRawType, originalUpperBound[0], visitedTypeVariables);
+                    if (upperBound != originalUpperBound[0])
+                    {
+                        return subtypeOf(upperBound);
+                    }
+                }
+                return original;
+
+            }
+            else
+            {
+                return toResolve;
+            }
+        }
+    }
+
+    static Type resolveTypeVariable(Type context, Class<?> contextRawType, TypeVariable<?> unknown)
+    {
+        Class<?> declaredByRaw = declaringClassOf(unknown);
+
+        // we can't reduce this further
+        if (declaredByRaw == null)
+        {
+            return unknown;
+        }
+
+        Type declaredBy = getGenericSupertype(context, contextRawType, declaredByRaw);
+        if (declaredBy instanceof ParameterizedType)
+        {
+            int index = indexOf(declaredByRaw.getTypeParameters(), unknown);
+            return ((ParameterizedType) declaredBy).getActualTypeArguments()[index];
+        }
+
+        return unknown;
+    }
+
+    private static int indexOf(Object[] array, Object toFind)
+    {
+        for (int i = 0, length = array.length; i < length; i++)
+        {
+            if (toFind.equals(array[i]))
+            {
+                return i;
+            }
+        }
+        throw new NoSuchElementException();
+    }
+
+    /**
+     * Returns the declaring class of {@code typeVariable}, or {@code null} if it was not declared by
+     * a class.
+     * 
+     * @param typeVariable
+     * @return
+     */
+    private static Class<?> declaringClassOf(TypeVariable<?> typeVariable)
+    {
+        GenericDeclaration genericDeclaration = typeVariable.getGenericDeclaration();
+        return genericDeclaration instanceof Class ? (Class<?>) genericDeclaration : null;
+    }
+
+    static void checkNotPrimitive(Type type)
+    {
+        checkArgument(!(type instanceof Class<?>) || !((Class<?>) type).isPrimitive());
+    }
+
+    private static final class ParameterizedTypeImpl implements ParameterizedType, Serializable
+    {
+        private final Type ownerType;
+        private final Type rawType;
+        private final Type[] typeArguments;
+
+        public ParameterizedTypeImpl(Type ownerType, Type rawType, Type... typeArguments)
+        {
+            // require an owner type if the raw type needs it
+            if (rawType instanceof Class<?>)
+            {
+                Class<?> rawTypeAsClass = (Class<?>) rawType;
+                boolean isStaticOrTopLevelClass = Modifier.isStatic(rawTypeAsClass.getModifiers())
+                        || rawTypeAsClass.getEnclosingClass() == null;
+                checkArgument(ownerType != null || isStaticOrTopLevelClass);
+            }
+
+            this.ownerType = ownerType == null ? null : canonicalize(ownerType);
+            this.rawType = canonicalize(rawType);
+            this.typeArguments = typeArguments.clone();
+            for (int t = 0, length = this.typeArguments.length; t < length; t++)
+            {
+                checkNotNull(this.typeArguments[t]);
+                checkNotPrimitive(this.typeArguments[t]);
+                this.typeArguments[t] = canonicalize(this.typeArguments[t]);
+            }
+        }
+
+        public Type[] getActualTypeArguments()
+        {
+            return typeArguments.clone();
+        }
+
+        public Type getRawType()
+        {
+            return rawType;
+        }
+
+        public Type getOwnerType()
+        {
+            return ownerType;
+        }
+
+        @Override
+        public boolean equals(Object other)
+        {
+            return other instanceof ParameterizedType && $Gson$Types.equals(this, (ParameterizedType) other);
+        }
+
+        @Override
+        public int hashCode()
+        {
+            return Arrays.hashCode(typeArguments) ^ rawType.hashCode() ^ hashCodeOrZero(ownerType);
+        }
+
+        @Override
+        public String toString()
+        {
+            int length = typeArguments.length;
+            if (length == 0)
+            {
+                return typeToString(rawType);
+            }
+
+            StringBuilder stringBuilder = new StringBuilder(30 * (length + 1));
+            stringBuilder.append(typeToString(rawType)).append("<").append(typeToString(typeArguments[0]));
+            for (int i = 1; i < length; i++)
+            {
+                stringBuilder.append(", ").append(typeToString(typeArguments[i]));
+            }
+            return stringBuilder.append(">").toString();
+        }
+
+        private static final long serialVersionUID = 0;
+    }
+
+    private static final class GenericArrayTypeImpl implements GenericArrayType, Serializable
+    {
+        private final Type componentType;
+
+        public GenericArrayTypeImpl(Type componentType)
+        {
+            this.componentType = canonicalize(componentType);
+        }
+
+        public Type getGenericComponentType()
+        {
+            return componentType;
+        }
+
+        @Override
+        public boolean equals(Object o)
+        {
+            return o instanceof GenericArrayType && $Gson$Types.equals(this, (GenericArrayType) o);
+        }
+
+        @Override
+        public int hashCode()
+        {
+            return componentType.hashCode();
+        }
+
+        @Override
+        public String toString()
+        {
+            return typeToString(componentType) + "[]";
+        }
+
+        private static final long serialVersionUID = 0;
+    }
+
+    /**
+     * The WildcardType interface supports multiple upper bounds and multiple
+     * lower bounds. We only support what the Java 6 language needs - at most one
+     * bound. If a lower bound is set, the upper bound must be Object.class.
+     */
+    private static final class WildcardTypeImpl implements WildcardType, Serializable
+    {
+        private final Type upperBound;
+        private final Type lowerBound;
+
+        public WildcardTypeImpl(Type[] upperBounds, Type[] lowerBounds)
+        {
+            checkArgument(lowerBounds.length <= 1);
+            checkArgument(upperBounds.length == 1);
+
+            if (lowerBounds.length == 1)
+            {
+                checkNotNull(lowerBounds[0]);
+                checkNotPrimitive(lowerBounds[0]);
+                checkArgument(upperBounds[0] == Object.class);
+                this.lowerBound = canonicalize(lowerBounds[0]);
+                this.upperBound = Object.class;
+
+            }
+            else
+            {
+                checkNotNull(upperBounds[0]);
+                checkNotPrimitive(upperBounds[0]);
+                this.lowerBound = null;
+                this.upperBound = canonicalize(upperBounds[0]);
+            }
+        }
+
+        public Type[] getUpperBounds()
+        {
+            return new Type[] {upperBound};
+        }
+
+        public Type[] getLowerBounds()
+        {
+            return lowerBound != null ? new Type[] {lowerBound} : EMPTY_TYPE_ARRAY;
+        }
+
+        @Override
+        public boolean equals(Object other)
+        {
+            return other instanceof WildcardType && $Gson$Types.equals(this, (WildcardType) other);
+        }
+
+        @Override
+        public int hashCode()
+        {
+            // this equals Arrays.hashCode(getLowerBounds()) ^ Arrays.hashCode(getUpperBounds());
+            return (lowerBound != null ? 31 + lowerBound.hashCode() : 1) ^ (31 + upperBound.hashCode());
+        }
+
+        @Override
+        public String toString()
+        {
+            if (lowerBound != null)
+            {
+                return "? super " + typeToString(lowerBound);
+            }
+            else if (upperBound == Object.class)
+            {
+                return "?";
+            }
+            else
+            {
+                return "? extends " + typeToString(upperBound);
+            }
+        }
+
+        private static final long serialVersionUID = 0;
+    }
+}
diff --git a/gson/src/main/java/com/google/gson/internal/ConstructorConstructor.java b/gson/src/main/java/com/google/gson/internal/ConstructorConstructor.java
new file mode 100644
index 0000000000000000000000000000000000000000..5fab4601059706ded089931138ede04c0e140c70
--- /dev/null
+++ b/gson/src/main/java/com/google/gson/internal/ConstructorConstructor.java
@@ -0,0 +1,238 @@
+/*
+ * Copyright (C) 2011 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.gson.internal;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.EnumSet;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
+import java.util.Map;
+import java.util.Queue;
+import java.util.Set;
+import java.util.SortedMap;
+import java.util.SortedSet;
+import java.util.TreeMap;
+import java.util.TreeSet;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.ConcurrentNavigableMap;
+import java.util.concurrent.ConcurrentSkipListMap;
+
+import com.google.gson.InstanceCreator;
+import com.google.gson.JsonIOException;
+import com.google.gson.internal.reflect.ReflectionAccessor;
+import com.google.gson.reflect.TypeToken;
+
+/**
+ * Returns a function that can construct an instance of a requested type.
+ */
+public final class ConstructorConstructor {
+  private final Map<Type, InstanceCreator<?>> instanceCreators;
+  private final ReflectionAccessor accessor = ReflectionAccessor.getInstance();
+
+  public ConstructorConstructor(Map<Type, InstanceCreator<?>> instanceCreators) {
+    this.instanceCreators = instanceCreators;
+  }
+
+  public <T> ObjectConstructor<T> get(TypeToken<T> typeToken) {
+    final Type type = typeToken.getType();
+    final Class<? super T> rawType = typeToken.getRawType();
+
+    // first try an instance creator
+
+    @SuppressWarnings("unchecked") // types must agree
+    final InstanceCreator<T> typeCreator = (InstanceCreator<T>) instanceCreators.get(type);
+    if (typeCreator != null) {
+      return new ObjectConstructor<T>() {
+        @Override public T construct() {
+          return typeCreator.createInstance(type);
+        }
+      };
+    }
+
+    // Next try raw type match for instance creators
+    @SuppressWarnings("unchecked") // types must agree
+    final InstanceCreator<T> rawTypeCreator =
+        (InstanceCreator<T>) instanceCreators.get(rawType);
+    if (rawTypeCreator != null) {
+      return new ObjectConstructor<T>() {
+        @Override public T construct() {
+          return rawTypeCreator.createInstance(type);
+        }
+      };
+    }
+
+    ObjectConstructor<T> defaultConstructor = newDefaultConstructor(rawType);
+    if (defaultConstructor != null) {
+      return defaultConstructor;
+    }
+
+    ObjectConstructor<T> defaultImplementation = newDefaultImplementationConstructor(type, rawType);
+    if (defaultImplementation != null) {
+      return defaultImplementation;
+    }
+
+    // finally try unsafe
+    return newUnsafeAllocator(type, rawType);
+  }
+
+  private <T> ObjectConstructor<T> newDefaultConstructor(Class<? super T> rawType) {
+    try {
+      final Constructor<? super T> constructor = rawType.getDeclaredConstructor();
+      if (!constructor.isAccessible()) {
+        accessor.makeAccessible(constructor);
+      }
+      return new ObjectConstructor<T>() {
+        @SuppressWarnings("unchecked") // T is the same raw type as is requested
+        @Override public T construct() {
+          try {
+            Object[] args = null;
+            return (T) constructor.newInstance(args);
+          } catch (InstantiationException e) {
+            // TODO: JsonParseException ?
+            throw new RuntimeException("Failed to invoke " + constructor + " with no args", e);
+          } catch (InvocationTargetException e) {
+            // TODO: don't wrap if cause is unchecked!
+            // TODO: JsonParseException ?
+            throw new RuntimeException("Failed to invoke " + constructor + " with no args",
+                e.getTargetException());
+          } catch (IllegalAccessException e) {
+            throw new AssertionError(e);
+          }
+        }
+      };
+    } catch (NoSuchMethodException e) {
+      return null;
+    }
+  }
+
+  /**
+   * Constructors for common interface types like Map and List and their
+   * subtypes.
+   */
+  @SuppressWarnings("unchecked") // use runtime checks to guarantee that 'T' is what it is
+  private <T> ObjectConstructor<T> newDefaultImplementationConstructor(
+      final Type type, Class<? super T> rawType) {
+    if (Collection.class.isAssignableFrom(rawType)) {
+      if (SortedSet.class.isAssignableFrom(rawType)) {
+        return new ObjectConstructor<T>() {
+          @Override public T construct() {
+            return (T) new TreeSet<Object>();
+          }
+        };
+      } else if (EnumSet.class.isAssignableFrom(rawType)) {
+        return new ObjectConstructor<T>() {
+          @SuppressWarnings("rawtypes")
+          @Override public T construct() {
+            if (type instanceof ParameterizedType) {
+              Type elementType = ((ParameterizedType) type).getActualTypeArguments()[0];
+              if (elementType instanceof Class) {
+                return (T) EnumSet.noneOf((Class)elementType);
+              } else {
+                throw new JsonIOException("Invalid EnumSet type: " + type.toString());
+              }
+            } else {
+              throw new JsonIOException("Invalid EnumSet type: " + type.toString());
+            }
+          }
+        };
+      } else if (Set.class.isAssignableFrom(rawType)) {
+        return new ObjectConstructor<T>() {
+          @Override public T construct() {
+            return (T) new LinkedHashSet<Object>();
+          }
+        };
+      } else if (Queue.class.isAssignableFrom(rawType)) {
+        return new ObjectConstructor<T>() {
+          @Override public T construct() {
+            return (T) new ArrayDeque<Object>();
+          }
+        };
+      } else {
+        return new ObjectConstructor<T>() {
+          @Override public T construct() {
+            return (T) new ArrayList<Object>();
+          }
+        };
+      }
+    }
+
+    if (Map.class.isAssignableFrom(rawType)) {
+      if (ConcurrentNavigableMap.class.isAssignableFrom(rawType)) {
+        return new ObjectConstructor<T>() {
+          @Override public T construct() {
+            return (T) new ConcurrentSkipListMap<Object, Object>();
+          }
+        };
+      } else if (ConcurrentMap.class.isAssignableFrom(rawType)) {
+        return new ObjectConstructor<T>() {
+          @Override public T construct() {
+            return (T) new ConcurrentHashMap<Object, Object>();
+          }
+        };
+      } else if (SortedMap.class.isAssignableFrom(rawType)) {
+        return new ObjectConstructor<T>() {
+          @Override public T construct() {
+            return (T) new TreeMap<Object, Object>();
+          }
+        };
+      } else if (type instanceof ParameterizedType && !(String.class.isAssignableFrom(
+          TypeToken.get(((ParameterizedType) type).getActualTypeArguments()[0]).getRawType()))) {
+        return new ObjectConstructor<T>() {
+          @Override public T construct() {
+            return (T) new LinkedHashMap<Object, Object>();
+          }
+        };
+      } else {
+        return new ObjectConstructor<T>() {
+          @Override public T construct() {
+            return (T) new LinkedTreeMap<String, Object>();
+          }
+        };
+      }
+    }
+
+    return null;
+  }
+
+  private <T> ObjectConstructor<T> newUnsafeAllocator(
+      final Type type, final Class<? super T> rawType) {
+    return new ObjectConstructor<T>() {
+      private final UnsafeAllocator unsafeAllocator = UnsafeAllocator.create();
+      @SuppressWarnings("unchecked")
+      @Override public T construct() {
+        try {
+          Object newInstance = unsafeAllocator.newInstance(rawType);
+          return (T) newInstance;
+        } catch (Exception e) {
+          throw new RuntimeException(("Unable to invoke no-args constructor for " + type + ". "
+              + "Registering an InstanceCreator with Gson for this type may fix this problem."), e);
+        }
+      }
+    };
+  }
+
+  @Override public String toString() {
+    return instanceCreators.toString();
+  }
+}
diff --git a/gson/src/main/java/com/google/gson/internal/Excluder.java b/gson/src/main/java/com/google/gson/internal/Excluder.java
new file mode 100644
index 0000000000000000000000000000000000000000..6b83757edb10c1e7a1e9100c9f416e9312f3b82e
--- /dev/null
+++ b/gson/src/main/java/com/google/gson/internal/Excluder.java
@@ -0,0 +1,260 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.gson.internal;
+
+import com.google.gson.ExclusionStrategy;
+import com.google.gson.FieldAttributes;
+import com.google.gson.Gson;
+import com.google.gson.TypeAdapter;
+import com.google.gson.TypeAdapterFactory;
+import com.google.gson.annotations.Expose;
+import com.google.gson.annotations.Since;
+import com.google.gson.annotations.Until;
+import com.google.gson.reflect.TypeToken;
+import com.google.gson.stream.JsonReader;
+import com.google.gson.stream.JsonWriter;
+import java.io.IOException;
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * This class selects which fields and types to omit. It is configurable,
+ * supporting version attributes {@link Since} and {@link Until}, modifiers,
+ * synthetic fields, anonymous and local classes, inner classes, and fields with
+ * the {@link Expose} annotation.
+ *
+ * <p>This class is a type adapter factory; types that are excluded will be
+ * adapted to null. It may delegate to another type adapter if only one
+ * direction is excluded.
+ *
+ * @author Joel Leitch
+ * @author Jesse Wilson
+ */
+public final class Excluder implements TypeAdapterFactory, Cloneable {
+  private static final double IGNORE_VERSIONS = -1.0d;
+  public static final Excluder DEFAULT = new Excluder();
+
+  private double version = IGNORE_VERSIONS;
+  private int modifiers = Modifier.TRANSIENT | Modifier.STATIC;
+  private boolean serializeInnerClasses = true;
+  private boolean requireExpose;
+  private List<ExclusionStrategy> serializationStrategies = Collections.emptyList();
+  private List<ExclusionStrategy> deserializationStrategies = Collections.emptyList();
+
+  @Override protected Excluder clone() {
+    try {
+      return (Excluder) super.clone();
+    } catch (CloneNotSupportedException e) {
+      throw new AssertionError(e);
+    }
+  }
+
+  public Excluder withVersion(double ignoreVersionsAfter) {
+    Excluder result = clone();
+    result.version = ignoreVersionsAfter;
+    return result;
+  }
+
+  public Excluder withModifiers(int... modifiers) {
+    Excluder result = clone();
+    result.modifiers = 0;
+    for (int modifier : modifiers) {
+      result.modifiers |= modifier;
+    }
+    return result;
+  }
+
+  public Excluder disableInnerClassSerialization() {
+    Excluder result = clone();
+    result.serializeInnerClasses = false;
+    return result;
+  }
+
+  public Excluder excludeFieldsWithoutExposeAnnotation() {
+    Excluder result = clone();
+    result.requireExpose = true;
+    return result;
+  }
+
+  public Excluder withExclusionStrategy(ExclusionStrategy exclusionStrategy,
+      boolean serialization, boolean deserialization) {
+    Excluder result = clone();
+    if (serialization) {
+      result.serializationStrategies = new ArrayList<ExclusionStrategy>(serializationStrategies);
+      result.serializationStrategies.add(exclusionStrategy);
+    }
+    if (deserialization) {
+      result.deserializationStrategies
+          = new ArrayList<ExclusionStrategy>(deserializationStrategies);
+      result.deserializationStrategies.add(exclusionStrategy);
+    }
+    return result;
+  }
+
+  public <T> TypeAdapter<T> create(final Gson gson, final TypeToken<T> type) {
+    Class<?> rawType = type.getRawType();
+    boolean excludeClass = excludeClassChecks(rawType);
+
+    final boolean skipSerialize = excludeClass || excludeClassInStrategy(rawType, true);
+    final boolean skipDeserialize = excludeClass ||  excludeClassInStrategy(rawType, false);
+
+    if (!skipSerialize && !skipDeserialize) {
+      return null;
+    }
+
+    return new TypeAdapter<T>() {
+      /** The delegate is lazily created because it may not be needed, and creating it may fail. */
+      private TypeAdapter<T> delegate;
+
+      @Override public T read(JsonReader in) throws IOException {
+        if (skipDeserialize) {
+          in.skipValue();
+          return null;
+        }
+        return delegate().read(in);
+      }
+
+      @Override public void write(JsonWriter out, T value) throws IOException {
+        if (skipSerialize) {
+          out.nullValue();
+          return;
+        }
+        delegate().write(out, value);
+      }
+
+      private TypeAdapter<T> delegate() {
+        TypeAdapter<T> d = delegate;
+        return d != null
+            ? d
+            : (delegate = gson.getDelegateAdapter(Excluder.this, type));
+      }
+    };
+  }
+
+  public boolean excludeField(Field field, boolean serialize) {
+    if ((modifiers & field.getModifiers()) != 0) {
+      return true;
+    }
+
+    if (version != Excluder.IGNORE_VERSIONS
+        && !isValidVersion(field.getAnnotation(Since.class), field.getAnnotation(Until.class))) {
+      return true;
+    }
+
+    if (field.isSynthetic()) {
+      return true;
+    }
+
+    if (requireExpose) {
+      Expose annotation = field.getAnnotation(Expose.class);
+      if (annotation == null || (serialize ? !annotation.serialize() : !annotation.deserialize())) {
+        return true;
+      }
+    }
+
+    if (!serializeInnerClasses && isInnerClass(field.getType())) {
+      return true;
+    }
+
+    if (isAnonymousOrLocal(field.getType())) {
+      return true;
+    }
+
+    List<ExclusionStrategy> list = serialize ? serializationStrategies : deserializationStrategies;
+    if (!list.isEmpty()) {
+      FieldAttributes fieldAttributes = new FieldAttributes(field);
+      for (ExclusionStrategy exclusionStrategy : list) {
+        if (exclusionStrategy.shouldSkipField(fieldAttributes)) {
+          return true;
+        }
+      }
+    }
+
+    return false;
+  }
+
+  private boolean excludeClassChecks(Class<?> clazz) {
+      if (version != Excluder.IGNORE_VERSIONS && !isValidVersion(clazz.getAnnotation(Since.class), clazz.getAnnotation(Until.class))) {
+          return true;
+      }
+
+      if (!serializeInnerClasses && isInnerClass(clazz)) {
+          return true;
+      }
+
+      if (isAnonymousOrLocal(clazz)) {
+          return true;
+      }
+
+      return false;
+  }
+
+  public boolean excludeClass(Class<?> clazz, boolean serialize) {
+      return excludeClassChecks(clazz) ||
+              excludeClassInStrategy(clazz, serialize);
+  }
+
+  private boolean excludeClassInStrategy(Class<?> clazz, boolean serialize) {
+      List<ExclusionStrategy> list = serialize ? serializationStrategies : deserializationStrategies;
+      for (ExclusionStrategy exclusionStrategy : list) {
+          if (exclusionStrategy.shouldSkipClass(clazz)) {
+              return true;
+          }
+      }
+      return false;
+  }
+
+  private boolean isAnonymousOrLocal(Class<?> clazz) {
+    return !Enum.class.isAssignableFrom(clazz)
+        && (clazz.isAnonymousClass() || clazz.isLocalClass());
+  }
+
+  private boolean isInnerClass(Class<?> clazz) {
+    return clazz.isMemberClass() && !isStatic(clazz);
+  }
+
+  private boolean isStatic(Class<?> clazz) {
+    return (clazz.getModifiers() & Modifier.STATIC) != 0;
+  }
+
+  private boolean isValidVersion(Since since, Until until) {
+    return isValidSince(since) && isValidUntil(until);
+  }
+
+  private boolean isValidSince(Since annotation) {
+    if (annotation != null) {
+      double annotationVersion = annotation.value();
+      if (annotationVersion > version) {
+        return false;
+      }
+    }
+    return true;
+  }
+
+  private boolean isValidUntil(Until annotation) {
+    if (annotation != null) {
+      double annotationVersion = annotation.value();
+      if (annotationVersion <= version) {
+        return false;
+      }
+    }
+    return true;
+  }
+}
diff --git a/gson/src/main/java/com/google/gson/internal/GsonBuildConfig.java b/gson/src/main/java/com/google/gson/internal/GsonBuildConfig.java
new file mode 100644
index 0000000000000000000000000000000000000000..ccf0bf41e64ab7b7f3d9a1737cd81cd143654d10
--- /dev/null
+++ b/gson/src/main/java/com/google/gson/internal/GsonBuildConfig.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2018 The Gson authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.gson.internal;
+
+/**
+ * Build configuration for Gson. This file is automatically populated by
+ * templating-maven-plugin and .java/.class files are generated for use in Gson.
+ *
+ * @author Inderjeet Singh
+ */
+public final class GsonBuildConfig {
+  // Based on https://stackoverflow.com/questions/2469922/generate-a-version-java-file-in-maven
+
+  /** This field is automatically populated by Maven when a build is triggered */
+  public static final String VERSION = "2.8.6";
+
+  private GsonBuildConfig() { }
+}
diff --git a/gson/src/main/java/com/google/gson/internal/JavaVersion.java b/gson/src/main/java/com/google/gson/internal/JavaVersion.java
new file mode 100644
index 0000000000000000000000000000000000000000..fab1b7c2744532bd979a9d48d55454cffca54896
--- /dev/null
+++ b/gson/src/main/java/com/google/gson/internal/JavaVersion.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2017 The Gson authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.gson.internal;
+
+/**
+ * Utility to check the major Java version of the current JVM.
+ */
+public final class JavaVersion {
+  // Oracle defines naming conventions at http://www.oracle.com/technetwork/java/javase/versioning-naming-139433.html
+  // However, many alternate implementations differ. For example, Debian used 9-debian as the version string
+
+  private static final int majorJavaVersion = determineMajorJavaVersion();
+
+  private static int determineMajorJavaVersion() {
+    String javaVersion = System.getProperty("java.version");
+    return getMajorJavaVersion(javaVersion);
+  }
+
+  // Visible for testing only
+  static int getMajorJavaVersion(String javaVersion) {
+    int version = parseDotted(javaVersion);
+    if (version == -1) {
+      version = extractBeginningInt(javaVersion);
+    }
+    if (version == -1) {
+      return 6;  // Choose minimum supported JDK version as default
+    }
+    return version;
+  }
+
+  // Parses both legacy 1.8 style and newer 9.0.4 style 
+  private static int parseDotted(String javaVersion) {
+    try {
+      String[] parts = javaVersion.split("[._]");
+      int firstVer = Integer.parseInt(parts[0]);
+      if (firstVer == 1 && parts.length > 1) {
+        return Integer.parseInt(parts[1]);
+      } else {
+        return firstVer;
+      }
+    } catch (NumberFormatException e) {
+      return -1;
+    }
+  }
+
+  private static int extractBeginningInt(String javaVersion) {
+    try {
+      StringBuilder num = new StringBuilder();
+      for (int i = 0; i < javaVersion.length(); ++i) {
+        char c = javaVersion.charAt(i);
+        if (Character.isDigit(c)) {
+          num.append(c);
+        } else {
+          break;
+        }
+      }
+      return Integer.parseInt(num.toString());
+    } catch (NumberFormatException e) {
+      return -1;
+    }
+  }
+
+  /**
+   * @return the major Java version, i.e. '8' for Java 1.8, '9' for Java 9 etc.
+   */
+  public static int getMajorJavaVersion() {
+    return majorJavaVersion;
+  }
+
+  /**
+   * @return {@code true} if the application is running on Java 9 or later; and {@code false} otherwise.
+   */
+  public static boolean isJava9OrLater() {
+    return majorJavaVersion >= 9;
+  }
+
+  private JavaVersion() { }
+}
diff --git a/gson/src/main/java/com/google/gson/internal/JsonReaderInternalAccess.java b/gson/src/main/java/com/google/gson/internal/JsonReaderInternalAccess.java
new file mode 100644
index 0000000000000000000000000000000000000000..bbd472040a38d31501be8dc8d59e474a045aa03d
--- /dev/null
+++ b/gson/src/main/java/com/google/gson/internal/JsonReaderInternalAccess.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2011 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.gson.internal;
+
+import com.google.gson.stream.JsonReader;
+import java.io.IOException;
+
+/**
+ * Internal-only APIs of JsonReader available only to other classes in Gson.
+ */
+public abstract class JsonReaderInternalAccess {
+  public static JsonReaderInternalAccess INSTANCE;
+
+  /**
+   * Changes the type of the current property name token to a string value.
+   */
+  public abstract void promoteNameToValue(JsonReader reader) throws IOException;
+}
diff --git a/gson/src/main/java/com/google/gson/internal/LazilyParsedNumber.java b/gson/src/main/java/com/google/gson/internal/LazilyParsedNumber.java
new file mode 100644
index 0000000000000000000000000000000000000000..3669af7b58dc466e91ebd8f8845fc267ee3d4004
--- /dev/null
+++ b/gson/src/main/java/com/google/gson/internal/LazilyParsedNumber.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2011 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.google.gson.internal;
+
+import java.io.ObjectStreamException;
+import java.math.BigDecimal;
+
+/**
+ * This class holds a number value that is lazily converted to a specific number type
+ *
+ * @author Inderjeet Singh
+ */
+public final class LazilyParsedNumber extends Number {
+  private final String value;
+
+  /** @param value must not be null */
+  public LazilyParsedNumber(String value) {
+    this.value = value;
+  }
+
+  @Override
+  public int intValue() {
+    try {
+      return Integer.parseInt(value);
+    } catch (NumberFormatException e) {
+      try {
+        return (int) Long.parseLong(value);
+      } catch (NumberFormatException nfe) {
+        return new BigDecimal(value).intValue();
+      }
+    }
+  }
+
+  @Override
+  public long longValue() {
+    try {
+      return Long.parseLong(value);
+    } catch (NumberFormatException e) {
+      return new BigDecimal(value).longValue();
+    }
+  }
+
+  @Override
+  public float floatValue() {
+    return Float.parseFloat(value);
+  }
+
+  @Override
+  public double doubleValue() {
+    return Double.parseDouble(value);
+  }
+
+  @Override
+  public String toString() {
+    return value;
+  }
+
+  /**
+   * If somebody is unlucky enough to have to serialize one of these, serialize
+   * it as a BigDecimal so that they won't need Gson on the other side to
+   * deserialize it.
+   */
+  private Object writeReplace() throws ObjectStreamException {
+    return new BigDecimal(value);
+  }
+
+  @Override
+  public int hashCode() {
+    return value.hashCode();
+  }
+
+  @Override
+  public boolean equals(Object obj) {
+    if (this == obj) {
+      return true;
+    }
+    if (obj instanceof LazilyParsedNumber) {
+      LazilyParsedNumber other = (LazilyParsedNumber) obj;
+      return value == other.value || value.equals(other.value);
+    }
+    return false;
+  }
+}
diff --git a/gson/src/main/java/com/google/gson/internal/LinkedHashTreeMap.java b/gson/src/main/java/com/google/gson/internal/LinkedHashTreeMap.java
new file mode 100644
index 0000000000000000000000000000000000000000..b2707c50daf303f33b48cff434355ce4ad0b10c9
--- /dev/null
+++ b/gson/src/main/java/com/google/gson/internal/LinkedHashTreeMap.java
@@ -0,0 +1,864 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ * Copyright (C) 2012 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.gson.internal;
+
+import java.io.ObjectStreamException;
+import java.io.Serializable;
+import java.util.AbstractMap;
+import java.util.AbstractSet;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.ConcurrentModificationException;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.NoSuchElementException;
+import java.util.Set;
+
+/**
+ * A map of comparable keys to values. Unlike {@code TreeMap}, this class uses
+ * insertion order for iteration order. Comparison order is only used as an
+ * optimization for efficient insertion and removal.
+ *
+ * <p>This implementation was derived from Android 4.1's TreeMap and
+ * LinkedHashMap classes.
+ */
+public final class LinkedHashTreeMap<K, V> extends AbstractMap<K, V> implements Serializable {
+  @SuppressWarnings({ "unchecked", "rawtypes" }) // to avoid Comparable<Comparable<Comparable<...>>>
+  private static final Comparator<Comparable> NATURAL_ORDER = new Comparator<Comparable>() {
+    public int compare(Comparable a, Comparable b) {
+      return a.compareTo(b);
+    }
+  };
+
+  Comparator<? super K> comparator;
+  Node<K, V>[] table;
+  final Node<K, V> header;
+  int size = 0;
+  int modCount = 0;
+  int threshold;
+
+  /**
+   * Create a natural order, empty tree map whose keys must be mutually
+   * comparable and non-null.
+   */
+  @SuppressWarnings("unchecked") // unsafe! this assumes K is comparable
+  public LinkedHashTreeMap() {
+    this((Comparator<? super K>) NATURAL_ORDER);
+  }
+
+  /**
+   * Create a tree map ordered by {@code comparator}. This map's keys may only
+   * be null if {@code comparator} permits.
+   *
+   * @param comparator the comparator to order elements with, or {@code null} to
+   *     use the natural ordering.
+   */
+  @SuppressWarnings({ "unchecked", "rawtypes" }) // unsafe! if comparator is null, this assumes K is comparable
+  public LinkedHashTreeMap(Comparator<? super K> comparator) {
+    this.comparator = comparator != null
+        ? comparator
+        : (Comparator) NATURAL_ORDER;
+    this.header = new Node<K, V>();
+    this.table = new Node[16]; // TODO: sizing/resizing policies
+    this.threshold = (table.length / 2) + (table.length / 4); // 3/4 capacity
+  }
+
+  @Override public int size() {
+    return size;
+  }
+
+  @Override public V get(Object key) {
+    Node<K, V> node = findByObject(key);
+    return node != null ? node.value : null;
+  }
+
+  @Override public boolean containsKey(Object key) {
+    return findByObject(key) != null;
+  }
+
+  @Override public V put(K key, V value) {
+    if (key == null) {
+      throw new NullPointerException("key == null");
+    }
+    Node<K, V> created = find(key, true);
+    V result = created.value;
+    created.value = value;
+    return result;
+  }
+
+  @Override public void clear() {
+    Arrays.fill(table, null);
+    size = 0;
+    modCount++;
+
+    // Clear all links to help GC
+    Node<K, V> header = this.header;
+    for (Node<K, V> e = header.next; e != header; ) {
+      Node<K, V> next = e.next;
+      e.next = e.prev = null;
+      e = next;
+    }
+
+    header.next = header.prev = header;
+  }
+
+  @Override public V remove(Object key) {
+    Node<K, V> node = removeInternalByKey(key);
+    return node != null ? node.value : null;
+  }
+
+  /**
+   * Returns the node at or adjacent to the given key, creating it if requested.
+   *
+   * @throws ClassCastException if {@code key} and the tree's keys aren't
+   *     mutually comparable.
+   */
+  Node<K, V> find(K key, boolean create) {
+    Comparator<? super K> comparator = this.comparator;
+    Node<K, V>[] table = this.table;
+    int hash = secondaryHash(key.hashCode());
+    int index = hash & (table.length - 1);
+    Node<K, V> nearest = table[index];
+    int comparison = 0;
+
+    if (nearest != null) {
+      // Micro-optimization: avoid polymorphic calls to Comparator.compare().
+      @SuppressWarnings("unchecked") // Throws a ClassCastException below if there's trouble.
+      Comparable<Object> comparableKey = (comparator == NATURAL_ORDER)
+          ? (Comparable<Object>) key
+          : null;
+
+      while (true) {
+        comparison = (comparableKey != null)
+            ? comparableKey.compareTo(nearest.key)
+            : comparator.compare(key, nearest.key);
+
+        // We found the requested key.
+        if (comparison == 0) {
+          return nearest;
+        }
+
+        // If it exists, the key is in a subtree. Go deeper.
+        Node<K, V> child = (comparison < 0) ? nearest.left : nearest.right;
+        if (child == null) {
+          break;
+        }
+
+        nearest = child;
+      }
+    }
+
+    // The key doesn't exist in this tree.
+    if (!create) {
+      return null;
+    }
+
+    // Create the node and add it to the tree or the table.
+    Node<K, V> header = this.header;
+    Node<K, V> created;
+    if (nearest == null) {
+      // Check that the value is comparable if we didn't do any comparisons.
+      if (comparator == NATURAL_ORDER && !(key instanceof Comparable)) {
+        throw new ClassCastException(key.getClass().getName() + " is not Comparable");
+      }
+      created = new Node<K, V>(nearest, key, hash, header, header.prev);
+      table[index] = created;
+    } else {
+      created = new Node<K, V>(nearest, key, hash, header, header.prev);
+      if (comparison < 0) { // nearest.key is higher
+        nearest.left = created;
+      } else { // comparison > 0, nearest.key is lower
+        nearest.right = created;
+      }
+      rebalance(nearest, true);
+    }
+
+    if (size++ > threshold) {
+      doubleCapacity();
+    }
+    modCount++;
+
+    return created;
+  }
+
+  @SuppressWarnings("unchecked")
+  Node<K, V> findByObject(Object key) {
+    try {
+      return key != null ? find((K) key, false) : null;
+    } catch (ClassCastException e) {
+      return null;
+    }
+  }
+
+  /**
+   * Returns this map's entry that has the same key and value as {@code
+   * entry}, or null if this map has no such entry.
+   *
+   * <p>This method uses the comparator for key equality rather than {@code
+   * equals}. If this map's comparator isn't consistent with equals (such as
+   * {@code String.CASE_INSENSITIVE_ORDER}), then {@code remove()} and {@code
+   * contains()} will violate the collections API.
+   */
+  Node<K, V> findByEntry(Entry<?, ?> entry) {
+    Node<K, V> mine = findByObject(entry.getKey());
+    boolean valuesEqual = mine != null && equal(mine.value, entry.getValue());
+    return valuesEqual ? mine : null;
+  }
+
+  private boolean equal(Object a, Object b) {
+    return a == b || (a != null && a.equals(b));
+  }
+
+  /**
+   * Applies a supplemental hash function to a given hashCode, which defends
+   * against poor quality hash functions. This is critical because HashMap
+   * uses power-of-two length hash tables, that otherwise encounter collisions
+   * for hashCodes that do not differ in lower or upper bits.
+   */
+  private static int secondaryHash(int h) {
+    // Doug Lea's supplemental hash function
+    h ^= (h >>> 20) ^ (h >>> 12);
+    return h ^ (h >>> 7) ^ (h >>> 4);
+  }
+
+  /**
+   * Removes {@code node} from this tree, rearranging the tree's structure as
+   * necessary.
+   *
+   * @param unlink true to also unlink this node from the iteration linked list.
+   */
+  void removeInternal(Node<K, V> node, boolean unlink) {
+    if (unlink) {
+      node.prev.next = node.next;
+      node.next.prev = node.prev;
+      node.next = node.prev = null; // Help the GC (for performance)
+    }
+
+    Node<K, V> left = node.left;
+    Node<K, V> right = node.right;
+    Node<K, V> originalParent = node.parent;
+    if (left != null && right != null) {
+
+      /*
+       * To remove a node with both left and right subtrees, move an
+       * adjacent node from one of those subtrees into this node's place.
+       *
+       * Removing the adjacent node may change this node's subtrees. This
+       * node may no longer have two subtrees once the adjacent node is
+       * gone!
+       */
+
+      Node<K, V> adjacent = (left.height > right.height) ? left.last() : right.first();
+      removeInternal(adjacent, false); // takes care of rebalance and size--
+
+      int leftHeight = 0;
+      left = node.left;
+      if (left != null) {
+        leftHeight = left.height;
+        adjacent.left = left;
+        left.parent = adjacent;
+        node.left = null;
+      }
+      int rightHeight = 0;
+      right = node.right;
+      if (right != null) {
+        rightHeight = right.height;
+        adjacent.right = right;
+        right.parent = adjacent;
+        node.right = null;
+      }
+      adjacent.height = Math.max(leftHeight, rightHeight) + 1;
+      replaceInParent(node, adjacent);
+      return;
+    } else if (left != null) {
+      replaceInParent(node, left);
+      node.left = null;
+    } else if (right != null) {
+      replaceInParent(node, right);
+      node.right = null;
+    } else {
+      replaceInParent(node, null);
+    }
+
+    rebalance(originalParent, false);
+    size--;
+    modCount++;
+  }
+
+  Node<K, V> removeInternalByKey(Object key) {
+    Node<K, V> node = findByObject(key);
+    if (node != null) {
+      removeInternal(node, true);
+    }
+    return node;
+  }
+
+  private void replaceInParent(Node<K, V> node, Node<K, V> replacement) {
+    Node<K, V> parent = node.parent;
+    node.parent = null;
+    if (replacement != null) {
+      replacement.parent = parent;
+    }
+
+    if (parent != null) {
+      if (parent.left == node) {
+        parent.left = replacement;
+      } else {
+        assert (parent.right == node);
+        parent.right = replacement;
+      }
+    } else {
+      int index = node.hash & (table.length - 1);
+      table[index] = replacement;
+    }
+  }
+
+  /**
+   * Rebalances the tree by making any AVL rotations necessary between the
+   * newly-unbalanced node and the tree's root.
+   *
+   * @param insert true if the node was unbalanced by an insert; false if it
+   *     was by a removal.
+   */
+  private void rebalance(Node<K, V> unbalanced, boolean insert) {
+    for (Node<K, V> node = unbalanced; node != null; node = node.parent) {
+      Node<K, V> left = node.left;
+      Node<K, V> right = node.right;
+      int leftHeight = left != null ? left.height : 0;
+      int rightHeight = right != null ? right.height : 0;
+
+      int delta = leftHeight - rightHeight;
+      if (delta == -2) {
+        Node<K, V> rightLeft = right.left;
+        Node<K, V> rightRight = right.right;
+        int rightRightHeight = rightRight != null ? rightRight.height : 0;
+        int rightLeftHeight = rightLeft != null ? rightLeft.height : 0;
+
+        int rightDelta = rightLeftHeight - rightRightHeight;
+        if (rightDelta == -1 || (rightDelta == 0 && !insert)) {
+          rotateLeft(node); // AVL right right
+        } else {
+          assert (rightDelta == 1);
+          rotateRight(right); // AVL right left
+          rotateLeft(node);
+        }
+        if (insert) {
+          break; // no further rotations will be necessary
+        }
+
+      } else if (delta == 2) {
+        Node<K, V> leftLeft = left.left;
+        Node<K, V> leftRight = left.right;
+        int leftRightHeight = leftRight != null ? leftRight.height : 0;
+        int leftLeftHeight = leftLeft != null ? leftLeft.height : 0;
+
+        int leftDelta = leftLeftHeight - leftRightHeight;
+        if (leftDelta == 1 || (leftDelta == 0 && !insert)) {
+          rotateRight(node); // AVL left left
+        } else {
+          assert (leftDelta == -1);
+          rotateLeft(left); // AVL left right
+          rotateRight(node);
+        }
+        if (insert) {
+          break; // no further rotations will be necessary
+        }
+
+      } else if (delta == 0) {
+        node.height = leftHeight + 1; // leftHeight == rightHeight
+        if (insert) {
+          break; // the insert caused balance, so rebalancing is done!
+        }
+
+      } else {
+        assert (delta == -1 || delta == 1);
+        node.height = Math.max(leftHeight, rightHeight) + 1;
+        if (!insert) {
+          break; // the height hasn't changed, so rebalancing is done!
+        }
+      }
+    }
+  }
+
+  /**
+   * Rotates the subtree so that its root's right child is the new root.
+   */
+  private void rotateLeft(Node<K, V> root) {
+    Node<K, V> left = root.left;
+    Node<K, V> pivot = root.right;
+    Node<K, V> pivotLeft = pivot.left;
+    Node<K, V> pivotRight = pivot.right;
+
+    // move the pivot's left child to the root's right
+    root.right = pivotLeft;
+    if (pivotLeft != null) {
+      pivotLeft.parent = root;
+    }
+
+    replaceInParent(root, pivot);
+
+    // move the root to the pivot's left
+    pivot.left = root;
+    root.parent = pivot;
+
+    // fix heights
+    root.height = Math.max(left != null ? left.height : 0,
+        pivotLeft != null ? pivotLeft.height : 0) + 1;
+    pivot.height = Math.max(root.height,
+        pivotRight != null ? pivotRight.height : 0) + 1;
+  }
+
+  /**
+   * Rotates the subtree so that its root's left child is the new root.
+   */
+  private void rotateRight(Node<K, V> root) {
+    Node<K, V> pivot = root.left;
+    Node<K, V> right = root.right;
+    Node<K, V> pivotLeft = pivot.left;
+    Node<K, V> pivotRight = pivot.right;
+
+    // move the pivot's right child to the root's left
+    root.left = pivotRight;
+    if (pivotRight != null) {
+      pivotRight.parent = root;
+    }
+
+    replaceInParent(root, pivot);
+
+    // move the root to the pivot's right
+    pivot.right = root;
+    root.parent = pivot;
+
+    // fixup heights
+    root.height = Math.max(right != null ? right.height : 0,
+        pivotRight != null ? pivotRight.height : 0) + 1;
+    pivot.height = Math.max(root.height,
+        pivotLeft != null ? pivotLeft.height : 0) + 1;
+  }
+
+  private EntrySet entrySet;
+  private KeySet keySet;
+
+  @Override public Set<Entry<K, V>> entrySet() {
+    EntrySet result = entrySet;
+    return result != null ? result : (entrySet = new EntrySet());
+  }
+
+  @Override public Set<K> keySet() {
+    KeySet result = keySet;
+    return result != null ? result : (keySet = new KeySet());
+  }
+
+  static final class Node<K, V> implements Entry<K, V> {
+    Node<K, V> parent;
+    Node<K, V> left;
+    Node<K, V> right;
+    Node<K, V> next;
+    Node<K, V> prev;
+    final K key;
+    final int hash;
+    V value;
+    int height;
+
+    /** Create the header entry */
+    Node() {
+      key = null;
+      hash = -1;
+      next = prev = this;
+    }
+
+    /** Create a regular entry */
+    Node(Node<K, V> parent, K key, int hash, Node<K, V> next, Node<K, V> prev) {
+      this.parent = parent;
+      this.key = key;
+      this.hash = hash;
+      this.height = 1;
+      this.next = next;
+      this.prev = prev;
+      prev.next = this;
+      next.prev = this;
+    }
+
+    public K getKey() {
+      return key;
+    }
+
+    public V getValue() {
+      return value;
+    }
+
+    public V setValue(V value) {
+      V oldValue = this.value;
+      this.value = value;
+      return oldValue;
+    }
+
+    @SuppressWarnings("rawtypes")
+    @Override public boolean equals(Object o) {
+      if (o instanceof Entry) {
+        Entry other = (Entry) o;
+        return (key == null ? other.getKey() == null : key.equals(other.getKey()))
+            && (value == null ? other.getValue() == null : value.equals(other.getValue()));
+      }
+      return false;
+    }
+
+    @Override public int hashCode() {
+      return (key == null ? 0 : key.hashCode())
+          ^ (value == null ? 0 : value.hashCode());
+    }
+
+    @Override public String toString() {
+      return key + "=" + value;
+    }
+
+    /**
+     * Returns the first node in this subtree.
+     */
+    public Node<K, V> first() {
+      Node<K, V> node = this;
+      Node<K, V> child = node.left;
+      while (child != null) {
+        node = child;
+        child = node.left;
+      }
+      return node;
+    }
+
+    /**
+     * Returns the last node in this subtree.
+     */
+    public Node<K, V> last() {
+      Node<K, V> node = this;
+      Node<K, V> child = node.right;
+      while (child != null) {
+        node = child;
+        child = node.right;
+      }
+      return node;
+    }
+  }
+
+  private void doubleCapacity() {
+    table = doubleCapacity(table);
+    threshold = (table.length / 2) + (table.length / 4); // 3/4 capacity
+  }
+
+  /**
+   * Returns a new array containing the same nodes as {@code oldTable}, but with
+   * twice as many trees, each of (approximately) half the previous size.
+   */
+  static <K, V> Node<K, V>[] doubleCapacity(Node<K, V>[] oldTable) {
+    // TODO: don't do anything if we're already at MAX_CAPACITY
+    int oldCapacity = oldTable.length;
+    @SuppressWarnings("unchecked") // Arrays and generics don't get along.
+    Node<K, V>[] newTable = new Node[oldCapacity * 2];
+    AvlIterator<K, V> iterator = new AvlIterator<K, V>();
+    AvlBuilder<K, V> leftBuilder = new AvlBuilder<K, V>();
+    AvlBuilder<K, V> rightBuilder = new AvlBuilder<K, V>();
+
+    // Split each tree into two trees.
+    for (int i = 0; i < oldCapacity; i++) {
+      Node<K, V> root = oldTable[i];
+      if (root == null) {
+        continue;
+      }
+
+      // Compute the sizes of the left and right trees.
+      iterator.reset(root);
+      int leftSize = 0;
+      int rightSize = 0;
+      for (Node<K, V> node; (node = iterator.next()) != null; ) {
+        if ((node.hash & oldCapacity) == 0) {
+          leftSize++;
+        } else {
+          rightSize++;
+        }
+      }
+
+      // Split the tree into two.
+      leftBuilder.reset(leftSize);
+      rightBuilder.reset(rightSize);
+      iterator.reset(root);
+      for (Node<K, V> node; (node = iterator.next()) != null; ) {
+        if ((node.hash & oldCapacity) == 0) {
+          leftBuilder.add(node);
+        } else {
+          rightBuilder.add(node);
+        }
+      }
+
+      // Populate the enlarged array with these new roots.
+      newTable[i] = leftSize > 0 ? leftBuilder.root() : null;
+      newTable[i + oldCapacity] = rightSize > 0 ? rightBuilder.root() : null;
+    }
+    return newTable;
+  }
+
+  /**
+   * Walks an AVL tree in iteration order. Once a node has been returned, its
+   * left, right and parent links are <strong>no longer used</strong>. For this
+   * reason it is safe to transform these links as you walk a tree.
+   *
+   * <p><strong>Warning:</strong> this iterator is destructive. It clears the
+   * parent node of all nodes in the tree. It is an error to make a partial
+   * iteration of a tree.
+   */
+  static class AvlIterator<K, V> {
+    /** This stack is a singly linked list, linked by the 'parent' field. */
+    private Node<K, V> stackTop;
+
+    void reset(Node<K, V> root) {
+      Node<K, V> stackTop = null;
+      for (Node<K, V> n = root; n != null; n = n.left) {
+        n.parent = stackTop;
+        stackTop = n; // Stack push.
+      }
+      this.stackTop = stackTop;
+    }
+
+    public Node<K, V> next() {
+      Node<K, V> stackTop = this.stackTop;
+      if (stackTop == null) {
+        return null;
+      }
+      Node<K, V> result = stackTop;
+      stackTop = result.parent;
+      result.parent = null;
+      for (Node<K, V> n = result.right; n != null; n = n.left) {
+        n.parent = stackTop;
+        stackTop = n; // Stack push.
+      }
+      this.stackTop = stackTop;
+      return result;
+    }
+  }
+
+  /**
+   * Builds AVL trees of a predetermined size by accepting nodes of increasing
+   * value. To use:
+   * <ol>
+   *   <li>Call {@link #reset} to initialize the target size <i>size</i>.
+   *   <li>Call {@link #add} <i>size</i> times with increasing values.
+   *   <li>Call {@link #root} to get the root of the balanced tree.
+   * </ol>
+   *
+   * <p>The returned tree will satisfy the AVL constraint: for every node
+   * <i>N</i>, the height of <i>N.left</i> and <i>N.right</i> is different by at
+   * most 1. It accomplishes this by omitting deepest-level leaf nodes when
+   * building trees whose size isn't a power of 2 minus 1.
+   *
+   * <p>Unlike rebuilding a tree from scratch, this approach requires no value
+   * comparisons. Using this class to create a tree of size <i>S</i> is
+   * {@code O(S)}.
+   */
+  final static class AvlBuilder<K, V> {
+    /** This stack is a singly linked list, linked by the 'parent' field. */
+    private Node<K, V> stack;
+    private int leavesToSkip;
+    private int leavesSkipped;
+    private int size;
+
+    void reset(int targetSize) {
+      // compute the target tree size. This is a power of 2 minus one, like 15 or 31.
+      int treeCapacity = Integer.highestOneBit(targetSize) * 2 - 1;
+      leavesToSkip = treeCapacity - targetSize;
+      size = 0;
+      leavesSkipped = 0;
+      stack = null;
+    }
+
+    void add(Node<K, V> node) {
+      node.left = node.parent = node.right = null;
+      node.height = 1;
+
+      // Skip a leaf if necessary.
+      if (leavesToSkip > 0 && (size & 1) == 0) {
+        size++;
+        leavesToSkip--;
+        leavesSkipped++;
+      }
+
+      node.parent = stack;
+      stack = node; // Stack push.
+      size++;
+
+      // Skip a leaf if necessary.
+      if (leavesToSkip > 0 && (size & 1) == 0) {
+        size++;
+        leavesToSkip--;
+        leavesSkipped++;
+      }
+
+      /*
+       * Combine 3 nodes into subtrees whenever the size is one less than a
+       * multiple of 4. For example we combine the nodes A, B, C into a
+       * 3-element tree with B as the root.
+       *
+       * Combine two subtrees and a spare single value whenever the size is one
+       * less than a multiple of 8. For example at 8 we may combine subtrees
+       * (A B C) and (E F G) with D as the root to form ((A B C) D (E F G)).
+       *
+       * Just as we combine single nodes when size nears a multiple of 4, and
+       * 3-element trees when size nears a multiple of 8, we combine subtrees of
+       * size (N-1) whenever the total size is 2N-1 whenever N is a power of 2.
+       */
+      for (int scale = 4; (size & scale - 1) == scale - 1; scale *= 2) {
+        if (leavesSkipped == 0) {
+          // Pop right, center and left, then make center the top of the stack.
+          Node<K, V> right = stack;
+          Node<K, V> center = right.parent;
+          Node<K, V> left = center.parent;
+          center.parent = left.parent;
+          stack = center;
+          // Construct a tree.
+          center.left = left;
+          center.right = right;
+          center.height = right.height + 1;
+          left.parent = center;
+          right.parent = center;
+        } else if (leavesSkipped == 1) {
+          // Pop right and center, then make center the top of the stack.
+          Node<K, V> right = stack;
+          Node<K, V> center = right.parent;
+          stack = center;
+          // Construct a tree with no left child.
+          center.right = right;
+          center.height = right.height + 1;
+          right.parent = center;
+          leavesSkipped = 0;
+        } else if (leavesSkipped == 2) {
+          leavesSkipped = 0;
+        }
+      }
+    }
+
+    Node<K, V> root() {
+      Node<K, V> stackTop = this.stack;
+      if (stackTop.parent != null) {
+        throw new IllegalStateException();
+      }
+      return stackTop;
+    }
+  }
+
+  private abstract class LinkedTreeMapIterator<T> implements Iterator<T> {
+    Node<K, V> next = header.next;
+    Node<K, V> lastReturned = null;
+    int expectedModCount = modCount;
+
+    LinkedTreeMapIterator() {
+    }
+
+    public final boolean hasNext() {
+      return next != header;
+    }
+
+    final Node<K, V> nextNode() {
+      Node<K, V> e = next;
+      if (e == header) {
+        throw new NoSuchElementException();
+      }
+      if (modCount != expectedModCount) {
+        throw new ConcurrentModificationException();
+      }
+      next = e.next;
+      return lastReturned = e;
+    }
+
+    public final void remove() {
+      if (lastReturned == null) {
+        throw new IllegalStateException();
+      }
+      removeInternal(lastReturned, true);
+      lastReturned = null;
+      expectedModCount = modCount;
+    }
+  }
+
+  final class EntrySet extends AbstractSet<Entry<K, V>> {
+    @Override public int size() {
+      return size;
+    }
+
+    @Override public Iterator<Entry<K, V>> iterator() {
+      return new LinkedTreeMapIterator<Entry<K, V>>() {
+        public Entry<K, V> next() {
+          return nextNode();
+        }
+      };
+    }
+
+    @Override public boolean contains(Object o) {
+      return o instanceof Entry && findByEntry((Entry<?, ?>) o) != null;
+    }
+
+    @Override public boolean remove(Object o) {
+      if (!(o instanceof Entry)) {
+        return false;
+      }
+
+      Node<K, V> node = findByEntry((Entry<?, ?>) o);
+      if (node == null) {
+        return false;
+      }
+      removeInternal(node, true);
+      return true;
+    }
+
+    @Override public void clear() {
+      LinkedHashTreeMap.this.clear();
+    }
+  }
+
+  final class KeySet extends AbstractSet<K> {
+    @Override public int size() {
+      return size;
+    }
+
+    @Override public Iterator<K> iterator() {
+      return new LinkedTreeMapIterator<K>() {
+        public K next() {
+          return nextNode().key;
+        }
+      };
+    }
+
+    @Override public boolean contains(Object o) {
+      return containsKey(o);
+    }
+
+    @Override public boolean remove(Object key) {
+      return removeInternalByKey(key) != null;
+    }
+
+    @Override public void clear() {
+      LinkedHashTreeMap.this.clear();
+    }
+  }
+
+  /**
+   * If somebody is unlucky enough to have to serialize one of these, serialize
+   * it as a LinkedHashMap so that they won't need Gson on the other side to
+   * deserialize it. Using serialization defeats our DoS defence, so most apps
+   * shouldn't use it.
+   */
+  private Object writeReplace() throws ObjectStreamException {
+    return new LinkedHashMap<K, V>(this);
+  }
+}
diff --git a/gson/src/main/java/com/google/gson/internal/LinkedTreeMap.java b/gson/src/main/java/com/google/gson/internal/LinkedTreeMap.java
new file mode 100644
index 0000000000000000000000000000000000000000..80462742e30874a09ba7b27eb17fc6e2e6812a8f
--- /dev/null
+++ b/gson/src/main/java/com/google/gson/internal/LinkedTreeMap.java
@@ -0,0 +1,630 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ * Copyright (C) 2012 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.gson.internal;
+
+import java.io.ObjectStreamException;
+import java.io.Serializable;
+import java.util.AbstractMap;
+import java.util.AbstractSet;
+import java.util.Comparator;
+import java.util.ConcurrentModificationException;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.NoSuchElementException;
+import java.util.Set;
+
+/**
+ * A map of comparable keys to values. Unlike {@code TreeMap}, this class uses
+ * insertion order for iteration order. Comparison order is only used as an
+ * optimization for efficient insertion and removal.
+ *
+ * <p>This implementation was derived from Android 4.1's TreeMap class.
+ */
+public final class LinkedTreeMap<K, V> extends AbstractMap<K, V> implements Serializable {
+  @SuppressWarnings({ "unchecked", "rawtypes" }) // to avoid Comparable<Comparable<Comparable<...>>>
+  private static final Comparator<Comparable> NATURAL_ORDER = new Comparator<Comparable>() {
+    public int compare(Comparable a, Comparable b) {
+      return a.compareTo(b);
+    }
+  };
+
+  Comparator<? super K> comparator;
+  Node<K, V> root;
+  int size = 0;
+  int modCount = 0;
+
+  // Used to preserve iteration order
+  final Node<K, V> header = new Node<K, V>();
+
+  /**
+   * Create a natural order, empty tree map whose keys must be mutually
+   * comparable and non-null.
+   */
+  @SuppressWarnings("unchecked") // unsafe! this assumes K is comparable
+  public LinkedTreeMap() {
+    this((Comparator<? super K>) NATURAL_ORDER);
+  }
+
+  /**
+   * Create a tree map ordered by {@code comparator}. This map's keys may only
+   * be null if {@code comparator} permits.
+   *
+   * @param comparator the comparator to order elements with, or {@code null} to
+   *     use the natural ordering.
+   */
+  @SuppressWarnings({ "unchecked", "rawtypes" }) // unsafe! if comparator is null, this assumes K is comparable
+  public LinkedTreeMap(Comparator<? super K> comparator) {
+    this.comparator = comparator != null
+        ? comparator
+        : (Comparator) NATURAL_ORDER;
+  }
+
+  @Override public int size() {
+    return size;
+  }
+
+  @Override public V get(Object key) {
+    Node<K, V> node = findByObject(key);
+    return node != null ? node.value : null;
+  }
+
+  @Override public boolean containsKey(Object key) {
+    return findByObject(key) != null;
+  }
+
+  @Override public V put(K key, V value) {
+    if (key == null) {
+      throw new NullPointerException("key == null");
+    }
+    Node<K, V> created = find(key, true);
+    V result = created.value;
+    created.value = value;
+    return result;
+  }
+
+  @Override public void clear() {
+    root = null;
+    size = 0;
+    modCount++;
+
+    // Clear iteration order
+    Node<K, V> header = this.header;
+    header.next = header.prev = header;
+  }
+
+  @Override public V remove(Object key) {
+    Node<K, V> node = removeInternalByKey(key);
+    return node != null ? node.value : null;
+  }
+
+  /**
+   * Returns the node at or adjacent to the given key, creating it if requested.
+   *
+   * @throws ClassCastException if {@code key} and the tree's keys aren't
+   *     mutually comparable.
+   */
+  Node<K, V> find(K key, boolean create) {
+    Comparator<? super K> comparator = this.comparator;
+    Node<K, V> nearest = root;
+    int comparison = 0;
+
+    if (nearest != null) {
+      // Micro-optimization: avoid polymorphic calls to Comparator.compare().
+      @SuppressWarnings("unchecked") // Throws a ClassCastException below if there's trouble.
+          Comparable<Object> comparableKey = (comparator == NATURAL_ORDER)
+          ? (Comparable<Object>) key
+          : null;
+
+      while (true) {
+        comparison = (comparableKey != null)
+            ? comparableKey.compareTo(nearest.key)
+            : comparator.compare(key, nearest.key);
+
+        // We found the requested key.
+        if (comparison == 0) {
+          return nearest;
+        }
+
+        // If it exists, the key is in a subtree. Go deeper.
+        Node<K, V> child = (comparison < 0) ? nearest.left : nearest.right;
+        if (child == null) {
+          break;
+        }
+
+        nearest = child;
+      }
+    }
+
+    // The key doesn't exist in this tree.
+    if (!create) {
+      return null;
+    }
+
+    // Create the node and add it to the tree or the table.
+    Node<K, V> header = this.header;
+    Node<K, V> created;
+    if (nearest == null) {
+      // Check that the value is comparable if we didn't do any comparisons.
+      if (comparator == NATURAL_ORDER && !(key instanceof Comparable)) {
+        throw new ClassCastException(key.getClass().getName() + " is not Comparable");
+      }
+      created = new Node<K, V>(nearest, key, header, header.prev);
+      root = created;
+    } else {
+      created = new Node<K, V>(nearest, key, header, header.prev);
+      if (comparison < 0) { // nearest.key is higher
+        nearest.left = created;
+      } else { // comparison > 0, nearest.key is lower
+        nearest.right = created;
+      }
+      rebalance(nearest, true);
+    }
+    size++;
+    modCount++;
+
+    return created;
+  }
+
+  @SuppressWarnings("unchecked")
+  Node<K, V> findByObject(Object key) {
+    try {
+      return key != null ? find((K) key, false) : null;
+    } catch (ClassCastException e) {
+      return null;
+    }
+  }
+
+  /**
+   * Returns this map's entry that has the same key and value as {@code
+   * entry}, or null if this map has no such entry.
+   *
+   * <p>This method uses the comparator for key equality rather than {@code
+   * equals}. If this map's comparator isn't consistent with equals (such as
+   * {@code String.CASE_INSENSITIVE_ORDER}), then {@code remove()} and {@code
+   * contains()} will violate the collections API.
+   */
+  Node<K, V> findByEntry(Entry<?, ?> entry) {
+    Node<K, V> mine = findByObject(entry.getKey());
+    boolean valuesEqual = mine != null && equal(mine.value, entry.getValue());
+    return valuesEqual ? mine : null;
+  }
+
+  private boolean equal(Object a, Object b) {
+    return a == b || (a != null && a.equals(b));
+  }
+
+  /**
+   * Removes {@code node} from this tree, rearranging the tree's structure as
+   * necessary.
+   *
+   * @param unlink true to also unlink this node from the iteration linked list.
+   */
+  void removeInternal(Node<K, V> node, boolean unlink) {
+    if (unlink) {
+      node.prev.next = node.next;
+      node.next.prev = node.prev;
+    }
+
+    Node<K, V> left = node.left;
+    Node<K, V> right = node.right;
+    Node<K, V> originalParent = node.parent;
+    if (left != null && right != null) {
+
+      /*
+       * To remove a node with both left and right subtrees, move an
+       * adjacent node from one of those subtrees into this node's place.
+       *
+       * Removing the adjacent node may change this node's subtrees. This
+       * node may no longer have two subtrees once the adjacent node is
+       * gone!
+       */
+
+      Node<K, V> adjacent = (left.height > right.height) ? left.last() : right.first();
+      removeInternal(adjacent, false); // takes care of rebalance and size--
+
+      int leftHeight = 0;
+      left = node.left;
+      if (left != null) {
+        leftHeight = left.height;
+        adjacent.left = left;
+        left.parent = adjacent;
+        node.left = null;
+      }
+
+      int rightHeight = 0;
+      right = node.right;
+      if (right != null) {
+        rightHeight = right.height;
+        adjacent.right = right;
+        right.parent = adjacent;
+        node.right = null;
+      }
+
+      adjacent.height = Math.max(leftHeight, rightHeight) + 1;
+      replaceInParent(node, adjacent);
+      return;
+    } else if (left != null) {
+      replaceInParent(node, left);
+      node.left = null;
+    } else if (right != null) {
+      replaceInParent(node, right);
+      node.right = null;
+    } else {
+      replaceInParent(node, null);
+    }
+
+    rebalance(originalParent, false);
+    size--;
+    modCount++;
+  }
+
+  Node<K, V> removeInternalByKey(Object key) {
+    Node<K, V> node = findByObject(key);
+    if (node != null) {
+      removeInternal(node, true);
+    }
+    return node;
+  }
+
+  private void replaceInParent(Node<K, V> node, Node<K, V> replacement) {
+    Node<K, V> parent = node.parent;
+    node.parent = null;
+    if (replacement != null) {
+      replacement.parent = parent;
+    }
+
+    if (parent != null) {
+      if (parent.left == node) {
+        parent.left = replacement;
+      } else {
+        assert (parent.right == node);
+        parent.right = replacement;
+      }
+    } else {
+      root = replacement;
+    }
+  }
+
+  /**
+   * Rebalances the tree by making any AVL rotations necessary between the
+   * newly-unbalanced node and the tree's root.
+   *
+   * @param insert true if the node was unbalanced by an insert; false if it
+   *     was by a removal.
+   */
+  private void rebalance(Node<K, V> unbalanced, boolean insert) {
+    for (Node<K, V> node = unbalanced; node != null; node = node.parent) {
+      Node<K, V> left = node.left;
+      Node<K, V> right = node.right;
+      int leftHeight = left != null ? left.height : 0;
+      int rightHeight = right != null ? right.height : 0;
+
+      int delta = leftHeight - rightHeight;
+      if (delta == -2) {
+        Node<K, V> rightLeft = right.left;
+        Node<K, V> rightRight = right.right;
+        int rightRightHeight = rightRight != null ? rightRight.height : 0;
+        int rightLeftHeight = rightLeft != null ? rightLeft.height : 0;
+
+        int rightDelta = rightLeftHeight - rightRightHeight;
+        if (rightDelta == -1 || (rightDelta == 0 && !insert)) {
+          rotateLeft(node); // AVL right right
+        } else {
+          assert (rightDelta == 1);
+          rotateRight(right); // AVL right left
+          rotateLeft(node);
+        }
+        if (insert) {
+          break; // no further rotations will be necessary
+        }
+
+      } else if (delta == 2) {
+        Node<K, V> leftLeft = left.left;
+        Node<K, V> leftRight = left.right;
+        int leftRightHeight = leftRight != null ? leftRight.height : 0;
+        int leftLeftHeight = leftLeft != null ? leftLeft.height : 0;
+
+        int leftDelta = leftLeftHeight - leftRightHeight;
+        if (leftDelta == 1 || (leftDelta == 0 && !insert)) {
+          rotateRight(node); // AVL left left
+        } else {
+          assert (leftDelta == -1);
+          rotateLeft(left); // AVL left right
+          rotateRight(node);
+        }
+        if (insert) {
+          break; // no further rotations will be necessary
+        }
+
+      } else if (delta == 0) {
+        node.height = leftHeight + 1; // leftHeight == rightHeight
+        if (insert) {
+          break; // the insert caused balance, so rebalancing is done!
+        }
+
+      } else {
+        assert (delta == -1 || delta == 1);
+        node.height = Math.max(leftHeight, rightHeight) + 1;
+        if (!insert) {
+          break; // the height hasn't changed, so rebalancing is done!
+        }
+      }
+    }
+  }
+
+  /**
+   * Rotates the subtree so that its root's right child is the new root.
+   */
+  private void rotateLeft(Node<K, V> root) {
+    Node<K, V> left = root.left;
+    Node<K, V> pivot = root.right;
+    Node<K, V> pivotLeft = pivot.left;
+    Node<K, V> pivotRight = pivot.right;
+
+    // move the pivot's left child to the root's right
+    root.right = pivotLeft;
+    if (pivotLeft != null) {
+      pivotLeft.parent = root;
+    }
+
+    replaceInParent(root, pivot);
+
+    // move the root to the pivot's left
+    pivot.left = root;
+    root.parent = pivot;
+
+    // fix heights
+    root.height = Math.max(left != null ? left.height : 0,
+        pivotLeft != null ? pivotLeft.height : 0) + 1;
+    pivot.height = Math.max(root.height,
+        pivotRight != null ? pivotRight.height : 0) + 1;
+  }
+
+  /**
+   * Rotates the subtree so that its root's left child is the new root.
+   */
+  private void rotateRight(Node<K, V> root) {
+    Node<K, V> pivot = root.left;
+    Node<K, V> right = root.right;
+    Node<K, V> pivotLeft = pivot.left;
+    Node<K, V> pivotRight = pivot.right;
+
+    // move the pivot's right child to the root's left
+    root.left = pivotRight;
+    if (pivotRight != null) {
+      pivotRight.parent = root;
+    }
+
+    replaceInParent(root, pivot);
+
+    // move the root to the pivot's right
+    pivot.right = root;
+    root.parent = pivot;
+
+    // fixup heights
+    root.height = Math.max(right != null ? right.height : 0,
+        pivotRight != null ? pivotRight.height : 0) + 1;
+    pivot.height = Math.max(root.height,
+        pivotLeft != null ? pivotLeft.height : 0) + 1;
+  }
+
+  private EntrySet entrySet;
+  private KeySet keySet;
+
+  @Override public Set<Entry<K, V>> entrySet() {
+    EntrySet result = entrySet;
+    return result != null ? result : (entrySet = new EntrySet());
+  }
+
+  @Override public Set<K> keySet() {
+    KeySet result = keySet;
+    return result != null ? result : (keySet = new KeySet());
+  }
+
+  static final class Node<K, V> implements Entry<K, V> {
+    Node<K, V> parent;
+    Node<K, V> left;
+    Node<K, V> right;
+    Node<K, V> next;
+    Node<K, V> prev;
+    final K key;
+    V value;
+    int height;
+
+    /** Create the header entry */
+    Node() {
+      key = null;
+      next = prev = this;
+    }
+
+    /** Create a regular entry */
+    Node(Node<K, V> parent, K key, Node<K, V> next, Node<K, V> prev) {
+      this.parent = parent;
+      this.key = key;
+      this.height = 1;
+      this.next = next;
+      this.prev = prev;
+      prev.next = this;
+      next.prev = this;
+    }
+
+    public K getKey() {
+      return key;
+    }
+
+    public V getValue() {
+      return value;
+    }
+
+    public V setValue(V value) {
+      V oldValue = this.value;
+      this.value = value;
+      return oldValue;
+    }
+
+    @SuppressWarnings("rawtypes")
+    @Override public boolean equals(Object o) {
+      if (o instanceof Entry) {
+        Entry other = (Entry) o;
+        return (key == null ? other.getKey() == null : key.equals(other.getKey()))
+            && (value == null ? other.getValue() == null : value.equals(other.getValue()));
+      }
+      return false;
+    }
+
+    @Override public int hashCode() {
+      return (key == null ? 0 : key.hashCode())
+          ^ (value == null ? 0 : value.hashCode());
+    }
+
+    @Override public String toString() {
+      return key + "=" + value;
+    }
+
+    /**
+     * Returns the first node in this subtree.
+     */
+    public Node<K, V> first() {
+      Node<K, V> node = this;
+      Node<K, V> child = node.left;
+      while (child != null) {
+        node = child;
+        child = node.left;
+      }
+      return node;
+    }
+
+    /**
+     * Returns the last node in this subtree.
+     */
+    public Node<K, V> last() {
+      Node<K, V> node = this;
+      Node<K, V> child = node.right;
+      while (child != null) {
+        node = child;
+        child = node.right;
+      }
+      return node;
+    }
+  }
+
+  private abstract class LinkedTreeMapIterator<T> implements Iterator<T> {
+    Node<K, V> next = header.next;
+    Node<K, V> lastReturned = null;
+    int expectedModCount = modCount;
+
+    LinkedTreeMapIterator() {
+    }
+
+    public final boolean hasNext() {
+      return next != header;
+    }
+
+    final Node<K, V> nextNode() {
+      Node<K, V> e = next;
+      if (e == header) {
+        throw new NoSuchElementException();
+      }
+      if (modCount != expectedModCount) {
+        throw new ConcurrentModificationException();
+      }
+      next = e.next;
+      return lastReturned = e;
+    }
+
+    public final void remove() {
+      if (lastReturned == null) {
+        throw new IllegalStateException();
+      }
+      removeInternal(lastReturned, true);
+      lastReturned = null;
+      expectedModCount = modCount;
+    }
+  }
+
+  class EntrySet extends AbstractSet<Entry<K, V>> {
+    @Override public int size() {
+      return size;
+    }
+
+    @Override public Iterator<Entry<K, V>> iterator() {
+      return new LinkedTreeMapIterator<Entry<K, V>>() {
+        public Entry<K, V> next() {
+          return nextNode();
+        }
+      };
+    }
+
+    @Override public boolean contains(Object o) {
+      return o instanceof Entry && findByEntry((Entry<?, ?>) o) != null;
+    }
+
+    @Override public boolean remove(Object o) {
+      if (!(o instanceof Entry)) {
+        return false;
+      }
+
+      Node<K, V> node = findByEntry((Entry<?, ?>) o);
+      if (node == null) {
+        return false;
+      }
+      removeInternal(node, true);
+      return true;
+    }
+
+    @Override public void clear() {
+      LinkedTreeMap.this.clear();
+    }
+  }
+
+  final class KeySet extends AbstractSet<K> {
+    @Override public int size() {
+      return size;
+    }
+
+    @Override public Iterator<K> iterator() {
+      return new LinkedTreeMapIterator<K>() {
+        public K next() {
+          return nextNode().key;
+        }
+      };
+    }
+
+    @Override public boolean contains(Object o) {
+      return containsKey(o);
+    }
+
+    @Override public boolean remove(Object key) {
+      return removeInternalByKey(key) != null;
+    }
+
+    @Override public void clear() {
+      LinkedTreeMap.this.clear();
+    }
+  }
+
+  /**
+   * If somebody is unlucky enough to have to serialize one of these, serialize
+   * it as a LinkedHashMap so that they won't need Gson on the other side to
+   * deserialize it. Using serialization defeats our DoS defence, so most apps
+   * shouldn't use it.
+   */
+  private Object writeReplace() throws ObjectStreamException {
+    return new LinkedHashMap<K, V>(this);
+  }
+}
diff --git a/gson/src/main/java/com/google/gson/internal/ObjectConstructor.java b/gson/src/main/java/com/google/gson/internal/ObjectConstructor.java
new file mode 100644
index 0000000000000000000000000000000000000000..6ef20607fe159513bbd6fdf7735501f18f528e04
--- /dev/null
+++ b/gson/src/main/java/com/google/gson/internal/ObjectConstructor.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.gson.internal;
+
+/**
+ * Defines a generic object construction factory.  The purpose of this class
+ * is to construct a default instance of a class that can be used for object
+ * navigation while deserialization from its JSON representation.
+ *
+ * @author Inderjeet Singh
+ * @author Joel Leitch
+ */
+public interface ObjectConstructor<T> {
+
+  /**
+   * Returns a new instance.
+   */
+  public T construct();
+}
\ No newline at end of file
diff --git a/gson/src/main/java/com/google/gson/internal/PreJava9DateFormatProvider.java b/gson/src/main/java/com/google/gson/internal/PreJava9DateFormatProvider.java
new file mode 100644
index 0000000000000000000000000000000000000000..beb527c9eec088876781b84b768526f2f931c22f
--- /dev/null
+++ b/gson/src/main/java/com/google/gson/internal/PreJava9DateFormatProvider.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2017 The Gson authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.google.gson.internal;
+
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.Locale;
+
+/**
+ * Provides DateFormats for US locale with patterns which were the default ones before Java 9.
+ */
+public class PreJava9DateFormatProvider {
+
+  /**
+   * Returns the same DateFormat as {@code DateFormat.getDateInstance(style, Locale.US)} in Java 8 or below.
+   */
+  public static DateFormat getUSDateFormat(int style) {
+    return new SimpleDateFormat(getDateFormatPattern(style), Locale.US);
+  }
+
+  /**
+   * Returns the same DateFormat as {@code DateFormat.getDateTimeInstance(dateStyle, timeStyle, Locale.US)}
+   * in Java 8 or below.
+   */
+  public static DateFormat getUSDateTimeFormat(int dateStyle, int timeStyle) {
+    String pattern = getDatePartOfDateTimePattern(dateStyle) + " " + getTimePartOfDateTimePattern(timeStyle);
+    return new SimpleDateFormat(pattern, Locale.US);
+  }
+
+  private static String getDateFormatPattern(int style) {
+    switch (style) {
+    case DateFormat.SHORT:
+      return "M/d/yy";
+    case DateFormat.MEDIUM:
+      return "MMM d, y";
+    case DateFormat.LONG:
+      return "MMMM d, y";
+    case DateFormat.FULL:
+      return "EEEE, MMMM d, y";
+    default:
+      throw new IllegalArgumentException("Unknown DateFormat style: " + style);
+    }
+  }
+
+  private static String getDatePartOfDateTimePattern(int dateStyle) {
+    switch (dateStyle) {
+    case DateFormat.SHORT:
+      return "M/d/yy";
+    case DateFormat.MEDIUM:
+      return "MMM d, yyyy";
+    case DateFormat.LONG:
+      return "MMMM d, yyyy";
+    case DateFormat.FULL:
+      return "EEEE, MMMM d, yyyy";
+    default:
+      throw new IllegalArgumentException("Unknown DateFormat style: " + dateStyle);
+    }
+  }
+
+  private static String getTimePartOfDateTimePattern(int timeStyle) {
+    switch (timeStyle) {
+    case DateFormat.SHORT:
+      return "h:mm a";
+    case DateFormat.MEDIUM:
+      return "h:mm:ss a";
+    case DateFormat.FULL:
+    case DateFormat.LONG:
+      return "h:mm:ss a z";
+    default:
+      throw new IllegalArgumentException("Unknown DateFormat style: " + timeStyle);
+    }
+  }
+}
diff --git a/gson/src/main/java/com/google/gson/internal/Primitives.java b/gson/src/main/java/com/google/gson/internal/Primitives.java
new file mode 100644
index 0000000000000000000000000000000000000000..2506fd0a53099482b59657a4d38c38ba7936c712
--- /dev/null
+++ b/gson/src/main/java/com/google/gson/internal/Primitives.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.gson.internal;
+
+import java.lang.reflect.Type;
+
+/**
+ * Contains static utility methods pertaining to primitive types and their
+ * corresponding wrapper types.
+ *
+ * @author Kevin Bourrillion
+ */
+public final class Primitives {
+  private Primitives() {}
+
+  /**
+   * Returns true if this type is a primitive.
+   */
+  public static boolean isPrimitive(Type type) {
+    return type instanceof Class<?> && ((Class<?>) type).isPrimitive();
+  }
+
+  /**
+   * Returns {@code true} if {@code type} is one of the nine
+   * primitive-wrapper types, such as {@link Integer}.
+   *
+   * @see Class#isPrimitive
+   */
+  public static boolean isWrapperType(Type type) {
+    return type == Integer.class
+        || type == Float.class
+        || type == Byte.class
+        || type == Double.class
+        || type == Long.class
+        || type == Character.class
+        || type == Boolean.class
+        || type == Short.class
+        || type == Void.class;
+  }
+
+  /**
+   * Returns the corresponding wrapper type of {@code type} if it is a primitive
+   * type; otherwise returns {@code type} itself. Idempotent.
+   * <pre>
+   *     wrap(int.class) == Integer.class
+   *     wrap(Integer.class) == Integer.class
+   *     wrap(String.class) == String.class
+   * </pre>
+   */
+  @SuppressWarnings("unchecked")
+  public static <T> Class<T> wrap(Class<T> type) {
+    if (type == int.class) return (Class<T>) Integer.class;
+    if (type == float.class) return (Class<T>) Float.class;
+    if (type == byte.class) return (Class<T>) Byte.class;
+    if (type == double.class) return (Class<T>) Double.class;
+    if (type == long.class) return (Class<T>) Long.class;
+    if (type == char.class) return (Class<T>) Character.class;
+    if (type == boolean.class) return (Class<T>) Boolean.class;
+    if (type == short.class) return (Class<T>) Short.class;
+    if (type == void.class) return (Class<T>) Void.class;
+    return type;
+  }
+
+  /**
+   * Returns the corresponding primitive type of {@code type} if it is a
+   * wrapper type; otherwise returns {@code type} itself. Idempotent.
+   * <pre>
+   *     unwrap(Integer.class) == int.class
+   *     unwrap(int.class) == int.class
+   *     unwrap(String.class) == String.class
+   * </pre>
+   */
+  @SuppressWarnings("unchecked")
+  public static <T> Class<T> unwrap(Class<T> type) {
+    if (type == Integer.class) return (Class<T>) int.class;
+    if (type == Float.class) return (Class<T>) float.class;
+    if (type == Byte.class) return (Class<T>) byte.class;
+    if (type == Double.class) return (Class<T>) double.class;
+    if (type == Long.class) return (Class<T>) long.class;
+    if (type == Character.class) return (Class<T>) char.class;
+    if (type == Boolean.class) return (Class<T>) boolean.class;
+    if (type == Short.class) return (Class<T>) short.class;
+    if (type == Void.class) return (Class<T>) void.class;
+    return type;
+  }
+}
diff --git a/gson/src/main/java/com/google/gson/internal/Streams.java b/gson/src/main/java/com/google/gson/internal/Streams.java
new file mode 100644
index 0000000000000000000000000000000000000000..ac99910a9795b64fe8371d223cf6f48b29522ece
--- /dev/null
+++ b/gson/src/main/java/com/google/gson/internal/Streams.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.gson.internal;
+
+import com.google.gson.JsonElement;
+import com.google.gson.JsonIOException;
+import com.google.gson.JsonNull;
+import com.google.gson.JsonParseException;
+import com.google.gson.JsonSyntaxException;
+import com.google.gson.internal.bind.TypeAdapters;
+import com.google.gson.stream.JsonReader;
+import com.google.gson.stream.JsonWriter;
+import com.google.gson.stream.MalformedJsonException;
+import java.io.EOFException;
+import java.io.IOException;
+import java.io.Writer;
+
+/**
+ * Reads and writes GSON parse trees over streams.
+ */
+public final class Streams {
+  private Streams() {
+    throw new UnsupportedOperationException();
+  }
+
+  /**
+   * Takes a reader in any state and returns the next value as a JsonElement.
+   */
+  public static JsonElement parse(JsonReader reader) throws JsonParseException {
+    boolean isEmpty = true;
+    try {
+      reader.peek();
+      isEmpty = false;
+      return TypeAdapters.JSON_ELEMENT.read(reader);
+    } catch (EOFException e) {
+      /*
+       * For compatibility with JSON 1.5 and earlier, we return a JsonNull for
+       * empty documents instead of throwing.
+       */
+      if (isEmpty) {
+        return JsonNull.INSTANCE;
+      }
+      // The stream ended prematurely so it is likely a syntax error.
+      throw new JsonSyntaxException(e);
+    } catch (MalformedJsonException e) {
+      throw new JsonSyntaxException(e);
+    } catch (IOException e) {
+      throw new JsonIOException(e);
+    } catch (NumberFormatException e) {
+      throw new JsonSyntaxException(e);
+    }
+  }
+
+  /**
+   * Writes the JSON element to the writer, recursively.
+   */
+  public static void write(JsonElement element, JsonWriter writer) throws IOException {
+    TypeAdapters.JSON_ELEMENT.write(writer, element);
+  }
+
+  public static Writer writerForAppendable(Appendable appendable) {
+    return appendable instanceof Writer ? (Writer) appendable : new AppendableWriter(appendable);
+  }
+
+  /**
+   * Adapts an {@link Appendable} so it can be passed anywhere a {@link Writer}
+   * is used.
+   */
+  private static final class AppendableWriter extends Writer {
+    private final Appendable appendable;
+    private final CurrentWrite currentWrite = new CurrentWrite();
+
+    AppendableWriter(Appendable appendable) {
+      this.appendable = appendable;
+    }
+
+    @Override public void write(char[] chars, int offset, int length) throws IOException {
+      currentWrite.chars = chars;
+      appendable.append(currentWrite, offset, offset + length);
+    }
+
+    @Override public void write(int i) throws IOException {
+      appendable.append((char) i);
+    }
+
+    @Override public void flush() {}
+    @Override public void close() {}
+
+    /**
+     * A mutable char sequence pointing at a single char[].
+     */
+    static class CurrentWrite implements CharSequence {
+      char[] chars;
+      public int length() {
+        return chars.length;
+      }
+      public char charAt(int i) {
+        return chars[i];
+      }
+      public CharSequence subSequence(int start, int end) {
+        return new String(chars, start, end - start);
+      }
+    }
+  }
+
+}
diff --git a/gson/src/main/java/com/google/gson/internal/UnsafeAllocator.java b/gson/src/main/java/com/google/gson/internal/UnsafeAllocator.java
new file mode 100644
index 0000000000000000000000000000000000000000..999a2b57ef0240af79d37dfa5461fc69c53507d0
--- /dev/null
+++ b/gson/src/main/java/com/google/gson/internal/UnsafeAllocator.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2011 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.gson.internal;
+
+import java.io.ObjectInputStream;
+import java.io.ObjectStreamClass;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+
+/**
+ * Do sneaky things to allocate objects without invoking their constructors.
+ *
+ * @author Joel Leitch
+ * @author Jesse Wilson
+ */
+public abstract class UnsafeAllocator {
+  public abstract <T> T newInstance(Class<T> c) throws Exception;
+
+  public static UnsafeAllocator create() {
+    // try JVM
+    // public class Unsafe {
+    //   public Object allocateInstance(Class<?> type);
+    // }
+    try {
+      Class<?> unsafeClass = Class.forName("sun.misc.Unsafe");
+      Field f = unsafeClass.getDeclaredField("theUnsafe");
+      f.setAccessible(true);
+      final Object unsafe = f.get(null);
+      final Method allocateInstance = unsafeClass.getMethod("allocateInstance", Class.class);
+      return new UnsafeAllocator() {
+        @Override
+        @SuppressWarnings("unchecked")
+        public <T> T newInstance(Class<T> c) throws Exception {
+          assertInstantiable(c);
+          return (T) allocateInstance.invoke(unsafe, c);
+        }
+      };
+    } catch (Exception ignored) {
+    }
+
+    // try dalvikvm, post-gingerbread
+    // public class ObjectStreamClass {
+    //   private static native int getConstructorId(Class<?> c);
+    //   private static native Object newInstance(Class<?> instantiationClass, int methodId);
+    // }
+    try {
+      Method getConstructorId = ObjectStreamClass.class
+          .getDeclaredMethod("getConstructorId", Class.class);
+      getConstructorId.setAccessible(true);
+      final int constructorId = (Integer) getConstructorId.invoke(null, Object.class);
+      final Method newInstance = ObjectStreamClass.class
+          .getDeclaredMethod("newInstance", Class.class, int.class);
+      newInstance.setAccessible(true);
+      return new UnsafeAllocator() {
+        @Override
+        @SuppressWarnings("unchecked")
+        public <T> T newInstance(Class<T> c) throws Exception {
+          assertInstantiable(c);
+          return (T) newInstance.invoke(null, c, constructorId);
+        }
+      };
+    } catch (Exception ignored) {
+    }
+
+    // try dalvikvm, pre-gingerbread
+    // public class ObjectInputStream {
+    //   private static native Object newInstance(
+    //     Class<?> instantiationClass, Class<?> constructorClass);
+    // }
+    try {
+      final Method newInstance = ObjectInputStream.class
+          .getDeclaredMethod("newInstance", Class.class, Class.class);
+      newInstance.setAccessible(true);
+      return new UnsafeAllocator() {
+        @Override
+        @SuppressWarnings("unchecked")
+        public <T> T newInstance(Class<T> c) throws Exception {
+          assertInstantiable(c);
+          return (T) newInstance.invoke(null, c, Object.class);
+        }
+      };
+    } catch (Exception ignored) {
+    }
+
+    // give up
+    return new UnsafeAllocator() {
+      @Override
+      public <T> T newInstance(Class<T> c) {
+        throw new UnsupportedOperationException("Cannot allocate " + c);
+      }
+    };
+  }
+
+  /**
+   * Check if the class can be instantiated by unsafe allocator. If the instance has interface or abstract modifiers
+   * throw an {@link java.lang.UnsupportedOperationException}
+   * @param c instance of the class to be checked
+   */
+  static void assertInstantiable(Class<?> c) {
+    int modifiers = c.getModifiers();
+    if (Modifier.isInterface(modifiers)) {
+      throw new UnsupportedOperationException("Interface can't be instantiated! Interface name: " + c.getName());
+    }
+    if (Modifier.isAbstract(modifiers)) {
+      throw new UnsupportedOperationException("Abstract class can't be instantiated! Class name: " + c.getName());
+    }
+  }
+}
diff --git a/gson/src/main/java/com/google/gson/internal/bind/ArrayTypeAdapter.java b/gson/src/main/java/com/google/gson/internal/bind/ArrayTypeAdapter.java
new file mode 100644
index 0000000000000000000000000000000000000000..56101706b3d4ce2ec7a61bfab1b8826172d27d86
--- /dev/null
+++ b/gson/src/main/java/com/google/gson/internal/bind/ArrayTypeAdapter.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2011 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.gson.internal.bind;
+
+import java.io.IOException;
+import java.lang.reflect.Array;
+import java.lang.reflect.GenericArrayType;
+import java.lang.reflect.Type;
+import java.util.ArrayList;
+import java.util.List;
+
+import com.google.gson.Gson;
+import com.google.gson.TypeAdapter;
+import com.google.gson.TypeAdapterFactory;
+import com.google.gson.internal.$Gson$Types;
+import com.google.gson.reflect.TypeToken;
+import com.google.gson.stream.JsonReader;
+import com.google.gson.stream.JsonToken;
+import com.google.gson.stream.JsonWriter;
+
+/**
+ * Adapt an array of objects.
+ */
+public final class ArrayTypeAdapter<E> extends TypeAdapter<Object> {
+  public static final TypeAdapterFactory FACTORY = new TypeAdapterFactory() {
+    @SuppressWarnings({"unchecked", "rawtypes"})
+    @Override public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) {
+      Type type = typeToken.getType();
+      if (!(type instanceof GenericArrayType || type instanceof Class && ((Class<?>) type).isArray())) {
+        return null;
+      }
+
+      Type componentType = $Gson$Types.getArrayComponentType(type);
+      TypeAdapter<?> componentTypeAdapter = gson.getAdapter(TypeToken.get(componentType));
+      return new ArrayTypeAdapter(
+              gson, componentTypeAdapter, $Gson$Types.getRawType(componentType));
+    }
+  };
+
+  private final Class<E> componentType;
+  private final TypeAdapter<E> componentTypeAdapter;
+
+  public ArrayTypeAdapter(Gson context, TypeAdapter<E> componentTypeAdapter, Class<E> componentType) {
+    this.componentTypeAdapter =
+      new TypeAdapterRuntimeTypeWrapper<E>(context, componentTypeAdapter, componentType);
+    this.componentType = componentType;
+  }
+
+  @Override public Object read(JsonReader in) throws IOException {
+    if (in.peek() == JsonToken.NULL) {
+      in.nextNull();
+      return null;
+    }
+
+    List<E> list = new ArrayList<E>();
+    in.beginArray();
+    while (in.hasNext()) {
+      E instance = componentTypeAdapter.read(in);
+      list.add(instance);
+    }
+    in.endArray();
+
+    int size = list.size();
+    Object array = Array.newInstance(componentType, size);
+    for (int i = 0; i < size; i++) {
+      Array.set(array, i, list.get(i));
+    }
+    return array;
+  }
+
+  @SuppressWarnings("unchecked")
+  @Override public void write(JsonWriter out, Object array) throws IOException {
+    if (array == null) {
+      out.nullValue();
+      return;
+    }
+
+    out.beginArray();
+    for (int i = 0, length = Array.getLength(array); i < length; i++) {
+      E value = (E) Array.get(array, i);
+      componentTypeAdapter.write(out, value);
+    }
+    out.endArray();
+  }
+}
diff --git a/gson/src/main/java/com/google/gson/internal/bind/CollectionTypeAdapterFactory.java b/gson/src/main/java/com/google/gson/internal/bind/CollectionTypeAdapterFactory.java
new file mode 100644
index 0000000000000000000000000000000000000000..1d57844a4e61a6ffb5f93a84e8265352cca4b77b
--- /dev/null
+++ b/gson/src/main/java/com/google/gson/internal/bind/CollectionTypeAdapterFactory.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2011 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.gson.internal.bind;
+
+import com.google.gson.Gson;
+import com.google.gson.TypeAdapter;
+import com.google.gson.TypeAdapterFactory;
+import com.google.gson.internal.$Gson$Types;
+import com.google.gson.internal.ConstructorConstructor;
+import com.google.gson.internal.ObjectConstructor;
+import com.google.gson.reflect.TypeToken;
+import com.google.gson.stream.JsonReader;
+import com.google.gson.stream.JsonToken;
+import com.google.gson.stream.JsonWriter;
+import java.io.IOException;
+import java.lang.reflect.Type;
+import java.util.Collection;
+
+/**
+ * Adapt a homogeneous collection of objects.
+ */
+public final class CollectionTypeAdapterFactory implements TypeAdapterFactory {
+  private final ConstructorConstructor constructorConstructor;
+
+  public CollectionTypeAdapterFactory(ConstructorConstructor constructorConstructor) {
+    this.constructorConstructor = constructorConstructor;
+  }
+
+  @Override
+  public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) {
+    Type type = typeToken.getType();
+
+    Class<? super T> rawType = typeToken.getRawType();
+    if (!Collection.class.isAssignableFrom(rawType)) {
+      return null;
+    }
+
+    Type elementType = $Gson$Types.getCollectionElementType(type, rawType);
+    TypeAdapter<?> elementTypeAdapter = gson.getAdapter(TypeToken.get(elementType));
+    ObjectConstructor<T> constructor = constructorConstructor.get(typeToken);
+
+    @SuppressWarnings({"unchecked", "rawtypes"}) // create() doesn't define a type parameter
+    TypeAdapter<T> result = new Adapter(gson, elementType, elementTypeAdapter, constructor);
+    return result;
+  }
+
+  private static final class Adapter<E> extends TypeAdapter<Collection<E>> {
+    private final TypeAdapter<E> elementTypeAdapter;
+    private final ObjectConstructor<? extends Collection<E>> constructor;
+
+    public Adapter(Gson context, Type elementType,
+        TypeAdapter<E> elementTypeAdapter,
+        ObjectConstructor<? extends Collection<E>> constructor) {
+      this.elementTypeAdapter =
+          new TypeAdapterRuntimeTypeWrapper<E>(context, elementTypeAdapter, elementType);
+      this.constructor = constructor;
+    }
+
+    @Override public Collection<E> read(JsonReader in) throws IOException {
+      if (in.peek() == JsonToken.NULL) {
+        in.nextNull();
+        return null;
+      }
+
+      Collection<E> collection = constructor.construct();
+      in.beginArray();
+      while (in.hasNext()) {
+        E instance = elementTypeAdapter.read(in);
+        collection.add(instance);
+      }
+      in.endArray();
+      return collection;
+    }
+
+    @Override public void write(JsonWriter out, Collection<E> collection) throws IOException {
+      if (collection == null) {
+        out.nullValue();
+        return;
+      }
+
+      out.beginArray();
+      for (E element : collection) {
+        elementTypeAdapter.write(out, element);
+      }
+      out.endArray();
+    }
+  }
+}
diff --git a/gson/src/main/java/com/google/gson/internal/bind/DateTypeAdapter.java b/gson/src/main/java/com/google/gson/internal/bind/DateTypeAdapter.java
new file mode 100644
index 0000000000000000000000000000000000000000..6e849690eaa8aec9db0c799908b00328691df3a8
--- /dev/null
+++ b/gson/src/main/java/com/google/gson/internal/bind/DateTypeAdapter.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2011 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.gson.internal.bind;
+
+import com.google.gson.Gson;
+import com.google.gson.JsonSyntaxException;
+import com.google.gson.TypeAdapter;
+import com.google.gson.TypeAdapterFactory;
+import com.google.gson.internal.JavaVersion;
+import com.google.gson.internal.PreJava9DateFormatProvider;
+import com.google.gson.internal.bind.util.ISO8601Utils;
+import com.google.gson.reflect.TypeToken;
+import com.google.gson.stream.JsonReader;
+import com.google.gson.stream.JsonToken;
+import com.google.gson.stream.JsonWriter;
+
+import java.io.IOException;
+import java.text.DateFormat;
+import java.text.ParseException;
+import java.text.ParsePosition;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import java.util.Locale;
+
+/**
+ * Adapter for Date. Although this class appears stateless, it is not.
+ * DateFormat captures its time zone and locale when it is created, which gives
+ * this class state. DateFormat isn't thread safe either, so this class has
+ * to synchronize its read and write methods.
+ */
+public final class DateTypeAdapter extends TypeAdapter<Date> {
+  public static final TypeAdapterFactory FACTORY = new TypeAdapterFactory() {
+    @SuppressWarnings("unchecked") // we use a runtime check to make sure the 'T's equal
+    @Override public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) {
+      return typeToken.getRawType() == Date.class ? (TypeAdapter<T>) new DateTypeAdapter() : null;
+    }
+  };
+
+  /**
+   * List of 1 or more different date formats used for de-serialization attempts.
+   * The first of them (default US format) is used for serialization as well.
+   */
+  private final List<DateFormat> dateFormats = new ArrayList<DateFormat>();
+
+  public DateTypeAdapter() {
+    dateFormats.add(DateFormat.getDateTimeInstance(DateFormat.DEFAULT, DateFormat.DEFAULT, Locale.US));
+    if (!Locale.getDefault().equals(Locale.US)) {
+      dateFormats.add(DateFormat.getDateTimeInstance(DateFormat.DEFAULT, DateFormat.DEFAULT));
+    }
+    if (JavaVersion.isJava9OrLater()) {
+      dateFormats.add(PreJava9DateFormatProvider.getUSDateTimeFormat(DateFormat.DEFAULT, DateFormat.DEFAULT));
+    }
+  }
+
+  @Override public Date read(JsonReader in) throws IOException {
+    if (in.peek() == JsonToken.NULL) {
+      in.nextNull();
+      return null;
+    }
+    return deserializeToDate(in.nextString());
+  }
+
+  private synchronized Date deserializeToDate(String json) {
+    for (DateFormat dateFormat : dateFormats) {
+      try {
+        return dateFormat.parse(json);
+      } catch (ParseException ignored) {}
+    }
+    try {
+    	return ISO8601Utils.parse(json, new ParsePosition(0));
+    } catch (ParseException e) {
+      throw new JsonSyntaxException(json, e);
+    }
+  }
+
+  @Override public synchronized void write(JsonWriter out, Date value) throws IOException {
+    if (value == null) {
+      out.nullValue();
+      return;
+    }
+    String dateFormatAsString = dateFormats.get(0).format(value);
+    out.value(dateFormatAsString);
+  }
+  
+  
+}
diff --git a/gson/src/main/java/com/google/gson/internal/bind/JsonAdapterAnnotationTypeAdapterFactory.java b/gson/src/main/java/com/google/gson/internal/bind/JsonAdapterAnnotationTypeAdapterFactory.java
new file mode 100644
index 0000000000000000000000000000000000000000..13a7bb7ebe5e90f7d5dd2b83c1436f7ca151fb07
--- /dev/null
+++ b/gson/src/main/java/com/google/gson/internal/bind/JsonAdapterAnnotationTypeAdapterFactory.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2014 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.gson.internal.bind;
+
+import com.google.gson.Gson;
+import com.google.gson.JsonDeserializer;
+import com.google.gson.JsonSerializer;
+import com.google.gson.TypeAdapter;
+import com.google.gson.TypeAdapterFactory;
+import com.google.gson.annotations.JsonAdapter;
+import com.google.gson.internal.ConstructorConstructor;
+import com.google.gson.reflect.TypeToken;
+
+/**
+ * Given a type T, looks for the annotation {@link JsonAdapter} and uses an instance of the
+ * specified class as the default type adapter.
+ *
+ * @since 2.3
+ */
+public final class JsonAdapterAnnotationTypeAdapterFactory implements TypeAdapterFactory {
+  private final ConstructorConstructor constructorConstructor;
+
+  public JsonAdapterAnnotationTypeAdapterFactory(ConstructorConstructor constructorConstructor) {
+    this.constructorConstructor = constructorConstructor;
+  }
+
+  @SuppressWarnings("unchecked")
+  @Override
+  public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> targetType) {
+    Class<? super T> rawType = targetType.getRawType();
+    JsonAdapter annotation = rawType.getAnnotation(JsonAdapter.class);
+    if (annotation == null) {
+      return null;
+    }
+    return (TypeAdapter<T>) getTypeAdapter(constructorConstructor, gson, targetType, annotation);
+  }
+
+  @SuppressWarnings({ "unchecked", "rawtypes" }) // Casts guarded by conditionals.
+  TypeAdapter<?> getTypeAdapter(ConstructorConstructor constructorConstructor, Gson gson,
+      TypeToken<?> type, JsonAdapter annotation) {
+    Object instance = constructorConstructor.get(TypeToken.get(annotation.value())).construct();
+
+    TypeAdapter<?> typeAdapter;
+    if (instance instanceof TypeAdapter) {
+      typeAdapter = (TypeAdapter<?>) instance;
+    } else if (instance instanceof TypeAdapterFactory) {
+      typeAdapter = ((TypeAdapterFactory) instance).create(gson, type);
+    } else if (instance instanceof JsonSerializer || instance instanceof JsonDeserializer) {
+      JsonSerializer<?> serializer = instance instanceof JsonSerializer
+          ? (JsonSerializer) instance
+          : null;
+      JsonDeserializer<?> deserializer = instance instanceof JsonDeserializer
+          ? (JsonDeserializer) instance
+          : null;
+      typeAdapter = new TreeTypeAdapter(serializer, deserializer, gson, type, null);
+    } else {
+      throw new IllegalArgumentException("Invalid attempt to bind an instance of "
+          + instance.getClass().getName() + " as a @JsonAdapter for " + type.toString()
+          + ". @JsonAdapter value must be a TypeAdapter, TypeAdapterFactory,"
+          + " JsonSerializer or JsonDeserializer.");
+    }
+
+    if (typeAdapter != null && annotation.nullSafe()) {
+      typeAdapter = typeAdapter.nullSafe();
+    }
+
+    return typeAdapter;
+  }
+}
diff --git a/gson/src/main/java/com/google/gson/internal/bind/JsonTreeReader.java b/gson/src/main/java/com/google/gson/internal/bind/JsonTreeReader.java
new file mode 100644
index 0000000000000000000000000000000000000000..a223754aedd68519c791c8843f661c63305263ed
--- /dev/null
+++ b/gson/src/main/java/com/google/gson/internal/bind/JsonTreeReader.java
@@ -0,0 +1,316 @@
+/*
+ * Copyright (C) 2011 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.gson.internal.bind;
+
+import com.google.gson.JsonArray;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonNull;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonPrimitive;
+import com.google.gson.stream.JsonReader;
+import com.google.gson.stream.JsonToken;
+import java.io.IOException;
+import java.io.Reader;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Arrays;
+
+/**
+ * This reader walks the elements of a JsonElement as if it was coming from a
+ * character stream.
+ *
+ * @author Jesse Wilson
+ */
+public final class JsonTreeReader extends JsonReader {
+  private static final Reader UNREADABLE_READER = new Reader() {
+    @Override public int read(char[] buffer, int offset, int count) throws IOException {
+      throw new AssertionError();
+    }
+    @Override public void close() throws IOException {
+      throw new AssertionError();
+    }
+  };
+  private static final Object SENTINEL_CLOSED = new Object();
+
+  /*
+   * The nesting stack. Using a manual array rather than an ArrayList saves 20%.
+   */
+  private Object[] stack = new Object[32];
+  private int stackSize = 0;
+
+  /*
+   * The path members. It corresponds directly to stack: At indices where the
+   * stack contains an object (EMPTY_OBJECT, DANGLING_NAME or NONEMPTY_OBJECT),
+   * pathNames contains the name at this scope. Where it contains an array
+   * (EMPTY_ARRAY, NONEMPTY_ARRAY) pathIndices contains the current index in
+   * that array. Otherwise the value is undefined, and we take advantage of that
+   * by incrementing pathIndices when doing so isn't useful.
+   */
+  private String[] pathNames = new String[32];
+  private int[] pathIndices = new int[32];
+
+  public JsonTreeReader(JsonElement element) {
+    super(UNREADABLE_READER);
+    push(element);
+  }
+
+  @Override public void beginArray() throws IOException {
+    expect(JsonToken.BEGIN_ARRAY);
+    JsonArray array = (JsonArray) peekStack();
+    push(array.iterator());
+    pathIndices[stackSize - 1] = 0;
+  }
+
+  @Override public void endArray() throws IOException {
+    expect(JsonToken.END_ARRAY);
+    popStack(); // empty iterator
+    popStack(); // array
+    if (stackSize > 0) {
+      pathIndices[stackSize - 1]++;
+    }
+  }
+
+  @Override public void beginObject() throws IOException {
+    expect(JsonToken.BEGIN_OBJECT);
+    JsonObject object = (JsonObject) peekStack();
+    push(object.entrySet().iterator());
+  }
+
+  @Override public void endObject() throws IOException {
+    expect(JsonToken.END_OBJECT);
+    popStack(); // empty iterator
+    popStack(); // object
+    if (stackSize > 0) {
+      pathIndices[stackSize - 1]++;
+    }
+  }
+
+  @Override public boolean hasNext() throws IOException {
+    JsonToken token = peek();
+    return token != JsonToken.END_OBJECT && token != JsonToken.END_ARRAY;
+  }
+
+  @Override public JsonToken peek() throws IOException {
+    if (stackSize == 0) {
+      return JsonToken.END_DOCUMENT;
+    }
+
+    Object o = peekStack();
+    if (o instanceof Iterator) {
+      boolean isObject = stack[stackSize - 2] instanceof JsonObject;
+      Iterator<?> iterator = (Iterator<?>) o;
+      if (iterator.hasNext()) {
+        if (isObject) {
+          return JsonToken.NAME;
+        } else {
+          push(iterator.next());
+          return peek();
+        }
+      } else {
+        return isObject ? JsonToken.END_OBJECT : JsonToken.END_ARRAY;
+      }
+    } else if (o instanceof JsonObject) {
+      return JsonToken.BEGIN_OBJECT;
+    } else if (o instanceof JsonArray) {
+      return JsonToken.BEGIN_ARRAY;
+    } else if (o instanceof JsonPrimitive) {
+      JsonPrimitive primitive = (JsonPrimitive) o;
+      if (primitive.isString()) {
+        return JsonToken.STRING;
+      } else if (primitive.isBoolean()) {
+        return JsonToken.BOOLEAN;
+      } else if (primitive.isNumber()) {
+        return JsonToken.NUMBER;
+      } else {
+        throw new AssertionError();
+      }
+    } else if (o instanceof JsonNull) {
+      return JsonToken.NULL;
+    } else if (o == SENTINEL_CLOSED) {
+      throw new IllegalStateException("JsonReader is closed");
+    } else {
+      throw new AssertionError();
+    }
+  }
+
+  private Object peekStack() {
+    return stack[stackSize - 1];
+  }
+
+  private Object popStack() {
+    Object result = stack[--stackSize];
+    stack[stackSize] = null;
+    return result;
+  }
+
+  private void expect(JsonToken expected) throws IOException {
+    if (peek() != expected) {
+      throw new IllegalStateException(
+          "Expected " + expected + " but was " + peek() + locationString());
+    }
+  }
+
+  @Override public String nextName() throws IOException {
+    expect(JsonToken.NAME);
+    Iterator<?> i = (Iterator<?>) peekStack();
+    Map.Entry<?, ?> entry = (Map.Entry<?, ?>) i.next();
+    String result = (String) entry.getKey();
+    pathNames[stackSize - 1] = result;
+    push(entry.getValue());
+    return result;
+  }
+
+  @Override public String nextString() throws IOException {
+    JsonToken token = peek();
+    if (token != JsonToken.STRING && token != JsonToken.NUMBER) {
+      throw new IllegalStateException(
+          "Expected " + JsonToken.STRING + " but was " + token + locationString());
+    }
+    String result = ((JsonPrimitive) popStack()).getAsString();
+    if (stackSize > 0) {
+      pathIndices[stackSize - 1]++;
+    }
+    return result;
+  }
+
+  @Override public boolean nextBoolean() throws IOException {
+    expect(JsonToken.BOOLEAN);
+    boolean result = ((JsonPrimitive) popStack()).getAsBoolean();
+    if (stackSize > 0) {
+      pathIndices[stackSize - 1]++;
+    }
+    return result;
+  }
+
+  @Override public void nextNull() throws IOException {
+    expect(JsonToken.NULL);
+    popStack();
+    if (stackSize > 0) {
+      pathIndices[stackSize - 1]++;
+    }
+  }
+
+  @Override public double nextDouble() throws IOException {
+    JsonToken token = peek();
+    if (token != JsonToken.NUMBER && token != JsonToken.STRING) {
+      throw new IllegalStateException(
+          "Expected " + JsonToken.NUMBER + " but was " + token + locationString());
+    }
+    double result = ((JsonPrimitive) peekStack()).getAsDouble();
+    if (!isLenient() && (Double.isNaN(result) || Double.isInfinite(result))) {
+      throw new NumberFormatException("JSON forbids NaN and infinities: " + result);
+    }
+    popStack();
+    if (stackSize > 0) {
+      pathIndices[stackSize - 1]++;
+    }
+    return result;
+  }
+
+  @Override public long nextLong() throws IOException {
+    JsonToken token = peek();
+    if (token != JsonToken.NUMBER && token != JsonToken.STRING) {
+      throw new IllegalStateException(
+          "Expected " + JsonToken.NUMBER + " but was " + token + locationString());
+    }
+    long result = ((JsonPrimitive) peekStack()).getAsLong();
+    popStack();
+    if (stackSize > 0) {
+      pathIndices[stackSize - 1]++;
+    }
+    return result;
+  }
+
+  @Override public int nextInt() throws IOException {
+    JsonToken token = peek();
+    if (token != JsonToken.NUMBER && token != JsonToken.STRING) {
+      throw new IllegalStateException(
+          "Expected " + JsonToken.NUMBER + " but was " + token + locationString());
+    }
+    int result = ((JsonPrimitive) peekStack()).getAsInt();
+    popStack();
+    if (stackSize > 0) {
+      pathIndices[stackSize - 1]++;
+    }
+    return result;
+  }
+
+  @Override public void close() throws IOException {
+    stack = new Object[] { SENTINEL_CLOSED };
+    stackSize = 1;
+  }
+
+  @Override public void skipValue() throws IOException {
+    if (peek() == JsonToken.NAME) {
+      nextName();
+      pathNames[stackSize - 2] = "null";
+    } else {
+      popStack();
+      if (stackSize > 0) {
+        pathNames[stackSize - 1] = "null";
+      }
+    }
+    if (stackSize > 0) {
+      pathIndices[stackSize - 1]++;
+    }
+  }
+
+  @Override public String toString() {
+    return getClass().getSimpleName();
+  }
+
+  public void promoteNameToValue() throws IOException {
+    expect(JsonToken.NAME);
+    Iterator<?> i = (Iterator<?>) peekStack();
+    Map.Entry<?, ?> entry = (Map.Entry<?, ?>) i.next();
+    push(entry.getValue());
+    push(new JsonPrimitive((String) entry.getKey()));
+  }
+
+  private void push(Object newTop) {
+    if (stackSize == stack.length) {
+      int newLength = stackSize * 2;
+      stack = Arrays.copyOf(stack, newLength);
+      pathIndices = Arrays.copyOf(pathIndices, newLength);
+      pathNames = Arrays.copyOf(pathNames, newLength);
+    }
+    stack[stackSize++] = newTop;
+  }
+
+  @Override public String getPath() {
+    StringBuilder result = new StringBuilder().append('$');
+    for (int i = 0; i < stackSize; i++) {
+      if (stack[i] instanceof JsonArray) {
+        if (stack[++i] instanceof Iterator) {
+          result.append('[').append(pathIndices[i]).append(']');
+        }
+      } else if (stack[i] instanceof JsonObject) {
+        if (stack[++i] instanceof Iterator) {
+          result.append('.');
+          if (pathNames[i] != null) {
+            result.append(pathNames[i]);
+          }
+        }
+      }
+    }
+    return result.toString();
+  }
+
+  private String locationString() {
+    return " at path " + getPath();
+  }
+}
diff --git a/gson/src/main/java/com/google/gson/internal/bind/JsonTreeWriter.java b/gson/src/main/java/com/google/gson/internal/bind/JsonTreeWriter.java
new file mode 100644
index 0000000000000000000000000000000000000000..51dc1f3a34b832a53340e310816467b602185d21
--- /dev/null
+++ b/gson/src/main/java/com/google/gson/internal/bind/JsonTreeWriter.java
@@ -0,0 +1,208 @@
+/*
+ * Copyright (C) 2011 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.gson.internal.bind;
+
+import com.google.gson.JsonArray;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonNull;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonPrimitive;
+import com.google.gson.stream.JsonWriter;
+import java.io.IOException;
+import java.io.Writer;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * This writer creates a JsonElement.
+ */
+public final class JsonTreeWriter extends JsonWriter {
+  private static final Writer UNWRITABLE_WRITER = new Writer() {
+    @Override public void write(char[] buffer, int offset, int counter) {
+      throw new AssertionError();
+    }
+    @Override public void flush() throws IOException {
+      throw new AssertionError();
+    }
+    @Override public void close() throws IOException {
+      throw new AssertionError();
+    }
+  };
+  /** Added to the top of the stack when this writer is closed to cause following ops to fail. */
+  private static final JsonPrimitive SENTINEL_CLOSED = new JsonPrimitive("closed");
+
+  /** The JsonElements and JsonArrays under modification, outermost to innermost. */
+  private final List<JsonElement> stack = new ArrayList<JsonElement>();
+
+  /** The name for the next JSON object value. If non-null, the top of the stack is a JsonObject. */
+  private String pendingName;
+
+  /** the JSON element constructed by this writer. */
+  private JsonElement product = JsonNull.INSTANCE; // TODO: is this really what we want?;
+
+  public JsonTreeWriter() {
+    super(UNWRITABLE_WRITER);
+  }
+
+  /**
+   * Returns the top level object produced by this writer.
+   */
+  public JsonElement get() {
+    if (!stack.isEmpty()) {
+      throw new IllegalStateException("Expected one JSON element but was " + stack);
+    }
+    return product;
+  }
+
+  private JsonElement peek() {
+    return stack.get(stack.size() - 1);
+  }
+
+  private void put(JsonElement value) {
+    if (pendingName != null) {
+      if (!value.isJsonNull() || getSerializeNulls()) {
+        JsonObject object = (JsonObject) peek();
+        object.add(pendingName, value);
+      }
+      pendingName = null;
+    } else if (stack.isEmpty()) {
+      product = value;
+    } else {
+      JsonElement element = peek();
+      if (element instanceof JsonArray) {
+        ((JsonArray) element).add(value);
+      } else {
+        throw new IllegalStateException();
+      }
+    }
+  }
+
+  @Override public JsonWriter beginArray() throws IOException {
+    JsonArray array = new JsonArray();
+    put(array);
+    stack.add(array);
+    return this;
+  }
+
+  @Override public JsonWriter endArray() throws IOException {
+    if (stack.isEmpty() || pendingName != null) {
+      throw new IllegalStateException();
+    }
+    JsonElement element = peek();
+    if (element instanceof JsonArray) {
+      stack.remove(stack.size() - 1);
+      return this;
+    }
+    throw new IllegalStateException();
+  }
+
+  @Override public JsonWriter beginObject() throws IOException {
+    JsonObject object = new JsonObject();
+    put(object);
+    stack.add(object);
+    return this;
+  }
+
+  @Override public JsonWriter endObject() throws IOException {
+    if (stack.isEmpty() || pendingName != null) {
+      throw new IllegalStateException();
+    }
+    JsonElement element = peek();
+    if (element instanceof JsonObject) {
+      stack.remove(stack.size() - 1);
+      return this;
+    }
+    throw new IllegalStateException();
+  }
+
+  @Override public JsonWriter name(String name) throws IOException {
+    if (stack.isEmpty() || pendingName != null) {
+      throw new IllegalStateException();
+    }
+    JsonElement element = peek();
+    if (element instanceof JsonObject) {
+      pendingName = name;
+      return this;
+    }
+    throw new IllegalStateException();
+  }
+
+  @Override public JsonWriter value(String value) throws IOException {
+    if (value == null) {
+      return nullValue();
+    }
+    put(new JsonPrimitive(value));
+    return this;
+  }
+
+  @Override public JsonWriter nullValue() throws IOException {
+    put(JsonNull.INSTANCE);
+    return this;
+  }
+
+  @Override public JsonWriter value(boolean value) throws IOException {
+    put(new JsonPrimitive(value));
+    return this;
+  }
+
+  @Override public JsonWriter value(Boolean value) throws IOException {
+    if (value == null) {
+      return nullValue();
+    }
+    put(new JsonPrimitive(value));
+    return this;
+  }
+
+  @Override public JsonWriter value(double value) throws IOException {
+    if (!isLenient() && (Double.isNaN(value) || Double.isInfinite(value))) {
+      throw new IllegalArgumentException("JSON forbids NaN and infinities: " + value);
+    }
+    put(new JsonPrimitive(value));
+    return this;
+  }
+
+  @Override public JsonWriter value(long value) throws IOException {
+    put(new JsonPrimitive(value));
+    return this;
+  }
+
+  @Override public JsonWriter value(Number value) throws IOException {
+    if (value == null) {
+      return nullValue();
+    }
+
+    if (!isLenient()) {
+      double d = value.doubleValue();
+      if (Double.isNaN(d) || Double.isInfinite(d)) {
+        throw new IllegalArgumentException("JSON forbids NaN and infinities: " + value);
+      }
+    }
+
+    put(new JsonPrimitive(value));
+    return this;
+  }
+
+  @Override public void flush() throws IOException {
+  }
+
+  @Override public void close() throws IOException {
+    if (!stack.isEmpty()) {
+      throw new IOException("Incomplete document");
+    }
+    stack.add(SENTINEL_CLOSED);
+  }
+}
diff --git a/gson/src/main/java/com/google/gson/internal/bind/MapTypeAdapterFactory.java b/gson/src/main/java/com/google/gson/internal/bind/MapTypeAdapterFactory.java
new file mode 100644
index 0000000000000000000000000000000000000000..5a34a5d5fb948938c058dba5b9d5ef2932e6e33d
--- /dev/null
+++ b/gson/src/main/java/com/google/gson/internal/bind/MapTypeAdapterFactory.java
@@ -0,0 +1,264 @@
+/*
+ * Copyright (C) 2011 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.gson.internal.bind;
+
+import com.google.gson.Gson;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonPrimitive;
+import com.google.gson.JsonSyntaxException;
+import com.google.gson.TypeAdapter;
+import com.google.gson.TypeAdapterFactory;
+import com.google.gson.internal.$Gson$Types;
+import com.google.gson.internal.ConstructorConstructor;
+import com.google.gson.internal.JsonReaderInternalAccess;
+import com.google.gson.internal.ObjectConstructor;
+import com.google.gson.internal.Streams;
+import com.google.gson.reflect.TypeToken;
+import com.google.gson.stream.JsonReader;
+import com.google.gson.stream.JsonToken;
+import com.google.gson.stream.JsonWriter;
+import java.io.IOException;
+import java.lang.reflect.Type;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Adapts maps to either JSON objects or JSON arrays.
+ *
+ * <h3>Maps as JSON objects</h3>
+ * For primitive keys or when complex map key serialization is not enabled, this
+ * converts Java {@link Map Maps} to JSON Objects. This requires that map keys
+ * can be serialized as strings; this is insufficient for some key types. For
+ * example, consider a map whose keys are points on a grid. The default JSON
+ * form encodes reasonably: <pre>   {@code
+ *   Map<Point, String> original = new LinkedHashMap<Point, String>();
+ *   original.put(new Point(5, 6), "a");
+ *   original.put(new Point(8, 8), "b");
+ *   System.out.println(gson.toJson(original, type));
+ * }</pre>
+ * The above code prints this JSON object:<pre>   {@code
+ *   {
+ *     "(5,6)": "a",
+ *     "(8,8)": "b"
+ *   }
+ * }</pre>
+ * But GSON is unable to deserialize this value because the JSON string name is
+ * just the {@link Object#toString() toString()} of the map key. Attempting to
+ * convert the above JSON to an object fails with a parse exception:
+ * <pre>com.google.gson.JsonParseException: Expecting object found: "(5,6)"
+ *   at com.google.gson.JsonObjectDeserializationVisitor.visitFieldUsingCustomHandler
+ *   at com.google.gson.ObjectNavigator.navigateClassFields
+ *   ...</pre>
+ *
+ * <h3>Maps as JSON arrays</h3>
+ * An alternative approach taken by this type adapter when it is required and
+ * complex map key serialization is enabled is to encode maps as arrays of map
+ * entries. Each map entry is a two element array containing a key and a value.
+ * This approach is more flexible because any type can be used as the map's key;
+ * not just strings. But it's also less portable because the receiver of such
+ * JSON must be aware of the map entry convention.
+ *
+ * <p>Register this adapter when you are creating your GSON instance.
+ * <pre>   {@code
+ *   Gson gson = new GsonBuilder()
+ *     .registerTypeAdapter(Map.class, new MapAsArrayTypeAdapter())
+ *     .create();
+ * }</pre>
+ * This will change the structure of the JSON emitted by the code above. Now we
+ * get an array. In this case the arrays elements are map entries:
+ * <pre>   {@code
+ *   [
+ *     [
+ *       {
+ *         "x": 5,
+ *         "y": 6
+ *       },
+ *       "a",
+ *     ],
+ *     [
+ *       {
+ *         "x": 8,
+ *         "y": 8
+ *       },
+ *       "b"
+ *     ]
+ *   ]
+ * }</pre>
+ * This format will serialize and deserialize just fine as long as this adapter
+ * is registered.
+ */
+public final class MapTypeAdapterFactory implements TypeAdapterFactory {
+  private final ConstructorConstructor constructorConstructor;
+  final boolean complexMapKeySerialization;
+
+  public MapTypeAdapterFactory(ConstructorConstructor constructorConstructor,
+      boolean complexMapKeySerialization) {
+    this.constructorConstructor = constructorConstructor;
+    this.complexMapKeySerialization = complexMapKeySerialization;
+  }
+
+  @Override public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) {
+    Type type = typeToken.getType();
+
+    Class<? super T> rawType = typeToken.getRawType();
+    if (!Map.class.isAssignableFrom(rawType)) {
+      return null;
+    }
+
+    Class<?> rawTypeOfSrc = $Gson$Types.getRawType(type);
+    Type[] keyAndValueTypes = $Gson$Types.getMapKeyAndValueTypes(type, rawTypeOfSrc);
+    TypeAdapter<?> keyAdapter = getKeyAdapter(gson, keyAndValueTypes[0]);
+    TypeAdapter<?> valueAdapter = gson.getAdapter(TypeToken.get(keyAndValueTypes[1]));
+    ObjectConstructor<T> constructor = constructorConstructor.get(typeToken);
+
+    @SuppressWarnings({"unchecked", "rawtypes"})
+    // we don't define a type parameter for the key or value types
+    TypeAdapter<T> result = new Adapter(gson, keyAndValueTypes[0], keyAdapter,
+        keyAndValueTypes[1], valueAdapter, constructor);
+    return result;
+  }
+
+  /**
+   * Returns a type adapter that writes the value as a string.
+   */
+  private TypeAdapter<?> getKeyAdapter(Gson context, Type keyType) {
+    return (keyType == boolean.class || keyType == Boolean.class)
+        ? TypeAdapters.BOOLEAN_AS_STRING
+        : context.getAdapter(TypeToken.get(keyType));
+  }
+
+  private final class Adapter<K, V> extends TypeAdapter<Map<K, V>> {
+    private final TypeAdapter<K> keyTypeAdapter;
+    private final TypeAdapter<V> valueTypeAdapter;
+    private final ObjectConstructor<? extends Map<K, V>> constructor;
+
+    public Adapter(Gson context, Type keyType, TypeAdapter<K> keyTypeAdapter,
+        Type valueType, TypeAdapter<V> valueTypeAdapter,
+        ObjectConstructor<? extends Map<K, V>> constructor) {
+      this.keyTypeAdapter =
+        new TypeAdapterRuntimeTypeWrapper<K>(context, keyTypeAdapter, keyType);
+      this.valueTypeAdapter =
+        new TypeAdapterRuntimeTypeWrapper<V>(context, valueTypeAdapter, valueType);
+      this.constructor = constructor;
+    }
+
+    @Override public Map<K, V> read(JsonReader in) throws IOException {
+      JsonToken peek = in.peek();
+      if (peek == JsonToken.NULL) {
+        in.nextNull();
+        return null;
+      }
+
+      Map<K, V> map = constructor.construct();
+
+      if (peek == JsonToken.BEGIN_ARRAY) {
+        in.beginArray();
+        while (in.hasNext()) {
+          in.beginArray(); // entry array
+          K key = keyTypeAdapter.read(in);
+          V value = valueTypeAdapter.read(in);
+          V replaced = map.put(key, value);
+          if (replaced != null) {
+            throw new JsonSyntaxException("duplicate key: " + key);
+          }
+          in.endArray();
+        }
+        in.endArray();
+      } else {
+        in.beginObject();
+        while (in.hasNext()) {
+          JsonReaderInternalAccess.INSTANCE.promoteNameToValue(in);
+          K key = keyTypeAdapter.read(in);
+          V value = valueTypeAdapter.read(in);
+          V replaced = map.put(key, value);
+          if (replaced != null) {
+            throw new JsonSyntaxException("duplicate key: " + key);
+          }
+        }
+        in.endObject();
+      }
+      return map;
+    }
+
+    @Override public void write(JsonWriter out, Map<K, V> map) throws IOException {
+      if (map == null) {
+        out.nullValue();
+        return;
+      }
+
+      if (!complexMapKeySerialization) {
+        out.beginObject();
+        for (Map.Entry<K, V> entry : map.entrySet()) {
+          out.name(String.valueOf(entry.getKey()));
+          valueTypeAdapter.write(out, entry.getValue());
+        }
+        out.endObject();
+        return;
+      }
+
+      boolean hasComplexKeys = false;
+      List<JsonElement> keys = new ArrayList<JsonElement>(map.size());
+
+      List<V> values = new ArrayList<V>(map.size());
+      for (Map.Entry<K, V> entry : map.entrySet()) {
+        JsonElement keyElement = keyTypeAdapter.toJsonTree(entry.getKey());
+        keys.add(keyElement);
+        values.add(entry.getValue());
+        hasComplexKeys |= keyElement.isJsonArray() || keyElement.isJsonObject();
+      }
+
+      if (hasComplexKeys) {
+        out.beginArray();
+        for (int i = 0, size = keys.size(); i < size; i++) {
+          out.beginArray(); // entry array
+          Streams.write(keys.get(i), out);
+          valueTypeAdapter.write(out, values.get(i));
+          out.endArray();
+        }
+        out.endArray();
+      } else {
+        out.beginObject();
+        for (int i = 0, size = keys.size(); i < size; i++) {
+          JsonElement keyElement = keys.get(i);
+          out.name(keyToString(keyElement));
+          valueTypeAdapter.write(out, values.get(i));
+        }
+        out.endObject();
+      }
+    }
+
+    private String keyToString(JsonElement keyElement) {
+      if (keyElement.isJsonPrimitive()) {
+        JsonPrimitive primitive = keyElement.getAsJsonPrimitive();
+        if (primitive.isNumber()) {
+          return String.valueOf(primitive.getAsNumber());
+        } else if (primitive.isBoolean()) {
+          return Boolean.toString(primitive.getAsBoolean());
+        } else if (primitive.isString()) {
+          return primitive.getAsString();
+        } else {
+          throw new AssertionError();
+        }
+      } else if (keyElement.isJsonNull()) {
+        return "null";
+      } else {
+        throw new AssertionError();
+      }
+    }
+  }
+}
diff --git a/gson/src/main/java/com/google/gson/internal/bind/ObjectTypeAdapter.java b/gson/src/main/java/com/google/gson/internal/bind/ObjectTypeAdapter.java
new file mode 100644
index 0000000000000000000000000000000000000000..ec42e0482678ff5939c5f7577fbad4727e48db52
--- /dev/null
+++ b/gson/src/main/java/com/google/gson/internal/bind/ObjectTypeAdapter.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2011 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.gson.internal.bind;
+
+import com.google.gson.Gson;
+import com.google.gson.TypeAdapter;
+import com.google.gson.TypeAdapterFactory;
+import com.google.gson.internal.LinkedTreeMap;
+import com.google.gson.reflect.TypeToken;
+import com.google.gson.stream.JsonReader;
+import com.google.gson.stream.JsonToken;
+import com.google.gson.stream.JsonWriter;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Adapts types whose static type is only 'Object'. Uses getClass() on
+ * serialization and a primitive/Map/List on deserialization.
+ */
+public final class ObjectTypeAdapter extends TypeAdapter<Object> {
+  public static final TypeAdapterFactory FACTORY = new TypeAdapterFactory() {
+    @SuppressWarnings("unchecked")
+    @Override public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
+      if (type.getRawType() == Object.class) {
+        return (TypeAdapter<T>) new ObjectTypeAdapter(gson);
+      }
+      return null;
+    }
+  };
+
+  private final Gson gson;
+
+  ObjectTypeAdapter(Gson gson) {
+    this.gson = gson;
+  }
+
+  @Override public Object read(JsonReader in) throws IOException {
+    JsonToken token = in.peek();
+    switch (token) {
+    case BEGIN_ARRAY:
+      List<Object> list = new ArrayList<Object>();
+      in.beginArray();
+      while (in.hasNext()) {
+        list.add(read(in));
+      }
+      in.endArray();
+      return list;
+
+    case BEGIN_OBJECT:
+      Map<String, Object> map = new LinkedTreeMap<String, Object>();
+      in.beginObject();
+      while (in.hasNext()) {
+        map.put(in.nextName(), read(in));
+      }
+      in.endObject();
+      return map;
+
+    case STRING:
+      return in.nextString();
+
+    case NUMBER:
+      return in.nextDouble();
+
+    case BOOLEAN:
+      return in.nextBoolean();
+
+    case NULL:
+      in.nextNull();
+      return null;
+
+    default:
+      throw new IllegalStateException();
+    }
+  }
+
+  @SuppressWarnings("unchecked")
+  @Override public void write(JsonWriter out, Object value) throws IOException {
+    if (value == null) {
+      out.nullValue();
+      return;
+    }
+
+    TypeAdapter<Object> typeAdapter = (TypeAdapter<Object>) gson.getAdapter(value.getClass());
+    if (typeAdapter instanceof ObjectTypeAdapter) {
+      out.beginObject();
+      out.endObject();
+      return;
+    }
+
+    typeAdapter.write(out, value);
+  }
+}
diff --git a/gson/src/main/java/com/google/gson/internal/bind/ReflectiveTypeAdapterFactory.java b/gson/src/main/java/com/google/gson/internal/bind/ReflectiveTypeAdapterFactory.java
new file mode 100644
index 0000000000000000000000000000000000000000..777e7dee358ae86852e869fa4f6a88546adfcbf8
--- /dev/null
+++ b/gson/src/main/java/com/google/gson/internal/bind/ReflectiveTypeAdapterFactory.java
@@ -0,0 +1,254 @@
+/*
+ * Copyright (C) 2011 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.gson.internal.bind;
+
+import com.google.gson.FieldNamingStrategy;
+import com.google.gson.Gson;
+import com.google.gson.JsonSyntaxException;
+import com.google.gson.TypeAdapter;
+import com.google.gson.TypeAdapterFactory;
+import com.google.gson.annotations.JsonAdapter;
+import com.google.gson.annotations.SerializedName;
+import com.google.gson.internal.$Gson$Types;
+import com.google.gson.internal.ConstructorConstructor;
+import com.google.gson.internal.Excluder;
+import com.google.gson.internal.ObjectConstructor;
+import com.google.gson.internal.Primitives;
+import com.google.gson.internal.reflect.ReflectionAccessor;
+import com.google.gson.reflect.TypeToken;
+import com.google.gson.stream.JsonReader;
+import com.google.gson.stream.JsonToken;
+import com.google.gson.stream.JsonWriter;
+import java.io.IOException;
+import java.lang.reflect.Field;
+import java.lang.reflect.Type;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Type adapter that reflects over the fields and methods of a class.
+ */
+public final class ReflectiveTypeAdapterFactory implements TypeAdapterFactory {
+  private final ConstructorConstructor constructorConstructor;
+  private final FieldNamingStrategy fieldNamingPolicy;
+  private final Excluder excluder;
+  private final JsonAdapterAnnotationTypeAdapterFactory jsonAdapterFactory;
+  private final ReflectionAccessor accessor = ReflectionAccessor.getInstance();
+
+  public ReflectiveTypeAdapterFactory(ConstructorConstructor constructorConstructor,
+      FieldNamingStrategy fieldNamingPolicy, Excluder excluder,
+      JsonAdapterAnnotationTypeAdapterFactory jsonAdapterFactory) {
+    this.constructorConstructor = constructorConstructor;
+    this.fieldNamingPolicy = fieldNamingPolicy;
+    this.excluder = excluder;
+    this.jsonAdapterFactory = jsonAdapterFactory;
+  }
+
+  public boolean excludeField(Field f, boolean serialize) {
+    return excludeField(f, serialize, excluder);
+  }
+
+  static boolean excludeField(Field f, boolean serialize, Excluder excluder) {
+    return !excluder.excludeClass(f.getType(), serialize) && !excluder.excludeField(f, serialize);
+  }
+
+  /** first element holds the default name */
+  private List<String> getFieldNames(Field f) {
+    SerializedName annotation = f.getAnnotation(SerializedName.class);
+    if (annotation == null) {
+      String name = fieldNamingPolicy.translateName(f);
+      return Collections.singletonList(name);
+    }
+
+    String serializedName = annotation.value();
+    String[] alternates = annotation.alternate();
+    if (alternates.length == 0) {
+      return Collections.singletonList(serializedName);
+    }
+
+    List<String> fieldNames = new ArrayList<String>(alternates.length + 1);
+    fieldNames.add(serializedName);
+    for (String alternate : alternates) {
+      fieldNames.add(alternate);
+    }
+    return fieldNames;
+  }
+
+  @Override public <T> TypeAdapter<T> create(Gson gson, final TypeToken<T> type) {
+    Class<? super T> raw = type.getRawType();
+
+    if (!Object.class.isAssignableFrom(raw)) {
+      return null; // it's a primitive!
+    }
+
+    ObjectConstructor<T> constructor = constructorConstructor.get(type);
+    return new Adapter<T>(constructor, getBoundFields(gson, type, raw));
+  }
+
+  private ReflectiveTypeAdapterFactory.BoundField createBoundField(
+      final Gson context, final Field field, final String name,
+      final TypeToken<?> fieldType, boolean serialize, boolean deserialize) {
+    final boolean isPrimitive = Primitives.isPrimitive(fieldType.getRawType());
+    // special casing primitives here saves ~5% on Android...
+    JsonAdapter annotation = field.getAnnotation(JsonAdapter.class);
+    TypeAdapter<?> mapped = null;
+    if (annotation != null) {
+      mapped = jsonAdapterFactory.getTypeAdapter(
+          constructorConstructor, context, fieldType, annotation);
+    }
+    final boolean jsonAdapterPresent = mapped != null;
+    if (mapped == null) mapped = context.getAdapter(fieldType);
+
+    final TypeAdapter<?> typeAdapter = mapped;
+    return new ReflectiveTypeAdapterFactory.BoundField(name, serialize, deserialize) {
+      @SuppressWarnings({"unchecked", "rawtypes"}) // the type adapter and field type always agree
+      @Override void write(JsonWriter writer, Object value)
+          throws IOException, IllegalAccessException {
+        Object fieldValue = field.get(value);
+        TypeAdapter t = jsonAdapterPresent ? typeAdapter
+            : new TypeAdapterRuntimeTypeWrapper(context, typeAdapter, fieldType.getType());
+        t.write(writer, fieldValue);
+      }
+      @Override void read(JsonReader reader, Object value)
+          throws IOException, IllegalAccessException {
+        Object fieldValue = typeAdapter.read(reader);
+        if (fieldValue != null || !isPrimitive) {
+          field.set(value, fieldValue);
+        }
+      }
+      @Override public boolean writeField(Object value) throws IOException, IllegalAccessException {
+        if (!serialized) return false;
+        Object fieldValue = field.get(value);
+        return fieldValue != value; // avoid recursion for example for Throwable.cause
+      }
+    };
+  }
+
+  private Map<String, BoundField> getBoundFields(Gson context, TypeToken<?> type, Class<?> raw) {
+    Map<String, BoundField> result = new LinkedHashMap<String, BoundField>();
+    if (raw.isInterface()) {
+      return result;
+    }
+
+    Type declaredType = type.getType();
+    while (raw != Object.class) {
+      Field[] fields = raw.getDeclaredFields();
+      for (Field field : fields) {
+        boolean serialize = excludeField(field, true);
+        boolean deserialize = excludeField(field, false);
+        if (!serialize && !deserialize) {
+          continue;
+        }
+        accessor.makeAccessible(field);
+        Type fieldType = $Gson$Types.resolve(type.getType(), raw, field.getGenericType());
+        List<String> fieldNames = getFieldNames(field);
+        BoundField previous = null;
+        for (int i = 0, size = fieldNames.size(); i < size; ++i) {
+          String name = fieldNames.get(i);
+          if (i != 0) serialize = false; // only serialize the default name
+          BoundField boundField = createBoundField(context, field, name,
+              TypeToken.get(fieldType), serialize, deserialize);
+          BoundField replaced = result.put(name, boundField);
+          if (previous == null) previous = replaced;
+        }
+        if (previous != null) {
+          throw new IllegalArgumentException(declaredType
+              + " declares multiple JSON fields named " + previous.name);
+        }
+      }
+      type = TypeToken.get($Gson$Types.resolve(type.getType(), raw, raw.getGenericSuperclass()));
+      raw = type.getRawType();
+    }
+    return result;
+  }
+
+  static abstract class BoundField {
+    final String name;
+    final boolean serialized;
+    final boolean deserialized;
+
+    protected BoundField(String name, boolean serialized, boolean deserialized) {
+      this.name = name;
+      this.serialized = serialized;
+      this.deserialized = deserialized;
+    }
+    abstract boolean writeField(Object value) throws IOException, IllegalAccessException;
+    abstract void write(JsonWriter writer, Object value) throws IOException, IllegalAccessException;
+    abstract void read(JsonReader reader, Object value) throws IOException, IllegalAccessException;
+  }
+
+  public static final class Adapter<T> extends TypeAdapter<T> {
+    private final ObjectConstructor<T> constructor;
+    private final Map<String, BoundField> boundFields;
+
+    Adapter(ObjectConstructor<T> constructor, Map<String, BoundField> boundFields) {
+      this.constructor = constructor;
+      this.boundFields = boundFields;
+    }
+
+    @Override public T read(JsonReader in) throws IOException {
+      if (in.peek() == JsonToken.NULL) {
+        in.nextNull();
+        return null;
+      }
+
+      T instance = constructor.construct();
+
+      try {
+        in.beginObject();
+        while (in.hasNext()) {
+          String name = in.nextName();
+          BoundField field = boundFields.get(name);
+          if (field == null || !field.deserialized) {
+            in.skipValue();
+          } else {
+            field.read(in, instance);
+          }
+        }
+      } catch (IllegalStateException e) {
+        throw new JsonSyntaxException(e);
+      } catch (IllegalAccessException e) {
+        throw new AssertionError(e);
+      }
+      in.endObject();
+      return instance;
+    }
+
+    @Override public void write(JsonWriter out, T value) throws IOException {
+      if (value == null) {
+        out.nullValue();
+        return;
+      }
+
+      out.beginObject();
+      try {
+        for (BoundField boundField : boundFields.values()) {
+          if (boundField.writeField(value)) {
+            out.name(boundField.name);
+            boundField.write(out, value);
+          }
+        }
+      } catch (IllegalAccessException e) {
+        throw new AssertionError(e);
+      }
+      out.endObject();
+    }
+  }
+}
diff --git a/gson/src/main/java/com/google/gson/internal/bind/SqlDateTypeAdapter.java b/gson/src/main/java/com/google/gson/internal/bind/SqlDateTypeAdapter.java
new file mode 100644
index 0000000000000000000000000000000000000000..5ec244f29e4cfdcb23a5e403f358a3192fc9b723
--- /dev/null
+++ b/gson/src/main/java/com/google/gson/internal/bind/SqlDateTypeAdapter.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2011 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.gson.internal.bind;
+
+import com.google.gson.Gson;
+import com.google.gson.JsonSyntaxException;
+import com.google.gson.TypeAdapter;
+import com.google.gson.TypeAdapterFactory;
+import com.google.gson.reflect.TypeToken;
+import com.google.gson.stream.JsonReader;
+import com.google.gson.stream.JsonToken;
+import com.google.gson.stream.JsonWriter;
+import java.io.IOException;
+import java.text.DateFormat;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+
+/**
+ * Adapter for java.sql.Date. Although this class appears stateless, it is not.
+ * DateFormat captures its time zone and locale when it is created, which gives
+ * this class state. DateFormat isn't thread safe either, so this class has
+ * to synchronize its read and write methods.
+ */
+public final class SqlDateTypeAdapter extends TypeAdapter<java.sql.Date> {
+  public static final TypeAdapterFactory FACTORY = new TypeAdapterFactory() {
+    @SuppressWarnings("unchecked") // we use a runtime check to make sure the 'T's equal
+    @Override public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) {
+      return typeToken.getRawType() == java.sql.Date.class
+          ? (TypeAdapter<T>) new SqlDateTypeAdapter() : null;
+    }
+  };
+
+  private final DateFormat format = new SimpleDateFormat("MMM d, yyyy");
+
+  @Override
+  public synchronized java.sql.Date read(JsonReader in) throws IOException {
+    if (in.peek() == JsonToken.NULL) {
+      in.nextNull();
+      return null;
+    }
+    try {
+      final long utilDate = format.parse(in.nextString()).getTime();
+      return new java.sql.Date(utilDate);
+    } catch (ParseException e) {
+      throw new JsonSyntaxException(e);
+    }
+  }
+
+  @Override
+  public synchronized void write(JsonWriter out, java.sql.Date value) throws IOException {
+    out.value(value == null ? null : format.format(value));
+  }
+}
diff --git a/gson/src/main/java/com/google/gson/internal/bind/TimeTypeAdapter.java b/gson/src/main/java/com/google/gson/internal/bind/TimeTypeAdapter.java
new file mode 100644
index 0000000000000000000000000000000000000000..55d4b2f693f2dbe24bcddc77c6c690eef403d02b
--- /dev/null
+++ b/gson/src/main/java/com/google/gson/internal/bind/TimeTypeAdapter.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2011 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.gson.internal.bind;
+
+import com.google.gson.Gson;
+import com.google.gson.JsonSyntaxException;
+import com.google.gson.TypeAdapter;
+import com.google.gson.TypeAdapterFactory;
+import com.google.gson.reflect.TypeToken;
+import com.google.gson.stream.JsonReader;
+import com.google.gson.stream.JsonToken;
+import com.google.gson.stream.JsonWriter;
+import java.io.IOException;
+import java.sql.Time;
+import java.text.DateFormat;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+/**
+ * Adapter for Time. Although this class appears stateless, it is not.
+ * DateFormat captures its time zone and locale when it is created, which gives
+ * this class state. DateFormat isn't thread safe either, so this class has
+ * to synchronize its read and write methods.
+ */
+public final class TimeTypeAdapter extends TypeAdapter<Time> {
+  public static final TypeAdapterFactory FACTORY = new TypeAdapterFactory() {
+    @SuppressWarnings("unchecked") // we use a runtime check to make sure the 'T's equal
+    @Override public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) {
+      return typeToken.getRawType() == Time.class ? (TypeAdapter<T>) new TimeTypeAdapter() : null;
+    }
+  };
+
+  private final DateFormat format = new SimpleDateFormat("hh:mm:ss a");
+
+  @Override public synchronized Time read(JsonReader in) throws IOException {
+    if (in.peek() == JsonToken.NULL) {
+      in.nextNull();
+      return null;
+    }
+    try {
+      Date date = format.parse(in.nextString());
+      return new Time(date.getTime());
+    } catch (ParseException e) {
+      throw new JsonSyntaxException(e);
+    }
+  }
+
+  @Override public synchronized void write(JsonWriter out, Time value) throws IOException {
+    out.value(value == null ? null : format.format(value));
+  }
+}
diff --git a/gson/src/main/java/com/google/gson/internal/bind/TreeTypeAdapter.java b/gson/src/main/java/com/google/gson/internal/bind/TreeTypeAdapter.java
new file mode 100644
index 0000000000000000000000000000000000000000..a5c6c5dcda3da52996234567b419227eaf988a3b
--- /dev/null
+++ b/gson/src/main/java/com/google/gson/internal/bind/TreeTypeAdapter.java
@@ -0,0 +1,165 @@
+/*
+ * Copyright (C) 2011 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.gson.internal.bind;
+
+import com.google.gson.Gson;
+import com.google.gson.JsonDeserializationContext;
+import com.google.gson.JsonDeserializer;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonParseException;
+import com.google.gson.JsonSerializationContext;
+import com.google.gson.JsonSerializer;
+import com.google.gson.TypeAdapter;
+import com.google.gson.TypeAdapterFactory;
+import com.google.gson.internal.$Gson$Preconditions;
+import com.google.gson.internal.Streams;
+import com.google.gson.reflect.TypeToken;
+import com.google.gson.stream.JsonReader;
+import com.google.gson.stream.JsonWriter;
+import java.io.IOException;
+import java.lang.reflect.Type;
+
+/**
+ * Adapts a Gson 1.x tree-style adapter as a streaming TypeAdapter. Since the
+ * tree adapter may be serialization-only or deserialization-only, this class
+ * has a facility to lookup a delegate type adapter on demand.
+ */
+public final class TreeTypeAdapter<T> extends TypeAdapter<T> {
+  private final JsonSerializer<T> serializer;
+  private final JsonDeserializer<T> deserializer;
+  final Gson gson;
+  private final TypeToken<T> typeToken;
+  private final TypeAdapterFactory skipPast;
+  private final GsonContextImpl context = new GsonContextImpl();
+
+  /** The delegate is lazily created because it may not be needed, and creating it may fail. */
+  private TypeAdapter<T> delegate;
+
+  public TreeTypeAdapter(JsonSerializer<T> serializer, JsonDeserializer<T> deserializer,
+      Gson gson, TypeToken<T> typeToken, TypeAdapterFactory skipPast) {
+    this.serializer = serializer;
+    this.deserializer = deserializer;
+    this.gson = gson;
+    this.typeToken = typeToken;
+    this.skipPast = skipPast;
+  }
+
+  @Override public T read(JsonReader in) throws IOException {
+    if (deserializer == null) {
+      return delegate().read(in);
+    }
+    JsonElement value = Streams.parse(in);
+    if (value.isJsonNull()) {
+      return null;
+    }
+    return deserializer.deserialize(value, typeToken.getType(), context);
+  }
+
+  @Override public void write(JsonWriter out, T value) throws IOException {
+    if (serializer == null) {
+      delegate().write(out, value);
+      return;
+    }
+    if (value == null) {
+      out.nullValue();
+      return;
+    }
+    JsonElement tree = serializer.serialize(value, typeToken.getType(), context);
+    Streams.write(tree, out);
+  }
+
+  private TypeAdapter<T> delegate() {
+    TypeAdapter<T> d = delegate;
+    return d != null
+        ? d
+        : (delegate = gson.getDelegateAdapter(skipPast, typeToken));
+  }
+
+  /**
+   * Returns a new factory that will match each type against {@code exactType}.
+   */
+  public static TypeAdapterFactory newFactory(TypeToken<?> exactType, Object typeAdapter) {
+    return new SingleTypeFactory(typeAdapter, exactType, false, null);
+  }
+
+  /**
+   * Returns a new factory that will match each type and its raw type against
+   * {@code exactType}.
+   */
+  public static TypeAdapterFactory newFactoryWithMatchRawType(
+      TypeToken<?> exactType, Object typeAdapter) {
+    // only bother matching raw types if exact type is a raw type
+    boolean matchRawType = exactType.getType() == exactType.getRawType();
+    return new SingleTypeFactory(typeAdapter, exactType, matchRawType, null);
+  }
+
+  /**
+   * Returns a new factory that will match each type's raw type for assignability
+   * to {@code hierarchyType}.
+   */
+  public static TypeAdapterFactory newTypeHierarchyFactory(
+      Class<?> hierarchyType, Object typeAdapter) {
+    return new SingleTypeFactory(typeAdapter, null, false, hierarchyType);
+  }
+
+  private static final class SingleTypeFactory implements TypeAdapterFactory {
+    private final TypeToken<?> exactType;
+    private final boolean matchRawType;
+    private final Class<?> hierarchyType;
+    private final JsonSerializer<?> serializer;
+    private final JsonDeserializer<?> deserializer;
+
+    SingleTypeFactory(Object typeAdapter, TypeToken<?> exactType, boolean matchRawType,
+        Class<?> hierarchyType) {
+      serializer = typeAdapter instanceof JsonSerializer
+          ? (JsonSerializer<?>) typeAdapter
+          : null;
+      deserializer = typeAdapter instanceof JsonDeserializer
+          ? (JsonDeserializer<?>) typeAdapter
+          : null;
+      $Gson$Preconditions.checkArgument(serializer != null || deserializer != null);
+      this.exactType = exactType;
+      this.matchRawType = matchRawType;
+      this.hierarchyType = hierarchyType;
+    }
+
+    @SuppressWarnings("unchecked") // guarded by typeToken.equals() call
+    @Override
+    public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
+      boolean matches = exactType != null
+          ? exactType.equals(type) || matchRawType && exactType.getType() == type.getRawType()
+          : hierarchyType.isAssignableFrom(type.getRawType());
+      return matches
+          ? new TreeTypeAdapter<T>((JsonSerializer<T>) serializer,
+              (JsonDeserializer<T>) deserializer, gson, type, this)
+          : null;
+    }
+  }
+
+  private final class GsonContextImpl implements JsonSerializationContext, JsonDeserializationContext {
+    @Override public JsonElement serialize(Object src) {
+      return gson.toJsonTree(src);
+    }
+    @Override public JsonElement serialize(Object src, Type typeOfSrc) {
+      return gson.toJsonTree(src, typeOfSrc);
+    }
+    @SuppressWarnings("unchecked")
+    @Override public <R> R deserialize(JsonElement json, Type typeOfT) throws JsonParseException {
+      return (R) gson.fromJson(json, typeOfT);
+    }
+  };
+}
diff --git a/gson/src/main/java/com/google/gson/internal/bind/TypeAdapterRuntimeTypeWrapper.java b/gson/src/main/java/com/google/gson/internal/bind/TypeAdapterRuntimeTypeWrapper.java
new file mode 100644
index 0000000000000000000000000000000000000000..5bca700787f5f8c98b490a3816118c92e935f0c9
--- /dev/null
+++ b/gson/src/main/java/com/google/gson/internal/bind/TypeAdapterRuntimeTypeWrapper.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2011 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.google.gson.internal.bind;
+
+import java.io.IOException;
+import java.lang.reflect.Type;
+import java.lang.reflect.TypeVariable;
+
+import com.google.gson.Gson;
+import com.google.gson.TypeAdapter;
+import com.google.gson.reflect.TypeToken;
+import com.google.gson.stream.JsonReader;
+import com.google.gson.stream.JsonWriter;
+
+final class TypeAdapterRuntimeTypeWrapper<T> extends TypeAdapter<T> {
+  private final Gson context;
+  private final TypeAdapter<T> delegate;
+  private final Type type;
+
+  TypeAdapterRuntimeTypeWrapper(Gson context, TypeAdapter<T> delegate, Type type) {
+    this.context = context;
+    this.delegate = delegate;
+    this.type = type;
+  }
+
+  @Override
+  public T read(JsonReader in) throws IOException {
+    return delegate.read(in);
+  }
+
+  @SuppressWarnings({"rawtypes", "unchecked"})
+  @Override
+  public void write(JsonWriter out, T value) throws IOException {
+    // Order of preference for choosing type adapters
+    // First preference: a type adapter registered for the runtime type
+    // Second preference: a type adapter registered for the declared type
+    // Third preference: reflective type adapter for the runtime type (if it is a sub class of the declared type)
+    // Fourth preference: reflective type adapter for the declared type
+
+    TypeAdapter chosen = delegate;
+    Type runtimeType = getRuntimeTypeIfMoreSpecific(type, value);
+    if (runtimeType != type) {
+      TypeAdapter runtimeTypeAdapter = context.getAdapter(TypeToken.get(runtimeType));
+      if (!(runtimeTypeAdapter instanceof ReflectiveTypeAdapterFactory.Adapter)) {
+        // The user registered a type adapter for the runtime type, so we will use that
+        chosen = runtimeTypeAdapter;
+      } else if (!(delegate instanceof ReflectiveTypeAdapterFactory.Adapter)) {
+        // The user registered a type adapter for Base class, so we prefer it over the
+        // reflective type adapter for the runtime type
+        chosen = delegate;
+      } else {
+        // Use the type adapter for runtime type
+        chosen = runtimeTypeAdapter;
+      }
+    }
+    chosen.write(out, value);
+  }
+
+  /**
+   * Finds a compatible runtime type if it is more specific
+   */
+  private Type getRuntimeTypeIfMoreSpecific(Type type, Object value) {
+    if (value != null
+        && (type == Object.class || type instanceof TypeVariable<?> || type instanceof Class<?>)) {
+      type = value.getClass();
+    }
+    return type;
+  }
+}
diff --git a/gson/src/main/java/com/google/gson/internal/bind/TypeAdapters.java b/gson/src/main/java/com/google/gson/internal/bind/TypeAdapters.java
new file mode 100644
index 0000000000000000000000000000000000000000..354ce5a1fb24db68c4f589a4f802d31946d72a84
--- /dev/null
+++ b/gson/src/main/java/com/google/gson/internal/bind/TypeAdapters.java
@@ -0,0 +1,908 @@
+/*
+ * Copyright (C) 2011 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.gson.internal.bind;
+
+import java.io.IOException;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.net.InetAddress;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.sql.Timestamp;
+import java.util.ArrayList;
+import java.util.BitSet;
+import java.util.Calendar;
+import java.util.Currency;
+import java.util.Date;
+import java.util.GregorianCalendar;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.StringTokenizer;
+import java.util.UUID;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicIntegerArray;
+
+import com.google.gson.Gson;
+import com.google.gson.JsonArray;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonIOException;
+import com.google.gson.JsonNull;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonPrimitive;
+import com.google.gson.JsonSyntaxException;
+import com.google.gson.TypeAdapter;
+import com.google.gson.TypeAdapterFactory;
+import com.google.gson.annotations.SerializedName;
+import com.google.gson.internal.LazilyParsedNumber;
+import com.google.gson.reflect.TypeToken;
+import com.google.gson.stream.JsonReader;
+import com.google.gson.stream.JsonToken;
+import com.google.gson.stream.JsonWriter;
+
+/**
+ * Type adapters for basic types.
+ */
+public final class TypeAdapters {
+  private TypeAdapters() {
+    throw new UnsupportedOperationException();
+  }
+
+  @SuppressWarnings("rawtypes")
+  public static final TypeAdapter<Class> CLASS = new TypeAdapter<Class>() {
+    @Override
+    public void write(JsonWriter out, Class value) throws IOException {
+      throw new UnsupportedOperationException("Attempted to serialize java.lang.Class: "
+              + value.getName() + ". Forgot to register a type adapter?");
+    }
+    @Override
+    public Class read(JsonReader in) throws IOException {
+      throw new UnsupportedOperationException(
+              "Attempted to deserialize a java.lang.Class. Forgot to register a type adapter?");
+    }
+  }.nullSafe();
+
+  public static final TypeAdapterFactory CLASS_FACTORY = newFactory(Class.class, CLASS);
+
+  public static final TypeAdapter<BitSet> BIT_SET = new TypeAdapter<BitSet>() {
+    @Override public BitSet read(JsonReader in) throws IOException {
+      BitSet bitset = new BitSet();
+      in.beginArray();
+      int i = 0;
+      JsonToken tokenType = in.peek();
+      while (tokenType != JsonToken.END_ARRAY) {
+        boolean set;
+        switch (tokenType) {
+        case NUMBER:
+          set = in.nextInt() != 0;
+          break;
+        case BOOLEAN:
+          set = in.nextBoolean();
+          break;
+        case STRING:
+          String stringValue = in.nextString();
+          try {
+            set = Integer.parseInt(stringValue) != 0;
+          } catch (NumberFormatException e) {
+            throw new JsonSyntaxException(
+                "Error: Expecting: bitset number value (1, 0), Found: " + stringValue);
+          }
+          break;
+        default:
+          throw new JsonSyntaxException("Invalid bitset value type: " + tokenType);
+        }
+        if (set) {
+          bitset.set(i);
+        }
+        ++i;
+        tokenType = in.peek();
+      }
+      in.endArray();
+      return bitset;
+    }
+
+    @Override public void write(JsonWriter out, BitSet src) throws IOException {
+      out.beginArray();
+      for (int i = 0, length = src.length(); i < length; i++) {
+        int value = (src.get(i)) ? 1 : 0;
+        out.value(value);
+      }
+      out.endArray();
+    }
+  }.nullSafe();
+
+  public static final TypeAdapterFactory BIT_SET_FACTORY = newFactory(BitSet.class, BIT_SET);
+
+  public static final TypeAdapter<Boolean> BOOLEAN = new TypeAdapter<Boolean>() {
+    @Override
+    public Boolean read(JsonReader in) throws IOException {
+      JsonToken peek = in.peek();
+      if (peek == JsonToken.NULL) {
+        in.nextNull();
+        return null;
+      } else if (peek == JsonToken.STRING) {
+        // support strings for compatibility with GSON 1.7
+        return Boolean.parseBoolean(in.nextString());
+      }
+      return in.nextBoolean();
+    }
+    @Override
+    public void write(JsonWriter out, Boolean value) throws IOException {
+      out.value(value);
+    }
+  };
+
+  /**
+   * Writes a boolean as a string. Useful for map keys, where booleans aren't
+   * otherwise permitted.
+   */
+  public static final TypeAdapter<Boolean> BOOLEAN_AS_STRING = new TypeAdapter<Boolean>() {
+    @Override public Boolean read(JsonReader in) throws IOException {
+      if (in.peek() == JsonToken.NULL) {
+        in.nextNull();
+        return null;
+      }
+      return Boolean.valueOf(in.nextString());
+    }
+
+    @Override public void write(JsonWriter out, Boolean value) throws IOException {
+      out.value(value == null ? "null" : value.toString());
+    }
+  };
+
+  public static final TypeAdapterFactory BOOLEAN_FACTORY
+      = newFactory(boolean.class, Boolean.class, BOOLEAN);
+
+  public static final TypeAdapter<Number> BYTE = new TypeAdapter<Number>() {
+    @Override
+    public Number read(JsonReader in) throws IOException {
+      if (in.peek() == JsonToken.NULL) {
+        in.nextNull();
+        return null;
+      }
+      try {
+        int intValue = in.nextInt();
+        return (byte) intValue;
+      } catch (NumberFormatException e) {
+        throw new JsonSyntaxException(e);
+      }
+    }
+    @Override
+    public void write(JsonWriter out, Number value) throws IOException {
+      out.value(value);
+    }
+  };
+
+  public static final TypeAdapterFactory BYTE_FACTORY
+      = newFactory(byte.class, Byte.class, BYTE);
+
+  public static final TypeAdapter<Number> SHORT = new TypeAdapter<Number>() {
+    @Override
+    public Number read(JsonReader in) throws IOException {
+      if (in.peek() == JsonToken.NULL) {
+        in.nextNull();
+        return null;
+      }
+      try {
+        return (short) in.nextInt();
+      } catch (NumberFormatException e) {
+        throw new JsonSyntaxException(e);
+      }
+    }
+    @Override
+    public void write(JsonWriter out, Number value) throws IOException {
+      out.value(value);
+    }
+  };
+
+  public static final TypeAdapterFactory SHORT_FACTORY
+      = newFactory(short.class, Short.class, SHORT);
+
+  public static final TypeAdapter<Number> INTEGER = new TypeAdapter<Number>() {
+    @Override
+    public Number read(JsonReader in) throws IOException {
+      if (in.peek() == JsonToken.NULL) {
+        in.nextNull();
+        return null;
+      }
+      try {
+        return in.nextInt();
+      } catch (NumberFormatException e) {
+        throw new JsonSyntaxException(e);
+      }
+    }
+    @Override
+    public void write(JsonWriter out, Number value) throws IOException {
+      out.value(value);
+    }
+  };
+  public static final TypeAdapterFactory INTEGER_FACTORY
+      = newFactory(int.class, Integer.class, INTEGER);
+
+  public static final TypeAdapter<AtomicInteger> ATOMIC_INTEGER = new TypeAdapter<AtomicInteger>() {
+    @Override public AtomicInteger read(JsonReader in) throws IOException {
+      try {
+        return new AtomicInteger(in.nextInt());
+      } catch (NumberFormatException e) {
+        throw new JsonSyntaxException(e);
+      }
+    }
+    @Override public void write(JsonWriter out, AtomicInteger value) throws IOException {
+      out.value(value.get());
+    }
+  }.nullSafe();
+  public static final TypeAdapterFactory ATOMIC_INTEGER_FACTORY =
+      newFactory(AtomicInteger.class, TypeAdapters.ATOMIC_INTEGER);
+
+  public static final TypeAdapter<AtomicBoolean> ATOMIC_BOOLEAN = new TypeAdapter<AtomicBoolean>() {
+    @Override public AtomicBoolean read(JsonReader in) throws IOException {
+      return new AtomicBoolean(in.nextBoolean());
+    }
+    @Override public void write(JsonWriter out, AtomicBoolean value) throws IOException {
+      out.value(value.get());
+    }
+  }.nullSafe();
+  public static final TypeAdapterFactory ATOMIC_BOOLEAN_FACTORY =
+      newFactory(AtomicBoolean.class, TypeAdapters.ATOMIC_BOOLEAN);
+
+  public static final TypeAdapter<AtomicIntegerArray> ATOMIC_INTEGER_ARRAY = new TypeAdapter<AtomicIntegerArray>() {
+    @Override public AtomicIntegerArray read(JsonReader in) throws IOException {
+        List<Integer> list = new ArrayList<Integer>();
+        in.beginArray();
+        while (in.hasNext()) {
+          try {
+            int integer = in.nextInt();
+            list.add(integer);
+          } catch (NumberFormatException e) {
+            throw new JsonSyntaxException(e);
+          }
+        }
+        in.endArray();
+        int length = list.size();
+        AtomicIntegerArray array = new AtomicIntegerArray(length);
+        for (int i = 0; i < length; ++i) {
+          array.set(i, list.get(i));
+        }
+        return array;
+    }
+    @Override public void write(JsonWriter out, AtomicIntegerArray value) throws IOException {
+      out.beginArray();
+      for (int i = 0, length = value.length(); i < length; i++) {
+        out.value(value.get(i));
+      }
+      out.endArray();
+    }
+  }.nullSafe();
+  public static final TypeAdapterFactory ATOMIC_INTEGER_ARRAY_FACTORY =
+      newFactory(AtomicIntegerArray.class, TypeAdapters.ATOMIC_INTEGER_ARRAY);
+
+  public static final TypeAdapter<Number> LONG = new TypeAdapter<Number>() {
+    @Override
+    public Number read(JsonReader in) throws IOException {
+      if (in.peek() == JsonToken.NULL) {
+        in.nextNull();
+        return null;
+      }
+      try {
+        return in.nextLong();
+      } catch (NumberFormatException e) {
+        throw new JsonSyntaxException(e);
+      }
+    }
+    @Override
+    public void write(JsonWriter out, Number value) throws IOException {
+      out.value(value);
+    }
+  };
+
+  public static final TypeAdapter<Number> FLOAT = new TypeAdapter<Number>() {
+    @Override
+    public Number read(JsonReader in) throws IOException {
+      if (in.peek() == JsonToken.NULL) {
+        in.nextNull();
+        return null;
+      }
+      return (float) in.nextDouble();
+    }
+    @Override
+    public void write(JsonWriter out, Number value) throws IOException {
+      out.value(value);
+    }
+  };
+
+  public static final TypeAdapter<Number> DOUBLE = new TypeAdapter<Number>() {
+    @Override
+    public Number read(JsonReader in) throws IOException {
+      if (in.peek() == JsonToken.NULL) {
+        in.nextNull();
+        return null;
+      }
+      return in.nextDouble();
+    }
+    @Override
+    public void write(JsonWriter out, Number value) throws IOException {
+      out.value(value);
+    }
+  };
+
+  public static final TypeAdapter<Number> NUMBER = new TypeAdapter<Number>() {
+    @Override
+    public Number read(JsonReader in) throws IOException {
+      JsonToken jsonToken = in.peek();
+      switch (jsonToken) {
+      case NULL:
+        in.nextNull();
+        return null;
+      case NUMBER:
+      case STRING:
+        return new LazilyParsedNumber(in.nextString());
+      default:
+        throw new JsonSyntaxException("Expecting number, got: " + jsonToken);
+      }
+    }
+    @Override
+    public void write(JsonWriter out, Number value) throws IOException {
+      out.value(value);
+    }
+  };
+
+  public static final TypeAdapterFactory NUMBER_FACTORY = newFactory(Number.class, NUMBER);
+
+  public static final TypeAdapter<Character> CHARACTER = new TypeAdapter<Character>() {
+    @Override
+    public Character read(JsonReader in) throws IOException {
+      if (in.peek() == JsonToken.NULL) {
+        in.nextNull();
+        return null;
+      }
+      String str = in.nextString();
+      if (str.length() != 1) {
+        throw new JsonSyntaxException("Expecting character, got: " + str);
+      }
+      return str.charAt(0);
+    }
+    @Override
+    public void write(JsonWriter out, Character value) throws IOException {
+      out.value(value == null ? null : String.valueOf(value));
+    }
+  };
+
+  public static final TypeAdapterFactory CHARACTER_FACTORY
+      = newFactory(char.class, Character.class, CHARACTER);
+
+  public static final TypeAdapter<String> STRING = new TypeAdapter<String>() {
+    @Override
+    public String read(JsonReader in) throws IOException {
+      JsonToken peek = in.peek();
+      if (peek == JsonToken.NULL) {
+        in.nextNull();
+        return null;
+      }
+      /* coerce booleans to strings for backwards compatibility */
+      if (peek == JsonToken.BOOLEAN) {
+        return Boolean.toString(in.nextBoolean());
+      }
+      return in.nextString();
+    }
+    @Override
+    public void write(JsonWriter out, String value) throws IOException {
+      out.value(value);
+    }
+  };
+  
+  public static final TypeAdapter<BigDecimal> BIG_DECIMAL = new TypeAdapter<BigDecimal>() {
+    @Override public BigDecimal read(JsonReader in) throws IOException {
+      if (in.peek() == JsonToken.NULL) {
+        in.nextNull();
+        return null;
+      }
+      try {
+        return new BigDecimal(in.nextString());
+      } catch (NumberFormatException e) {
+        throw new JsonSyntaxException(e);
+      }
+    }
+
+    @Override public void write(JsonWriter out, BigDecimal value) throws IOException {
+      out.value(value);
+    }
+  };
+  
+  public static final TypeAdapter<BigInteger> BIG_INTEGER = new TypeAdapter<BigInteger>() {
+    @Override public BigInteger read(JsonReader in) throws IOException {
+      if (in.peek() == JsonToken.NULL) {
+        in.nextNull();
+        return null;
+      }
+      try {
+        return new BigInteger(in.nextString());
+      } catch (NumberFormatException e) {
+        throw new JsonSyntaxException(e);
+      }
+    }
+
+    @Override public void write(JsonWriter out, BigInteger value) throws IOException {
+      out.value(value);
+    }
+  };
+
+  public static final TypeAdapterFactory STRING_FACTORY = newFactory(String.class, STRING);
+
+  public static final TypeAdapter<StringBuilder> STRING_BUILDER = new TypeAdapter<StringBuilder>() {
+    @Override
+    public StringBuilder read(JsonReader in) throws IOException {
+      if (in.peek() == JsonToken.NULL) {
+        in.nextNull();
+        return null;
+      }
+      return new StringBuilder(in.nextString());
+    }
+    @Override
+    public void write(JsonWriter out, StringBuilder value) throws IOException {
+      out.value(value == null ? null : value.toString());
+    }
+  };
+
+  public static final TypeAdapterFactory STRING_BUILDER_FACTORY =
+    newFactory(StringBuilder.class, STRING_BUILDER);
+
+  public static final TypeAdapter<StringBuffer> STRING_BUFFER = new TypeAdapter<StringBuffer>() {
+    @Override
+    public StringBuffer read(JsonReader in) throws IOException {
+      if (in.peek() == JsonToken.NULL) {
+        in.nextNull();
+        return null;
+      }
+      return new StringBuffer(in.nextString());
+    }
+    @Override
+    public void write(JsonWriter out, StringBuffer value) throws IOException {
+      out.value(value == null ? null : value.toString());
+    }
+  };
+
+  public static final TypeAdapterFactory STRING_BUFFER_FACTORY =
+    newFactory(StringBuffer.class, STRING_BUFFER);
+
+  public static final TypeAdapter<URL> URL = new TypeAdapter<URL>() {
+    @Override
+    public URL read(JsonReader in) throws IOException {
+      if (in.peek() == JsonToken.NULL) {
+        in.nextNull();
+        return null;
+      }
+      String nextString = in.nextString();
+      return "null".equals(nextString) ? null : new URL(nextString);
+    }
+    @Override
+    public void write(JsonWriter out, URL value) throws IOException {
+      out.value(value == null ? null : value.toExternalForm());
+    }
+  };
+
+  public static final TypeAdapterFactory URL_FACTORY = newFactory(URL.class, URL);
+
+  public static final TypeAdapter<URI> URI = new TypeAdapter<URI>() {
+    @Override
+    public URI read(JsonReader in) throws IOException {
+      if (in.peek() == JsonToken.NULL) {
+        in.nextNull();
+        return null;
+      }
+      try {
+        String nextString = in.nextString();
+        return "null".equals(nextString) ? null : new URI(nextString);
+      } catch (URISyntaxException e) {
+        throw new JsonIOException(e);
+      }
+    }
+    @Override
+    public void write(JsonWriter out, URI value) throws IOException {
+      out.value(value == null ? null : value.toASCIIString());
+    }
+  };
+
+  public static final TypeAdapterFactory URI_FACTORY = newFactory(URI.class, URI);
+
+  public static final TypeAdapter<InetAddress> INET_ADDRESS = new TypeAdapter<InetAddress>() {
+    @Override
+    public InetAddress read(JsonReader in) throws IOException {
+      if (in.peek() == JsonToken.NULL) {
+        in.nextNull();
+        return null;
+      }
+      // regrettably, this should have included both the host name and the host address
+      return InetAddress.getByName(in.nextString());
+    }
+    @Override
+    public void write(JsonWriter out, InetAddress value) throws IOException {
+      out.value(value == null ? null : value.getHostAddress());
+    }
+  };
+
+  public static final TypeAdapterFactory INET_ADDRESS_FACTORY =
+    newTypeHierarchyFactory(InetAddress.class, INET_ADDRESS);
+
+  public static final TypeAdapter<UUID> UUID = new TypeAdapter<UUID>() {
+    @Override
+    public UUID read(JsonReader in) throws IOException {
+      if (in.peek() == JsonToken.NULL) {
+        in.nextNull();
+        return null;
+      }
+      return java.util.UUID.fromString(in.nextString());
+    }
+    @Override
+    public void write(JsonWriter out, UUID value) throws IOException {
+      out.value(value == null ? null : value.toString());
+    }
+  };
+
+  public static final TypeAdapterFactory UUID_FACTORY = newFactory(UUID.class, UUID);
+
+  public static final TypeAdapter<Currency> CURRENCY = new TypeAdapter<Currency>() {
+    @Override
+    public Currency read(JsonReader in) throws IOException {
+      return Currency.getInstance(in.nextString());
+    }
+    @Override
+    public void write(JsonWriter out, Currency value) throws IOException {
+      out.value(value.getCurrencyCode());
+    }
+  }.nullSafe();
+  public static final TypeAdapterFactory CURRENCY_FACTORY = newFactory(Currency.class, CURRENCY);
+
+  public static final TypeAdapterFactory TIMESTAMP_FACTORY = new TypeAdapterFactory() {
+    @SuppressWarnings("unchecked") // we use a runtime check to make sure the 'T's equal
+    @Override public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) {
+      if (typeToken.getRawType() != Timestamp.class) {
+        return null;
+      }
+
+      final TypeAdapter<Date> dateTypeAdapter = gson.getAdapter(Date.class);
+      return (TypeAdapter<T>) new TypeAdapter<Timestamp>() {
+        @Override public Timestamp read(JsonReader in) throws IOException {
+          Date date = dateTypeAdapter.read(in);
+          return date != null ? new Timestamp(date.getTime()) : null;
+        }
+
+        @Override public void write(JsonWriter out, Timestamp value) throws IOException {
+          dateTypeAdapter.write(out, value);
+        }
+      };
+    }
+  };
+
+  public static final TypeAdapter<Calendar> CALENDAR = new TypeAdapter<Calendar>() {
+    private static final String YEAR = "year";
+    private static final String MONTH = "month";
+    private static final String DAY_OF_MONTH = "dayOfMonth";
+    private static final String HOUR_OF_DAY = "hourOfDay";
+    private static final String MINUTE = "minute";
+    private static final String SECOND = "second";
+
+    @Override
+    public Calendar read(JsonReader in) throws IOException {
+      if (in.peek() == JsonToken.NULL) {
+        in.nextNull();
+        return  null;
+      }
+      in.beginObject();
+      int year = 0;
+      int month = 0;
+      int dayOfMonth = 0;
+      int hourOfDay = 0;
+      int minute = 0;
+      int second = 0;
+      while (in.peek() != JsonToken.END_OBJECT) {
+        String name = in.nextName();
+        int value = in.nextInt();
+        if (YEAR.equals(name)) {
+          year = value;
+        } else if (MONTH.equals(name)) {
+          month = value;
+        } else if (DAY_OF_MONTH.equals(name)) {
+          dayOfMonth = value;
+        } else if (HOUR_OF_DAY.equals(name)) {
+          hourOfDay = value;
+        } else if (MINUTE.equals(name)) {
+          minute = value;
+        } else if (SECOND.equals(name)) {
+          second = value;
+        }
+      }
+      in.endObject();
+      return new GregorianCalendar(year, month, dayOfMonth, hourOfDay, minute, second);
+    }
+
+    @Override
+    public void write(JsonWriter out, Calendar value) throws IOException {
+      if (value == null) {
+        out.nullValue();
+        return;
+      }
+      out.beginObject();
+      out.name(YEAR);
+      out.value(value.get(Calendar.YEAR));
+      out.name(MONTH);
+      out.value(value.get(Calendar.MONTH));
+      out.name(DAY_OF_MONTH);
+      out.value(value.get(Calendar.DAY_OF_MONTH));
+      out.name(HOUR_OF_DAY);
+      out.value(value.get(Calendar.HOUR_OF_DAY));
+      out.name(MINUTE);
+      out.value(value.get(Calendar.MINUTE));
+      out.name(SECOND);
+      out.value(value.get(Calendar.SECOND));
+      out.endObject();
+    }
+  };
+
+  public static final TypeAdapterFactory CALENDAR_FACTORY =
+    newFactoryForMultipleTypes(Calendar.class, GregorianCalendar.class, CALENDAR);
+
+  public static final TypeAdapter<Locale> LOCALE = new TypeAdapter<Locale>() {
+    @Override
+    public Locale read(JsonReader in) throws IOException {
+      if (in.peek() == JsonToken.NULL) {
+        in.nextNull();
+        return null;
+      }
+      String locale = in.nextString();
+      StringTokenizer tokenizer = new StringTokenizer(locale, "_");
+      String language = null;
+      String country = null;
+      String variant = null;
+      if (tokenizer.hasMoreElements()) {
+        language = tokenizer.nextToken();
+      }
+      if (tokenizer.hasMoreElements()) {
+        country = tokenizer.nextToken();
+      }
+      if (tokenizer.hasMoreElements()) {
+        variant = tokenizer.nextToken();
+      }
+      if (country == null && variant == null) {
+        return new Locale(language);
+      } else if (variant == null) {
+        return new Locale(language, country);
+      } else {
+        return new Locale(language, country, variant);
+      }
+    }
+    @Override
+    public void write(JsonWriter out, Locale value) throws IOException {
+      out.value(value == null ? null : value.toString());
+    }
+  };
+
+  public static final TypeAdapterFactory LOCALE_FACTORY = newFactory(Locale.class, LOCALE);
+
+  public static final TypeAdapter<JsonElement> JSON_ELEMENT = new TypeAdapter<JsonElement>() {
+    @Override public JsonElement read(JsonReader in) throws IOException {
+      switch (in.peek()) {
+      case STRING:
+        return new JsonPrimitive(in.nextString());
+      case NUMBER:
+        String number = in.nextString();
+        return new JsonPrimitive(new LazilyParsedNumber(number));
+      case BOOLEAN:
+        return new JsonPrimitive(in.nextBoolean());
+      case NULL:
+        in.nextNull();
+        return JsonNull.INSTANCE;
+      case BEGIN_ARRAY:
+        JsonArray array = new JsonArray();
+        in.beginArray();
+        while (in.hasNext()) {
+          array.add(read(in));
+        }
+        in.endArray();
+        return array;
+      case BEGIN_OBJECT:
+        JsonObject object = new JsonObject();
+        in.beginObject();
+        while (in.hasNext()) {
+          object.add(in.nextName(), read(in));
+        }
+        in.endObject();
+        return object;
+      case END_DOCUMENT:
+      case NAME:
+      case END_OBJECT:
+      case END_ARRAY:
+      default:
+        throw new IllegalArgumentException();
+      }
+    }
+
+    @Override public void write(JsonWriter out, JsonElement value) throws IOException {
+      if (value == null || value.isJsonNull()) {
+        out.nullValue();
+      } else if (value.isJsonPrimitive()) {
+        JsonPrimitive primitive = value.getAsJsonPrimitive();
+        if (primitive.isNumber()) {
+          out.value(primitive.getAsNumber());
+        } else if (primitive.isBoolean()) {
+          out.value(primitive.getAsBoolean());
+        } else {
+          out.value(primitive.getAsString());
+        }
+
+      } else if (value.isJsonArray()) {
+        out.beginArray();
+        for (JsonElement e : value.getAsJsonArray()) {
+          write(out, e);
+        }
+        out.endArray();
+
+      } else if (value.isJsonObject()) {
+        out.beginObject();
+        for (Map.Entry<String, JsonElement> e : value.getAsJsonObject().entrySet()) {
+          out.name(e.getKey());
+          write(out, e.getValue());
+        }
+        out.endObject();
+
+      } else {
+        throw new IllegalArgumentException("Couldn't write " + value.getClass());
+      }
+    }
+  };
+
+  public static final TypeAdapterFactory JSON_ELEMENT_FACTORY
+      = newTypeHierarchyFactory(JsonElement.class, JSON_ELEMENT);
+
+  private static final class EnumTypeAdapter<T extends Enum<T>> extends TypeAdapter<T> {
+    private final Map<String, T> nameToConstant = new HashMap<String, T>();
+    private final Map<T, String> constantToName = new HashMap<T, String>();
+
+    public EnumTypeAdapter(Class<T> classOfT) {
+      try {
+        for (T constant : classOfT.getEnumConstants()) {
+          String name = constant.name();
+          SerializedName annotation = classOfT.getField(name).getAnnotation(SerializedName.class);
+          if (annotation != null) {
+            name = annotation.value();
+            for (String alternate : annotation.alternate()) {
+              nameToConstant.put(alternate, constant);
+            }
+          }
+          nameToConstant.put(name, constant);
+          constantToName.put(constant, name);
+        }
+      } catch (NoSuchFieldException e) {
+        throw new AssertionError(e);
+      }
+    }
+    @Override public T read(JsonReader in) throws IOException {
+      if (in.peek() == JsonToken.NULL) {
+        in.nextNull();
+        return null;
+      }
+      return nameToConstant.get(in.nextString());
+    }
+
+    @Override public void write(JsonWriter out, T value) throws IOException {
+      out.value(value == null ? null : constantToName.get(value));
+    }
+  }
+
+  public static final TypeAdapterFactory ENUM_FACTORY = new TypeAdapterFactory() {
+    @SuppressWarnings({"rawtypes", "unchecked"})
+    @Override public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) {
+      Class<? super T> rawType = typeToken.getRawType();
+      if (!Enum.class.isAssignableFrom(rawType) || rawType == Enum.class) {
+        return null;
+      }
+      if (!rawType.isEnum()) {
+        rawType = rawType.getSuperclass(); // handle anonymous subclasses
+      }
+      return (TypeAdapter<T>) new EnumTypeAdapter(rawType);
+    }
+  };
+
+  public static <TT> TypeAdapterFactory newFactory(
+      final TypeToken<TT> type, final TypeAdapter<TT> typeAdapter) {
+    return new TypeAdapterFactory() {
+      @SuppressWarnings("unchecked") // we use a runtime check to make sure the 'T's equal
+      @Override public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) {
+        return typeToken.equals(type) ? (TypeAdapter<T>) typeAdapter : null;
+      }
+    };
+  }
+
+  public static <TT> TypeAdapterFactory newFactory(
+      final Class<TT> type, final TypeAdapter<TT> typeAdapter) {
+    return new TypeAdapterFactory() {
+      @SuppressWarnings("unchecked") // we use a runtime check to make sure the 'T's equal
+      @Override public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) {
+        return typeToken.getRawType() == type ? (TypeAdapter<T>) typeAdapter : null;
+      }
+      @Override public String toString() {
+        return "Factory[type=" + type.getName() + ",adapter=" + typeAdapter + "]";
+      }
+    };
+  }
+
+  public static <TT> TypeAdapterFactory newFactory(
+      final Class<TT> unboxed, final Class<TT> boxed, final TypeAdapter<? super TT> typeAdapter) {
+    return new TypeAdapterFactory() {
+      @SuppressWarnings("unchecked") // we use a runtime check to make sure the 'T's equal
+      @Override public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) {
+        Class<? super T> rawType = typeToken.getRawType();
+        return (rawType == unboxed || rawType == boxed) ? (TypeAdapter<T>) typeAdapter : null;
+      }
+      @Override public String toString() {
+        return "Factory[type=" + boxed.getName()
+            + "+" + unboxed.getName() + ",adapter=" + typeAdapter + "]";
+      }
+    };
+  }
+
+  public static <TT> TypeAdapterFactory newFactoryForMultipleTypes(final Class<TT> base,
+      final Class<? extends TT> sub, final TypeAdapter<? super TT> typeAdapter) {
+    return new TypeAdapterFactory() {
+      @SuppressWarnings("unchecked") // we use a runtime check to make sure the 'T's equal
+      @Override public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) {
+        Class<? super T> rawType = typeToken.getRawType();
+        return (rawType == base || rawType == sub) ? (TypeAdapter<T>) typeAdapter : null;
+      }
+      @Override public String toString() {
+        return "Factory[type=" + base.getName()
+            + "+" + sub.getName() + ",adapter=" + typeAdapter + "]";
+      }
+    };
+  }
+
+  /**
+   * Returns a factory for all subtypes of {@code typeAdapter}. We do a runtime check to confirm
+   * that the deserialized type matches the type requested.
+   */
+  public static <T1> TypeAdapterFactory newTypeHierarchyFactory(
+      final Class<T1> clazz, final TypeAdapter<T1> typeAdapter) {
+    return new TypeAdapterFactory() {
+      @SuppressWarnings("unchecked")
+      @Override public <T2> TypeAdapter<T2> create(Gson gson, TypeToken<T2> typeToken) {
+        final Class<? super T2> requestedType = typeToken.getRawType();
+        if (!clazz.isAssignableFrom(requestedType)) {
+          return null;
+        }
+        return (TypeAdapter<T2>) new TypeAdapter<T1>() {
+          @Override public void write(JsonWriter out, T1 value) throws IOException {
+            typeAdapter.write(out, value);
+          }
+
+          @Override public T1 read(JsonReader in) throws IOException {
+            T1 result = typeAdapter.read(in);
+            if (result != null && !requestedType.isInstance(result)) {
+              throw new JsonSyntaxException("Expected a " + requestedType.getName()
+                  + " but was " + result.getClass().getName());
+            }
+            return result;
+          }
+        };
+      }
+      @Override public String toString() {
+        return "Factory[typeHierarchy=" + clazz.getName() + ",adapter=" + typeAdapter + "]";
+      }
+    };
+  }
+}
\ No newline at end of file
diff --git a/gson/src/main/java/com/google/gson/internal/bind/util/ISO8601Utils.java b/gson/src/main/java/com/google/gson/internal/bind/util/ISO8601Utils.java
new file mode 100644
index 0000000000000000000000000000000000000000..99ec679a71d199cbc56912ccc0583377026260d3
--- /dev/null
+++ b/gson/src/main/java/com/google/gson/internal/bind/util/ISO8601Utils.java
@@ -0,0 +1,352 @@
+package com.google.gson.internal.bind.util;
+
+import java.text.ParseException;
+import java.text.ParsePosition;
+import java.util.*;
+
+/**
+ * Utilities methods for manipulating dates in iso8601 format. This is much much faster and GC friendly than using SimpleDateFormat so
+ * highly suitable if you (un)serialize lots of date objects.
+ * 
+ * Supported parse format: [yyyy-MM-dd|yyyyMMdd][T(hh:mm[:ss[.sss]]|hhmm[ss[.sss]])]?[Z|[+-]hh[:]mm]]
+ * 
+ * @see <a href="http://www.w3.org/TR/NOTE-datetime">this specification</a>
+ */
+//Date parsing code from Jackson databind ISO8601Utils.java
+// https://github.com/FasterXML/jackson-databind/blob/master/src/main/java/com/fasterxml/jackson/databind/util/ISO8601Utils.java
+public class ISO8601Utils
+{
+    /**
+     * ID to represent the 'UTC' string, default timezone since Jackson 2.7
+     * 
+     * @since 2.7
+     */
+    private static final String UTC_ID = "UTC";
+    /**
+     * The UTC timezone, prefetched to avoid more lookups.
+     * 
+     * @since 2.7
+     */
+    private static final TimeZone TIMEZONE_UTC = TimeZone.getTimeZone(UTC_ID);
+
+    /*
+    /**********************************************************
+    /* Formatting
+    /**********************************************************
+     */
+
+    /**
+     * Format a date into 'yyyy-MM-ddThh:mm:ssZ' (default timezone, no milliseconds precision)
+     * 
+     * @param date the date to format
+     * @return the date formatted as 'yyyy-MM-ddThh:mm:ssZ'
+     */
+    public static String format(Date date) {
+        return format(date, false, TIMEZONE_UTC);
+    }
+
+    /**
+     * Format a date into 'yyyy-MM-ddThh:mm:ss[.sss]Z' (GMT timezone)
+     * 
+     * @param date the date to format
+     * @param millis true to include millis precision otherwise false
+     * @return the date formatted as 'yyyy-MM-ddThh:mm:ss[.sss]Z'
+     */
+    public static String format(Date date, boolean millis) {
+        return format(date, millis, TIMEZONE_UTC);
+    }
+
+    /**
+     * Format date into yyyy-MM-ddThh:mm:ss[.sss][Z|[+-]hh:mm]
+     * 
+     * @param date the date to format
+     * @param millis true to include millis precision otherwise false
+     * @param tz timezone to use for the formatting (UTC will produce 'Z')
+     * @return the date formatted as yyyy-MM-ddThh:mm:ss[.sss][Z|[+-]hh:mm]
+     */
+    public static String format(Date date, boolean millis, TimeZone tz) {
+        Calendar calendar = new GregorianCalendar(tz, Locale.US);
+        calendar.setTime(date);
+
+        // estimate capacity of buffer as close as we can (yeah, that's pedantic ;)
+        int capacity = "yyyy-MM-ddThh:mm:ss".length();
+        capacity += millis ? ".sss".length() : 0;
+        capacity += tz.getRawOffset() == 0 ? "Z".length() : "+hh:mm".length();
+        StringBuilder formatted = new StringBuilder(capacity);
+
+        padInt(formatted, calendar.get(Calendar.YEAR), "yyyy".length());
+        formatted.append('-');
+        padInt(formatted, calendar.get(Calendar.MONTH) + 1, "MM".length());
+        formatted.append('-');
+        padInt(formatted, calendar.get(Calendar.DAY_OF_MONTH), "dd".length());
+        formatted.append('T');
+        padInt(formatted, calendar.get(Calendar.HOUR_OF_DAY), "hh".length());
+        formatted.append(':');
+        padInt(formatted, calendar.get(Calendar.MINUTE), "mm".length());
+        formatted.append(':');
+        padInt(formatted, calendar.get(Calendar.SECOND), "ss".length());
+        if (millis) {
+            formatted.append('.');
+            padInt(formatted, calendar.get(Calendar.MILLISECOND), "sss".length());
+        }
+
+        int offset = tz.getOffset(calendar.getTimeInMillis());
+        if (offset != 0) {
+            int hours = Math.abs((offset / (60 * 1000)) / 60);
+            int minutes = Math.abs((offset / (60 * 1000)) % 60);
+            formatted.append(offset < 0 ? '-' : '+');
+            padInt(formatted, hours, "hh".length());
+            formatted.append(':');
+            padInt(formatted, minutes, "mm".length());
+        } else {
+            formatted.append('Z');
+        }
+
+        return formatted.toString();
+    }
+
+    /*
+    /**********************************************************
+    /* Parsing
+    /**********************************************************
+     */
+
+    /**
+     * Parse a date from ISO-8601 formatted string. It expects a format
+     * [yyyy-MM-dd|yyyyMMdd][T(hh:mm[:ss[.sss]]|hhmm[ss[.sss]])]?[Z|[+-]hh[:mm]]]
+     * 
+     * @param date ISO string to parse in the appropriate format.
+     * @param pos The position to start parsing from, updated to where parsing stopped.
+     * @return the parsed date
+     * @throws ParseException if the date is not in the appropriate format
+     */
+    public static Date parse(String date, ParsePosition pos) throws ParseException {
+        Exception fail = null;
+        try {
+            int offset = pos.getIndex();
+
+            // extract year
+            int year = parseInt(date, offset, offset += 4);
+            if (checkOffset(date, offset, '-')) {
+                offset += 1;
+            }
+
+            // extract month
+            int month = parseInt(date, offset, offset += 2);
+            if (checkOffset(date, offset, '-')) {
+                offset += 1;
+            }
+
+            // extract day
+            int day = parseInt(date, offset, offset += 2);
+            // default time value
+            int hour = 0;
+            int minutes = 0;
+            int seconds = 0;
+            int milliseconds = 0; // always use 0 otherwise returned date will include millis of current time
+
+            // if the value has no time component (and no time zone), we are done
+            boolean hasT = checkOffset(date, offset, 'T');
+            
+            if (!hasT && (date.length() <= offset)) {
+                Calendar calendar = new GregorianCalendar(year, month - 1, day);
+
+                pos.setIndex(offset);
+                return calendar.getTime();
+            }
+
+            if (hasT) {
+
+                // extract hours, minutes, seconds and milliseconds
+                hour = parseInt(date, offset += 1, offset += 2);
+                if (checkOffset(date, offset, ':')) {
+                    offset += 1;
+                }
+
+                minutes = parseInt(date, offset, offset += 2);
+                if (checkOffset(date, offset, ':')) {
+                    offset += 1;
+                }
+                // second and milliseconds can be optional
+                if (date.length() > offset) {
+                    char c = date.charAt(offset);
+                    if (c != 'Z' && c != '+' && c != '-') {
+                        seconds = parseInt(date, offset, offset += 2);
+                        if (seconds > 59 && seconds < 63) seconds = 59; // truncate up to 3 leap seconds
+                        // milliseconds can be optional in the format
+                        if (checkOffset(date, offset, '.')) {
+                            offset += 1;
+                            int endOffset = indexOfNonDigit(date, offset + 1); // assume at least one digit
+                            int parseEndOffset = Math.min(endOffset, offset + 3); // parse up to 3 digits
+                            int fraction = parseInt(date, offset, parseEndOffset);
+                            // compensate for "missing" digits
+                            switch (parseEndOffset - offset) { // number of digits parsed
+                            case 2:
+                                milliseconds = fraction * 10;
+                                break;
+                            case 1:
+                                milliseconds = fraction * 100;
+                                break;
+                            default:
+                                milliseconds = fraction;
+                            }
+                            offset = endOffset;
+                        }
+                    }
+                }
+            }
+
+            // extract timezone
+            if (date.length() <= offset) {
+                throw new IllegalArgumentException("No time zone indicator");
+            }
+
+            TimeZone timezone = null;
+            char timezoneIndicator = date.charAt(offset);
+
+            if (timezoneIndicator == 'Z') {
+                timezone = TIMEZONE_UTC;
+                offset += 1;
+            } else if (timezoneIndicator == '+' || timezoneIndicator == '-') {
+                String timezoneOffset = date.substring(offset);
+
+                // When timezone has no minutes, we should append it, valid timezones are, for example: +00:00, +0000 and +00
+                timezoneOffset = timezoneOffset.length() >= 5 ? timezoneOffset : timezoneOffset + "00";
+
+                offset += timezoneOffset.length();
+                // 18-Jun-2015, tatu: Minor simplification, skip offset of "+0000"/"+00:00"
+                if ("+0000".equals(timezoneOffset) || "+00:00".equals(timezoneOffset)) {
+                    timezone = TIMEZONE_UTC;
+                } else {
+                    // 18-Jun-2015, tatu: Looks like offsets only work from GMT, not UTC...
+                    //    not sure why, but that's the way it looks. Further, Javadocs for
+                    //    `java.util.TimeZone` specifically instruct use of GMT as base for
+                    //    custom timezones... odd.
+                    String timezoneId = "GMT" + timezoneOffset;
+//                    String timezoneId = "UTC" + timezoneOffset;
+
+                    timezone = TimeZone.getTimeZone(timezoneId);
+
+                    String act = timezone.getID();
+                    if (!act.equals(timezoneId)) {
+                        /* 22-Jan-2015, tatu: Looks like canonical version has colons, but we may be given
+                         *    one without. If so, don't sweat.
+                         *   Yes, very inefficient. Hopefully not hit often.
+                         *   If it becomes a perf problem, add 'loose' comparison instead.
+                         */
+                        String cleaned = act.replace(":", "");
+                        if (!cleaned.equals(timezoneId)) {
+                            throw new IndexOutOfBoundsException("Mismatching time zone indicator: "+timezoneId+" given, resolves to "
+                                    +timezone.getID());
+                        }
+                    }
+                }
+            } else {
+                throw new IndexOutOfBoundsException("Invalid time zone indicator '" + timezoneIndicator+"'");
+            }
+
+            Calendar calendar = new GregorianCalendar(timezone);
+            calendar.setLenient(false);
+            calendar.set(Calendar.YEAR, year);
+            calendar.set(Calendar.MONTH, month - 1);
+            calendar.set(Calendar.DAY_OF_MONTH, day);
+            calendar.set(Calendar.HOUR_OF_DAY, hour);
+            calendar.set(Calendar.MINUTE, minutes);
+            calendar.set(Calendar.SECOND, seconds);
+            calendar.set(Calendar.MILLISECOND, milliseconds);
+
+            pos.setIndex(offset);
+            return calendar.getTime();
+            // If we get a ParseException it'll already have the right message/offset.
+            // Other exception types can convert here.
+        } catch (IndexOutOfBoundsException e) {
+            fail = e;
+        } catch (NumberFormatException e) {
+            fail = e;
+        } catch (IllegalArgumentException e) {
+            fail = e;
+        }
+        String input = (date == null) ? null : ('"' + date + '"');
+        String msg = fail.getMessage();
+        if (msg == null || msg.isEmpty()) {
+            msg = "("+fail.getClass().getName()+")";
+        }
+        ParseException ex = new ParseException("Failed to parse date [" + input + "]: " + msg, pos.getIndex());
+        ex.initCause(fail);
+        throw ex;
+    }
+
+    /**
+     * Check if the expected character exist at the given offset in the value.
+     * 
+     * @param value the string to check at the specified offset
+     * @param offset the offset to look for the expected character
+     * @param expected the expected character
+     * @return true if the expected character exist at the given offset
+     */
+    private static boolean checkOffset(String value, int offset, char expected) {
+        return (offset < value.length()) && (value.charAt(offset) == expected);
+    }
+
+    /**
+     * Parse an integer located between 2 given offsets in a string
+     * 
+     * @param value the string to parse
+     * @param beginIndex the start index for the integer in the string
+     * @param endIndex the end index for the integer in the string
+     * @return the int
+     * @throws NumberFormatException if the value is not a number
+     */
+    private static int parseInt(String value, int beginIndex, int endIndex) throws NumberFormatException {
+        if (beginIndex < 0 || endIndex > value.length() || beginIndex > endIndex) {
+            throw new NumberFormatException(value);
+        }
+        // use same logic as in Integer.parseInt() but less generic we're not supporting negative values
+        int i = beginIndex;
+        int result = 0;
+        int digit;
+        if (i < endIndex) {
+            digit = Character.digit(value.charAt(i++), 10);
+            if (digit < 0) {
+                throw new NumberFormatException("Invalid number: " + value.substring(beginIndex, endIndex));
+            }
+            result = -digit;
+        }
+        while (i < endIndex) {
+            digit = Character.digit(value.charAt(i++), 10);
+            if (digit < 0) {
+                throw new NumberFormatException("Invalid number: " + value.substring(beginIndex, endIndex));
+            }
+            result *= 10;
+            result -= digit;
+        }
+        return -result;
+    }
+
+    /**
+     * Zero pad a number to a specified length
+     * 
+     * @param buffer buffer to use for padding
+     * @param value the integer value to pad if necessary.
+     * @param length the length of the string we should zero pad
+     */
+    private static void padInt(StringBuilder buffer, int value, int length) {
+        String strValue = Integer.toString(value);
+        for (int i = length - strValue.length(); i > 0; i--) {
+            buffer.append('0');
+        }
+        buffer.append(strValue);
+    }
+
+    /**
+     * Returns the index of the first character in the string that is not a digit, starting at offset.
+     */
+    private static int indexOfNonDigit(String string, int offset) {
+        for (int i = offset; i < string.length(); i++) {
+            char c = string.charAt(i);
+            if (c < '0' || c > '9') return i;
+        }
+        return string.length();
+    }
+
+}
diff --git a/gson/src/main/java/com/google/gson/internal/package-info.java b/gson/src/main/java/com/google/gson/internal/package-info.java
new file mode 100644
index 0000000000000000000000000000000000000000..b5139b6de25067cf360716c3c5f95e94dd272104
--- /dev/null
+++ b/gson/src/main/java/com/google/gson/internal/package-info.java
@@ -0,0 +1,7 @@
+/**
+ * Do NOT use any class in this package as they are meant for internal use in Gson.
+ * These classes will very likely change incompatibly in future versions. You have been warned.
+ *
+ * @author Inderjeet Singh, Joel Leitch, Jesse Wilson
+ */
+package com.google.gson.internal;
\ No newline at end of file
diff --git a/gson/src/main/java/com/google/gson/internal/reflect/PreJava9ReflectionAccessor.java b/gson/src/main/java/com/google/gson/internal/reflect/PreJava9ReflectionAccessor.java
new file mode 100644
index 0000000000000000000000000000000000000000..325274e2248d4171b6768ccb925272f64e6902e0
--- /dev/null
+++ b/gson/src/main/java/com/google/gson/internal/reflect/PreJava9ReflectionAccessor.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2017 The Gson authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.google.gson.internal.reflect;
+
+import java.lang.reflect.AccessibleObject;
+
+/**
+ * A basic implementation of {@link ReflectionAccessor} which is suitable for Java 8 and below.
+ * <p>
+ * This implementation just calls {@link AccessibleObject#setAccessible(boolean) setAccessible(true)}, which worked
+ * fine before Java 9.
+ */
+final class PreJava9ReflectionAccessor extends ReflectionAccessor {
+
+  /** {@inheritDoc} */
+  @Override
+  public void makeAccessible(AccessibleObject ao) {
+    ao.setAccessible(true);
+  }
+}
diff --git a/gson/src/main/java/com/google/gson/internal/reflect/ReflectionAccessor.java b/gson/src/main/java/com/google/gson/internal/reflect/ReflectionAccessor.java
new file mode 100644
index 0000000000000000000000000000000000000000..d74ab24ab74d0b670affd6f824efdea1be5ec059
--- /dev/null
+++ b/gson/src/main/java/com/google/gson/internal/reflect/ReflectionAccessor.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2017 The Gson authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.google.gson.internal.reflect;
+
+import java.lang.reflect.AccessibleObject;
+
+import com.google.gson.internal.JavaVersion;
+
+/**
+ * Provides a replacement for {@link AccessibleObject#setAccessible(boolean)}, which may be used to
+ * avoid reflective access issues appeared in Java 9, like java.lang.reflect.InaccessibleObjectException
+ * thrown or warnings like
+ * <pre>
+ *   WARNING: An illegal reflective access operation has occurred
+ *   WARNING: Illegal reflective access by ...
+ * </pre>
+ * <p>
+ * Works both for Java 9 and earlier Java versions.
+ * </p>
+ */
+public abstract class ReflectionAccessor {
+
+  // the singleton instance, use getInstance() to obtain
+  private static final ReflectionAccessor instance = JavaVersion.getMajorJavaVersion() < 9 ? new PreJava9ReflectionAccessor() : new UnsafeReflectionAccessor();
+
+  /**
+   * Does the same as {@code ao.setAccessible(true)}, but never throws
+   * java.lang.reflect.InaccessibleObjectException
+   */
+  public abstract void makeAccessible(AccessibleObject ao);
+
+  /**
+   * Obtains a {@link ReflectionAccessor} instance suitable for the current Java version.
+   * <p>
+   * You may need one a reflective operation in your code throws java.lang.reflect.InaccessibleObjectException.
+   * In such a case, use {@link ReflectionAccessor#makeAccessible(AccessibleObject)} on a field, method or constructor
+   * (instead of basic {@link AccessibleObject#setAccessible(boolean)}).
+   */
+  public static ReflectionAccessor getInstance() {
+    return instance;
+  }
+}
diff --git a/gson/src/main/java/com/google/gson/internal/reflect/UnsafeReflectionAccessor.java b/gson/src/main/java/com/google/gson/internal/reflect/UnsafeReflectionAccessor.java
new file mode 100644
index 0000000000000000000000000000000000000000..749335b77a6493fa34ebfcd1b2dc5b33123c0ad4
--- /dev/null
+++ b/gson/src/main/java/com/google/gson/internal/reflect/UnsafeReflectionAccessor.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2017 The Gson authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.google.gson.internal.reflect;
+
+import java.lang.reflect.AccessibleObject;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+
+import com.google.gson.JsonIOException;
+
+/**
+ * An implementation of {@link ReflectionAccessor} based on {@link Unsafe}.
+ * <p>
+ * NOTE: This implementation is designed for Java 9. Although it should work with earlier Java releases, it is better to
+ * use {@link PreJava9ReflectionAccessor} for them.
+ */
+@SuppressWarnings({"unchecked", "rawtypes"})
+final class UnsafeReflectionAccessor extends ReflectionAccessor {
+
+  private static Class unsafeClass;
+  private final Object theUnsafe = getUnsafeInstance();
+  private final Field overrideField = getOverrideField();
+
+  /** {@inheritDoc} */
+  @Override
+  public void makeAccessible(AccessibleObject ao) {
+    boolean success = makeAccessibleWithUnsafe(ao);
+    if (!success) {
+      try {
+        // unsafe couldn't be found, so try using accessible anyway
+        ao.setAccessible(true);
+      } catch (SecurityException e) {
+        throw new JsonIOException("Gson couldn't modify fields for " + ao
+          + "\nand sun.misc.Unsafe not found.\nEither write a custom type adapter,"
+          + " or make fields accessible, or include sun.misc.Unsafe.", e);
+      }
+    }
+  }
+
+  // Visible for testing only
+  boolean makeAccessibleWithUnsafe(AccessibleObject ao) {
+    if (theUnsafe != null && overrideField != null) {
+      try {
+        Method method = unsafeClass.getMethod("objectFieldOffset", Field.class);
+        long overrideOffset = (Long) method.invoke(theUnsafe, overrideField);  // long overrideOffset = theUnsafe.objectFieldOffset(overrideField);
+        Method putBooleanMethod = unsafeClass.getMethod("putBoolean",  Object.class, long.class, boolean.class);
+        putBooleanMethod.invoke(theUnsafe, ao, overrideOffset, true); // theUnsafe.putBoolean(ao, overrideOffset, true);
+        return true;
+      } catch (Exception ignored) { // do nothing
+      }
+    }
+    return false;
+  }
+
+  private static Object getUnsafeInstance() {
+    try {
+      unsafeClass = Class.forName("sun.misc.Unsafe");
+      Field unsafeField = unsafeClass.getDeclaredField("theUnsafe");
+      unsafeField.setAccessible(true);
+      return unsafeField.get(null);
+    } catch (Exception e) {
+      return null;
+    }
+  }
+
+  private static Field getOverrideField() {
+    try {
+      return AccessibleObject.class.getDeclaredField("override");
+    } catch (NoSuchFieldException e) {
+      return null;
+    }
+  }
+}
diff --git a/gson/src/main/java/com/google/gson/package-info.java b/gson/src/main/java/com/google/gson/package-info.java
new file mode 100644
index 0000000000000000000000000000000000000000..428e280cf424efb759c4bb1b2b41eb943fb7a216
--- /dev/null
+++ b/gson/src/main/java/com/google/gson/package-info.java
@@ -0,0 +1,11 @@
+/**
+ * This package provides the {@link com.google.gson.Gson} class to convert Json to Java and
+ * vice-versa.
+ *
+ * <p>The primary class to use is {@link com.google.gson.Gson} which can be constructed with
+ * {@code new Gson()} (using default settings) or by using {@link com.google.gson.GsonBuilder}
+ * (to configure various options such as using versioning and so on).</p>
+ *
+ * @author Inderjeet Singh, Joel Leitch
+ */
+package com.google.gson;
\ No newline at end of file
diff --git a/gson/src/main/java/com/google/gson/reflect/TypeToken.java b/gson/src/main/java/com/google/gson/reflect/TypeToken.java
new file mode 100644
index 0000000000000000000000000000000000000000..3fb8af2bcfb593b319d83747eeddd034df3bbb46
--- /dev/null
+++ b/gson/src/main/java/com/google/gson/reflect/TypeToken.java
@@ -0,0 +1,320 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.gson.reflect;
+
+import com.google.gson.internal.$Gson$Types;
+import com.google.gson.internal.$Gson$Preconditions;
+import java.lang.reflect.GenericArrayType;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.lang.reflect.TypeVariable;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Represents a generic type {@code T}. Java doesn't yet provide a way to
+ * represent generic types, so this class does. Forces clients to create a
+ * subclass of this class which enables retrieval the type information even at
+ * runtime.
+ *
+ * <p>For example, to create a type literal for {@code List<String>}, you can
+ * create an empty anonymous inner class:
+ *
+ * <p>
+ * {@code TypeToken<List<String>> list = new TypeToken<List<String>>() {};}
+ *
+ * <p>This syntax cannot be used to create type literals that have wildcard
+ * parameters, such as {@code Class<?>} or {@code List<? extends CharSequence>}.
+ *
+ * @author Bob Lee
+ * @author Sven Mawson
+ * @author Jesse Wilson
+ */
+public class TypeToken<T> {
+  final Class<? super T> rawType;
+  final Type type;
+  final int hashCode;
+
+  /**
+   * Constructs a new type literal. Derives represented class from type
+   * parameter.
+   *
+   * <p>Clients create an empty anonymous subclass. Doing so embeds the type
+   * parameter in the anonymous class's type hierarchy so we can reconstitute it
+   * at runtime despite erasure.
+   */
+  @SuppressWarnings("unchecked")
+  protected TypeToken() {
+    this.type = getSuperclassTypeParameter(getClass());
+    this.rawType = (Class<? super T>) $Gson$Types.getRawType(type);
+    this.hashCode = type.hashCode();
+  }
+
+  /**
+   * Unsafe. Constructs a type literal manually.
+   */
+  @SuppressWarnings("unchecked")
+  TypeToken(Type type) {
+    this.type = $Gson$Types.canonicalize($Gson$Preconditions.checkNotNull(type));
+    this.rawType = (Class<? super T>) $Gson$Types.getRawType(this.type);
+    this.hashCode = this.type.hashCode();
+  }
+
+  /**
+   * Returns the type from super class's type parameter in {@link $Gson$Types#canonicalize
+   * canonical form}.
+   */
+  static Type getSuperclassTypeParameter(Class<?> subclass) {
+    Type superclass = subclass.getGenericSuperclass();
+    if (superclass instanceof Class) {
+      throw new RuntimeException("Missing type parameter.");
+    }
+    ParameterizedType parameterized = (ParameterizedType) superclass;
+    return $Gson$Types.canonicalize(parameterized.getActualTypeArguments()[0]);
+  }
+
+  /**
+   * Returns the raw (non-generic) type for this type.
+   */
+  public final Class<? super T> getRawType() {
+    return rawType;
+  }
+
+  /**
+   * Gets underlying {@code Type} instance.
+   */
+  public final Type getType() {
+    return type;
+  }
+
+  /**
+   * Check if this type is assignable from the given class object.
+   *
+   * @deprecated this implementation may be inconsistent with javac for types
+   *     with wildcards.
+   */
+  @Deprecated
+  public boolean isAssignableFrom(Class<?> cls) {
+    return isAssignableFrom((Type) cls);
+  }
+
+  /**
+   * Check if this type is assignable from the given Type.
+   *
+   * @deprecated this implementation may be inconsistent with javac for types
+   *     with wildcards.
+   */
+  @Deprecated
+  public boolean isAssignableFrom(Type from) {
+    if (from == null) {
+      return false;
+    }
+
+    if (type.equals(from)) {
+      return true;
+    }
+
+    if (type instanceof Class<?>) {
+      return rawType.isAssignableFrom($Gson$Types.getRawType(from));
+    } else if (type instanceof ParameterizedType) {
+      return isAssignableFrom(from, (ParameterizedType) type,
+          new HashMap<String, Type>());
+    } else if (type instanceof GenericArrayType) {
+      return rawType.isAssignableFrom($Gson$Types.getRawType(from))
+          && isAssignableFrom(from, (GenericArrayType) type);
+    } else {
+      throw buildUnexpectedTypeError(
+          type, Class.class, ParameterizedType.class, GenericArrayType.class);
+    }
+  }
+
+  /**
+   * Check if this type is assignable from the given type token.
+   *
+   * @deprecated this implementation may be inconsistent with javac for types
+   *     with wildcards.
+   */
+  @Deprecated
+  public boolean isAssignableFrom(TypeToken<?> token) {
+    return isAssignableFrom(token.getType());
+  }
+
+  /**
+   * Private helper function that performs some assignability checks for
+   * the provided GenericArrayType.
+   */
+  private static boolean isAssignableFrom(Type from, GenericArrayType to) {
+    Type toGenericComponentType = to.getGenericComponentType();
+    if (toGenericComponentType instanceof ParameterizedType) {
+      Type t = from;
+      if (from instanceof GenericArrayType) {
+        t = ((GenericArrayType) from).getGenericComponentType();
+      } else if (from instanceof Class<?>) {
+        Class<?> classType = (Class<?>) from;
+        while (classType.isArray()) {
+          classType = classType.getComponentType();
+        }
+        t = classType;
+      }
+      return isAssignableFrom(t, (ParameterizedType) toGenericComponentType,
+          new HashMap<String, Type>());
+    }
+    // No generic defined on "to"; therefore, return true and let other
+    // checks determine assignability
+    return true;
+  }
+
+  /**
+   * Private recursive helper function to actually do the type-safe checking
+   * of assignability.
+   */
+  private static boolean isAssignableFrom(Type from, ParameterizedType to,
+      Map<String, Type> typeVarMap) {
+
+    if (from == null) {
+      return false;
+    }
+
+    if (to.equals(from)) {
+      return true;
+    }
+
+    // First figure out the class and any type information.
+    Class<?> clazz = $Gson$Types.getRawType(from);
+    ParameterizedType ptype = null;
+    if (from instanceof ParameterizedType) {
+      ptype = (ParameterizedType) from;
+    }
+
+    // Load up parameterized variable info if it was parameterized.
+    if (ptype != null) {
+      Type[] tArgs = ptype.getActualTypeArguments();
+      TypeVariable<?>[] tParams = clazz.getTypeParameters();
+      for (int i = 0; i < tArgs.length; i++) {
+        Type arg = tArgs[i];
+        TypeVariable<?> var = tParams[i];
+        while (arg instanceof TypeVariable<?>) {
+          TypeVariable<?> v = (TypeVariable<?>) arg;
+          arg = typeVarMap.get(v.getName());
+        }
+        typeVarMap.put(var.getName(), arg);
+      }
+
+      // check if they are equivalent under our current mapping.
+      if (typeEquals(ptype, to, typeVarMap)) {
+        return true;
+      }
+    }
+
+    for (Type itype : clazz.getGenericInterfaces()) {
+      if (isAssignableFrom(itype, to, new HashMap<String, Type>(typeVarMap))) {
+        return true;
+      }
+    }
+
+    // Interfaces didn't work, try the superclass.
+    Type sType = clazz.getGenericSuperclass();
+    return isAssignableFrom(sType, to, new HashMap<String, Type>(typeVarMap));
+  }
+
+  /**
+   * Checks if two parameterized types are exactly equal, under the variable
+   * replacement described in the typeVarMap.
+   */
+  private static boolean typeEquals(ParameterizedType from,
+      ParameterizedType to, Map<String, Type> typeVarMap) {
+    if (from.getRawType().equals(to.getRawType())) {
+      Type[] fromArgs = from.getActualTypeArguments();
+      Type[] toArgs = to.getActualTypeArguments();
+      for (int i = 0; i < fromArgs.length; i++) {
+        if (!matches(fromArgs[i], toArgs[i], typeVarMap)) {
+          return false;
+        }
+      }
+      return true;
+    }
+    return false;
+  }
+
+  private static AssertionError buildUnexpectedTypeError(
+      Type token, Class<?>... expected) {
+
+    // Build exception message
+    StringBuilder exceptionMessage =
+        new StringBuilder("Unexpected type. Expected one of: ");
+    for (Class<?> clazz : expected) {
+      exceptionMessage.append(clazz.getName()).append(", ");
+    }
+    exceptionMessage.append("but got: ").append(token.getClass().getName())
+        .append(", for type token: ").append(token.toString()).append('.');
+
+    return new AssertionError(exceptionMessage.toString());
+  }
+
+  /**
+   * Checks if two types are the same or are equivalent under a variable mapping
+   * given in the type map that was provided.
+   */
+  private static boolean matches(Type from, Type to, Map<String, Type> typeMap) {
+    return to.equals(from)
+        || (from instanceof TypeVariable
+        && to.equals(typeMap.get(((TypeVariable<?>) from).getName())));
+
+  }
+
+  @Override public final int hashCode() {
+    return this.hashCode;
+  }
+
+  @Override public final boolean equals(Object o) {
+    return o instanceof TypeToken<?>
+        && $Gson$Types.equals(type, ((TypeToken<?>) o).type);
+  }
+
+  @Override public final String toString() {
+    return $Gson$Types.typeToString(type);
+  }
+
+  /**
+   * Gets type literal for the given {@code Type} instance.
+   */
+  public static TypeToken<?> get(Type type) {
+    return new TypeToken<Object>(type);
+  }
+
+  /**
+   * Gets type literal for the given {@code Class} instance.
+   */
+  public static <T> TypeToken<T> get(Class<T> type) {
+    return new TypeToken<T>(type);
+  }
+
+  /**
+   * Gets type literal for the parameterized type represented by applying {@code typeArguments} to
+   * {@code rawType}.
+   */
+  public static TypeToken<?> getParameterized(Type rawType, Type... typeArguments) {
+    return new TypeToken<Object>($Gson$Types.newParameterizedTypeWithOwner(null, rawType, typeArguments));
+  }
+
+  /**
+   * Gets type literal for the array type whose elements are all instances of {@code componentType}.
+   */
+  public static TypeToken<?> getArray(Type componentType) {
+    return new TypeToken<Object>($Gson$Types.arrayOf(componentType));
+  }
+}
diff --git a/gson/src/main/java/com/google/gson/reflect/package-info.java b/gson/src/main/java/com/google/gson/reflect/package-info.java
new file mode 100644
index 0000000000000000000000000000000000000000..5e43ee9fcb0e6780f7470d0f4df3b44474e5e448
--- /dev/null
+++ b/gson/src/main/java/com/google/gson/reflect/package-info.java
@@ -0,0 +1,6 @@
+/**
+ * This package provides utility classes for finding type information for generic types.
+ *
+ * @author Inderjeet Singh, Joel Leitch
+ */
+package com.google.gson.reflect;
\ No newline at end of file
diff --git a/gson/src/main/java/com/google/gson/stream/JsonReader.java b/gson/src/main/java/com/google/gson/stream/JsonReader.java
new file mode 100644
index 0000000000000000000000000000000000000000..1c0d58a1fd10b83d16f9152e2d6f7b3b7a1b452d
--- /dev/null
+++ b/gson/src/main/java/com/google/gson/stream/JsonReader.java
@@ -0,0 +1,1613 @@
+/*
+ * Copyright (C) 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.gson.stream;
+
+import com.google.gson.internal.JsonReaderInternalAccess;
+import com.google.gson.internal.bind.JsonTreeReader;
+import java.io.Closeable;
+import java.io.EOFException;
+import java.io.IOException;
+import java.io.Reader;
+import java.util.Arrays;
+
+/**
+ * Reads a JSON (<a href="http://www.ietf.org/rfc/rfc7159.txt">RFC 7159</a>)
+ * encoded value as a stream of tokens. This stream includes both literal
+ * values (strings, numbers, booleans, and nulls) as well as the begin and
+ * end delimiters of objects and arrays. The tokens are traversed in
+ * depth-first order, the same order that they appear in the JSON document.
+ * Within JSON objects, name/value pairs are represented by a single token.
+ *
+ * <h3>Parsing JSON</h3>
+ * To create a recursive descent parser for your own JSON streams, first create
+ * an entry point method that creates a {@code JsonReader}.
+ *
+ * <p>Next, create handler methods for each structure in your JSON text. You'll
+ * need a method for each object type and for each array type.
+ * <ul>
+ *   <li>Within <strong>array handling</strong> methods, first call {@link
+ *       #beginArray} to consume the array's opening bracket. Then create a
+ *       while loop that accumulates values, terminating when {@link #hasNext}
+ *       is false. Finally, read the array's closing bracket by calling {@link
+ *       #endArray}.
+ *   <li>Within <strong>object handling</strong> methods, first call {@link
+ *       #beginObject} to consume the object's opening brace. Then create a
+ *       while loop that assigns values to local variables based on their name.
+ *       This loop should terminate when {@link #hasNext} is false. Finally,
+ *       read the object's closing brace by calling {@link #endObject}.
+ * </ul>
+ * <p>When a nested object or array is encountered, delegate to the
+ * corresponding handler method.
+ *
+ * <p>When an unknown name is encountered, strict parsers should fail with an
+ * exception. Lenient parsers should call {@link #skipValue()} to recursively
+ * skip the value's nested tokens, which may otherwise conflict.
+ *
+ * <p>If a value may be null, you should first check using {@link #peek()}.
+ * Null literals can be consumed using either {@link #nextNull()} or {@link
+ * #skipValue()}.
+ *
+ * <h3>Example</h3>
+ * Suppose we'd like to parse a stream of messages such as the following: <pre> {@code
+ * [
+ *   {
+ *     "id": 912345678901,
+ *     "text": "How do I read a JSON stream in Java?",
+ *     "geo": null,
+ *     "user": {
+ *       "name": "json_newb",
+ *       "followers_count": 41
+ *      }
+ *   },
+ *   {
+ *     "id": 912345678902,
+ *     "text": "@json_newb just use JsonReader!",
+ *     "geo": [50.454722, -104.606667],
+ *     "user": {
+ *       "name": "jesse",
+ *       "followers_count": 2
+ *     }
+ *   }
+ * ]}</pre>
+ * This code implements the parser for the above structure: <pre>   {@code
+ *
+ *   public List<Message> readJsonStream(InputStream in) throws IOException {
+ *     JsonReader reader = new JsonReader(new InputStreamReader(in, "UTF-8"));
+ *     try {
+ *       return readMessagesArray(reader);
+ *     } finally {
+ *       reader.close();
+ *     }
+ *   }
+ *
+ *   public List<Message> readMessagesArray(JsonReader reader) throws IOException {
+ *     List<Message> messages = new ArrayList<Message>();
+ *
+ *     reader.beginArray();
+ *     while (reader.hasNext()) {
+ *       messages.add(readMessage(reader));
+ *     }
+ *     reader.endArray();
+ *     return messages;
+ *   }
+ *
+ *   public Message readMessage(JsonReader reader) throws IOException {
+ *     long id = -1;
+ *     String text = null;
+ *     User user = null;
+ *     List<Double> geo = null;
+ *
+ *     reader.beginObject();
+ *     while (reader.hasNext()) {
+ *       String name = reader.nextName();
+ *       if (name.equals("id")) {
+ *         id = reader.nextLong();
+ *       } else if (name.equals("text")) {
+ *         text = reader.nextString();
+ *       } else if (name.equals("geo") && reader.peek() != JsonToken.NULL) {
+ *         geo = readDoublesArray(reader);
+ *       } else if (name.equals("user")) {
+ *         user = readUser(reader);
+ *       } else {
+ *         reader.skipValue();
+ *       }
+ *     }
+ *     reader.endObject();
+ *     return new Message(id, text, user, geo);
+ *   }
+ *
+ *   public List<Double> readDoublesArray(JsonReader reader) throws IOException {
+ *     List<Double> doubles = new ArrayList<Double>();
+ *
+ *     reader.beginArray();
+ *     while (reader.hasNext()) {
+ *       doubles.add(reader.nextDouble());
+ *     }
+ *     reader.endArray();
+ *     return doubles;
+ *   }
+ *
+ *   public User readUser(JsonReader reader) throws IOException {
+ *     String username = null;
+ *     int followersCount = -1;
+ *
+ *     reader.beginObject();
+ *     while (reader.hasNext()) {
+ *       String name = reader.nextName();
+ *       if (name.equals("name")) {
+ *         username = reader.nextString();
+ *       } else if (name.equals("followers_count")) {
+ *         followersCount = reader.nextInt();
+ *       } else {
+ *         reader.skipValue();
+ *       }
+ *     }
+ *     reader.endObject();
+ *     return new User(username, followersCount);
+ *   }}</pre>
+ *
+ * <h3>Number Handling</h3>
+ * This reader permits numeric values to be read as strings and string values to
+ * be read as numbers. For example, both elements of the JSON array {@code
+ * [1, "1"]} may be read using either {@link #nextInt} or {@link #nextString}.
+ * This behavior is intended to prevent lossy numeric conversions: double is
+ * JavaScript's only numeric type and very large values like {@code
+ * 9007199254740993} cannot be represented exactly on that platform. To minimize
+ * precision loss, extremely large values should be written and read as strings
+ * in JSON.
+ *
+ * <a name="nonexecuteprefix"></a><h3>Non-Execute Prefix</h3>
+ * Web servers that serve private data using JSON may be vulnerable to <a
+ * href="http://en.wikipedia.org/wiki/JSON#Cross-site_request_forgery">Cross-site
+ * request forgery</a> attacks. In such an attack, a malicious site gains access
+ * to a private JSON file by executing it with an HTML {@code <script>} tag.
+ *
+ * <p>Prefixing JSON files with <code>")]}'\n"</code> makes them non-executable
+ * by {@code <script>} tags, disarming the attack. Since the prefix is malformed
+ * JSON, strict parsing fails when it is encountered. This class permits the
+ * non-execute prefix when {@link #setLenient(boolean) lenient parsing} is
+ * enabled.
+ *
+ * <p>Each {@code JsonReader} may be used to read a single JSON stream. Instances
+ * of this class are not thread safe.
+ *
+ * @author Jesse Wilson
+ * @since 1.6
+ */
+public class JsonReader implements Closeable {
+  /** The only non-execute prefix this parser permits */
+  private static final char[] NON_EXECUTE_PREFIX = ")]}'\n".toCharArray();
+  private static final long MIN_INCOMPLETE_INTEGER = Long.MIN_VALUE / 10;
+
+  private static final int PEEKED_NONE = 0;
+  private static final int PEEKED_BEGIN_OBJECT = 1;
+  private static final int PEEKED_END_OBJECT = 2;
+  private static final int PEEKED_BEGIN_ARRAY = 3;
+  private static final int PEEKED_END_ARRAY = 4;
+  private static final int PEEKED_TRUE = 5;
+  private static final int PEEKED_FALSE = 6;
+  private static final int PEEKED_NULL = 7;
+  private static final int PEEKED_SINGLE_QUOTED = 8;
+  private static final int PEEKED_DOUBLE_QUOTED = 9;
+  private static final int PEEKED_UNQUOTED = 10;
+  /** When this is returned, the string value is stored in peekedString. */
+  private static final int PEEKED_BUFFERED = 11;
+  private static final int PEEKED_SINGLE_QUOTED_NAME = 12;
+  private static final int PEEKED_DOUBLE_QUOTED_NAME = 13;
+  private static final int PEEKED_UNQUOTED_NAME = 14;
+  /** When this is returned, the integer value is stored in peekedLong. */
+  private static final int PEEKED_LONG = 15;
+  private static final int PEEKED_NUMBER = 16;
+  private static final int PEEKED_EOF = 17;
+
+  /* State machine when parsing numbers */
+  private static final int NUMBER_CHAR_NONE = 0;
+  private static final int NUMBER_CHAR_SIGN = 1;
+  private static final int NUMBER_CHAR_DIGIT = 2;
+  private static final int NUMBER_CHAR_DECIMAL = 3;
+  private static final int NUMBER_CHAR_FRACTION_DIGIT = 4;
+  private static final int NUMBER_CHAR_EXP_E = 5;
+  private static final int NUMBER_CHAR_EXP_SIGN = 6;
+  private static final int NUMBER_CHAR_EXP_DIGIT = 7;
+
+  /** The input JSON. */
+  private final Reader in;
+
+  /** True to accept non-spec compliant JSON */
+  private boolean lenient = false;
+
+  /**
+   * Use a manual buffer to easily read and unread upcoming characters, and
+   * also so we can create strings without an intermediate StringBuilder.
+   * We decode literals directly out of this buffer, so it must be at least as
+   * long as the longest token that can be reported as a number.
+   */
+  private final char[] buffer = new char[1024];
+  private int pos = 0;
+  private int limit = 0;
+
+  private int lineNumber = 0;
+  private int lineStart = 0;
+
+  int peeked = PEEKED_NONE;
+
+  /**
+   * A peeked value that was composed entirely of digits with an optional
+   * leading dash. Positive values may not have a leading 0.
+   */
+  private long peekedLong;
+
+  /**
+   * The number of characters in a peeked number literal. Increment 'pos' by
+   * this after reading a number.
+   */
+  private int peekedNumberLength;
+
+  /**
+   * A peeked string that should be parsed on the next double, long or string.
+   * This is populated before a numeric value is parsed and used if that parsing
+   * fails.
+   */
+  private String peekedString;
+
+  /*
+   * The nesting stack. Using a manual array rather than an ArrayList saves 20%.
+   */
+  private int[] stack = new int[32];
+  private int stackSize = 0;
+  {
+    stack[stackSize++] = JsonScope.EMPTY_DOCUMENT;
+  }
+
+  /*
+   * The path members. It corresponds directly to stack: At indices where the
+   * stack contains an object (EMPTY_OBJECT, DANGLING_NAME or NONEMPTY_OBJECT),
+   * pathNames contains the name at this scope. Where it contains an array
+   * (EMPTY_ARRAY, NONEMPTY_ARRAY) pathIndices contains the current index in
+   * that array. Otherwise the value is undefined, and we take advantage of that
+   * by incrementing pathIndices when doing so isn't useful.
+   */
+  private String[] pathNames = new String[32];
+  private int[] pathIndices = new int[32];
+
+  /**
+   * Creates a new instance that reads a JSON-encoded stream from {@code in}.
+   */
+  public JsonReader(Reader in) {
+    if (in == null) {
+      throw new NullPointerException("in == null");
+    }
+    this.in = in;
+  }
+
+  /**
+   * Configure this parser to be liberal in what it accepts. By default,
+   * this parser is strict and only accepts JSON as specified by <a
+   * href="http://www.ietf.org/rfc/rfc4627.txt">RFC 4627</a>. Setting the
+   * parser to lenient causes it to ignore the following syntax errors:
+   *
+   * <ul>
+   *   <li>Streams that start with the <a href="#nonexecuteprefix">non-execute
+   *       prefix</a>, <code>")]}'\n"</code>.
+   *   <li>Streams that include multiple top-level values. With strict parsing,
+   *       each stream must contain exactly one top-level value.
+   *   <li>Top-level values of any type. With strict parsing, the top-level
+   *       value must be an object or an array.
+   *   <li>Numbers may be {@link Double#isNaN() NaNs} or {@link
+   *       Double#isInfinite() infinities}.
+   *   <li>End of line comments starting with {@code //} or {@code #} and
+   *       ending with a newline character.
+   *   <li>C-style comments starting with {@code /*} and ending with
+   *       {@code *}{@code /}. Such comments may not be nested.
+   *   <li>Names that are unquoted or {@code 'single quoted'}.
+   *   <li>Strings that are unquoted or {@code 'single quoted'}.
+   *   <li>Array elements separated by {@code ;} instead of {@code ,}.
+   *   <li>Unnecessary array separators. These are interpreted as if null
+   *       was the omitted value.
+   *   <li>Names and values separated by {@code =} or {@code =>} instead of
+   *       {@code :}.
+   *   <li>Name/value pairs separated by {@code ;} instead of {@code ,}.
+   * </ul>
+   */
+  public final void setLenient(boolean lenient) {
+    this.lenient = lenient;
+  }
+
+  /**
+   * Returns true if this parser is liberal in what it accepts.
+   */
+  public final boolean isLenient() {
+    return lenient;
+  }
+
+  /**
+   * Consumes the next token from the JSON stream and asserts that it is the
+   * beginning of a new array.
+   */
+  public void beginArray() throws IOException {
+    int p = peeked;
+    if (p == PEEKED_NONE) {
+      p = doPeek();
+    }
+    if (p == PEEKED_BEGIN_ARRAY) {
+      push(JsonScope.EMPTY_ARRAY);
+      pathIndices[stackSize - 1] = 0;
+      peeked = PEEKED_NONE;
+    } else {
+      throw new IllegalStateException("Expected BEGIN_ARRAY but was " + peek() + locationString());
+    }
+  }
+
+  /**
+   * Consumes the next token from the JSON stream and asserts that it is the
+   * end of the current array.
+   */
+  public void endArray() throws IOException {
+    int p = peeked;
+    if (p == PEEKED_NONE) {
+      p = doPeek();
+    }
+    if (p == PEEKED_END_ARRAY) {
+      stackSize--;
+      pathIndices[stackSize - 1]++;
+      peeked = PEEKED_NONE;
+    } else {
+      throw new IllegalStateException("Expected END_ARRAY but was " + peek() + locationString());
+    }
+  }
+
+  /**
+   * Consumes the next token from the JSON stream and asserts that it is the
+   * beginning of a new object.
+   */
+  public void beginObject() throws IOException {
+    int p = peeked;
+    if (p == PEEKED_NONE) {
+      p = doPeek();
+    }
+    if (p == PEEKED_BEGIN_OBJECT) {
+      push(JsonScope.EMPTY_OBJECT);
+      peeked = PEEKED_NONE;
+    } else {
+      throw new IllegalStateException("Expected BEGIN_OBJECT but was " + peek() + locationString());
+    }
+  }
+
+  /**
+   * Consumes the next token from the JSON stream and asserts that it is the
+   * end of the current object.
+   */
+  public void endObject() throws IOException {
+    int p = peeked;
+    if (p == PEEKED_NONE) {
+      p = doPeek();
+    }
+    if (p == PEEKED_END_OBJECT) {
+      stackSize--;
+      pathNames[stackSize] = null; // Free the last path name so that it can be garbage collected!
+      pathIndices[stackSize - 1]++;
+      peeked = PEEKED_NONE;
+    } else {
+      throw new IllegalStateException("Expected END_OBJECT but was " + peek() + locationString());
+    }
+  }
+
+  /**
+   * Returns true if the current array or object has another element.
+   */
+  public boolean hasNext() throws IOException {
+    int p = peeked;
+    if (p == PEEKED_NONE) {
+      p = doPeek();
+    }
+    return p != PEEKED_END_OBJECT && p != PEEKED_END_ARRAY;
+  }
+
+  /**
+   * Returns the type of the next token without consuming it.
+   */
+  public JsonToken peek() throws IOException {
+    int p = peeked;
+    if (p == PEEKED_NONE) {
+      p = doPeek();
+    }
+
+    switch (p) {
+    case PEEKED_BEGIN_OBJECT:
+      return JsonToken.BEGIN_OBJECT;
+    case PEEKED_END_OBJECT:
+      return JsonToken.END_OBJECT;
+    case PEEKED_BEGIN_ARRAY:
+      return JsonToken.BEGIN_ARRAY;
+    case PEEKED_END_ARRAY:
+      return JsonToken.END_ARRAY;
+    case PEEKED_SINGLE_QUOTED_NAME:
+    case PEEKED_DOUBLE_QUOTED_NAME:
+    case PEEKED_UNQUOTED_NAME:
+      return JsonToken.NAME;
+    case PEEKED_TRUE:
+    case PEEKED_FALSE:
+      return JsonToken.BOOLEAN;
+    case PEEKED_NULL:
+      return JsonToken.NULL;
+    case PEEKED_SINGLE_QUOTED:
+    case PEEKED_DOUBLE_QUOTED:
+    case PEEKED_UNQUOTED:
+    case PEEKED_BUFFERED:
+      return JsonToken.STRING;
+    case PEEKED_LONG:
+    case PEEKED_NUMBER:
+      return JsonToken.NUMBER;
+    case PEEKED_EOF:
+      return JsonToken.END_DOCUMENT;
+    default:
+      throw new AssertionError();
+    }
+  }
+
+  int doPeek() throws IOException {
+    int peekStack = stack[stackSize - 1];
+    if (peekStack == JsonScope.EMPTY_ARRAY) {
+      stack[stackSize - 1] = JsonScope.NONEMPTY_ARRAY;
+    } else if (peekStack == JsonScope.NONEMPTY_ARRAY) {
+      // Look for a comma before the next element.
+      int c = nextNonWhitespace(true);
+      switch (c) {
+      case ']':
+        return peeked = PEEKED_END_ARRAY;
+      case ';':
+        checkLenient(); // fall-through
+      case ',':
+        break;
+      default:
+        throw syntaxError("Unterminated array");
+      }
+    } else if (peekStack == JsonScope.EMPTY_OBJECT || peekStack == JsonScope.NONEMPTY_OBJECT) {
+      stack[stackSize - 1] = JsonScope.DANGLING_NAME;
+      // Look for a comma before the next element.
+      if (peekStack == JsonScope.NONEMPTY_OBJECT) {
+        int c = nextNonWhitespace(true);
+        switch (c) {
+        case '}':
+          return peeked = PEEKED_END_OBJECT;
+        case ';':
+          checkLenient(); // fall-through
+        case ',':
+          break;
+        default:
+          throw syntaxError("Unterminated object");
+        }
+      }
+      int c = nextNonWhitespace(true);
+      switch (c) {
+      case '"':
+        return peeked = PEEKED_DOUBLE_QUOTED_NAME;
+      case '\'':
+        checkLenient();
+        return peeked = PEEKED_SINGLE_QUOTED_NAME;
+      case '}':
+        if (peekStack != JsonScope.NONEMPTY_OBJECT) {
+          return peeked = PEEKED_END_OBJECT;
+        } else {
+          throw syntaxError("Expected name");
+        }
+      default:
+        checkLenient();
+        pos--; // Don't consume the first character in an unquoted string.
+        if (isLiteral((char) c)) {
+          return peeked = PEEKED_UNQUOTED_NAME;
+        } else {
+          throw syntaxError("Expected name");
+        }
+      }
+    } else if (peekStack == JsonScope.DANGLING_NAME) {
+      stack[stackSize - 1] = JsonScope.NONEMPTY_OBJECT;
+      // Look for a colon before the value.
+      int c = nextNonWhitespace(true);
+      switch (c) {
+      case ':':
+        break;
+      case '=':
+        checkLenient();
+        if ((pos < limit || fillBuffer(1)) && buffer[pos] == '>') {
+          pos++;
+        }
+        break;
+      default:
+        throw syntaxError("Expected ':'");
+      }
+    } else if (peekStack == JsonScope.EMPTY_DOCUMENT) {
+      if (lenient) {
+        consumeNonExecutePrefix();
+      }
+      stack[stackSize - 1] = JsonScope.NONEMPTY_DOCUMENT;
+    } else if (peekStack == JsonScope.NONEMPTY_DOCUMENT) {
+      int c = nextNonWhitespace(false);
+      if (c == -1) {
+        return peeked = PEEKED_EOF;
+      } else {
+        checkLenient();
+        pos--;
+      }
+    } else if (peekStack == JsonScope.CLOSED) {
+      throw new IllegalStateException("JsonReader is closed");
+    }
+
+    int c = nextNonWhitespace(true);
+    switch (c) {
+    case ']':
+      if (peekStack == JsonScope.EMPTY_ARRAY) {
+        return peeked = PEEKED_END_ARRAY;
+      }
+      // fall-through to handle ",]"
+    case ';':
+    case ',':
+      // In lenient mode, a 0-length literal in an array means 'null'.
+      if (peekStack == JsonScope.EMPTY_ARRAY || peekStack == JsonScope.NONEMPTY_ARRAY) {
+        checkLenient();
+        pos--;
+        return peeked = PEEKED_NULL;
+      } else {
+        throw syntaxError("Unexpected value");
+      }
+    case '\'':
+      checkLenient();
+      return peeked = PEEKED_SINGLE_QUOTED;
+    case '"':
+      return peeked = PEEKED_DOUBLE_QUOTED;
+    case '[':
+      return peeked = PEEKED_BEGIN_ARRAY;
+    case '{':
+      return peeked = PEEKED_BEGIN_OBJECT;
+    default:
+      pos--; // Don't consume the first character in a literal value.
+    }
+
+    int result = peekKeyword();
+    if (result != PEEKED_NONE) {
+      return result;
+    }
+
+    result = peekNumber();
+    if (result != PEEKED_NONE) {
+      return result;
+    }
+
+    if (!isLiteral(buffer[pos])) {
+      throw syntaxError("Expected value");
+    }
+
+    checkLenient();
+    return peeked = PEEKED_UNQUOTED;
+  }
+
+  private int peekKeyword() throws IOException {
+    // Figure out which keyword we're matching against by its first character.
+    char c = buffer[pos];
+    String keyword;
+    String keywordUpper;
+    int peeking;
+    if (c == 't' || c == 'T') {
+      keyword = "true";
+      keywordUpper = "TRUE";
+      peeking = PEEKED_TRUE;
+    } else if (c == 'f' || c == 'F') {
+      keyword = "false";
+      keywordUpper = "FALSE";
+      peeking = PEEKED_FALSE;
+    } else if (c == 'n' || c == 'N') {
+      keyword = "null";
+      keywordUpper = "NULL";
+      peeking = PEEKED_NULL;
+    } else {
+      return PEEKED_NONE;
+    }
+
+    // Confirm that chars [1..length) match the keyword.
+    int length = keyword.length();
+    for (int i = 1; i < length; i++) {
+      if (pos + i >= limit && !fillBuffer(i + 1)) {
+        return PEEKED_NONE;
+      }
+      c = buffer[pos + i];
+      if (c != keyword.charAt(i) && c != keywordUpper.charAt(i)) {
+        return PEEKED_NONE;
+      }
+    }
+
+    if ((pos + length < limit || fillBuffer(length + 1))
+        && isLiteral(buffer[pos + length])) {
+      return PEEKED_NONE; // Don't match trues, falsey or nullsoft!
+    }
+
+    // We've found the keyword followed either by EOF or by a non-literal character.
+    pos += length;
+    return peeked = peeking;
+  }
+
+  private int peekNumber() throws IOException {
+    // Like nextNonWhitespace, this uses locals 'p' and 'l' to save inner-loop field access.
+    char[] buffer = this.buffer;
+    int p = pos;
+    int l = limit;
+
+    long value = 0; // Negative to accommodate Long.MIN_VALUE more easily.
+    boolean negative = false;
+    boolean fitsInLong = true;
+    int last = NUMBER_CHAR_NONE;
+
+    int i = 0;
+
+    charactersOfNumber:
+    for (; true; i++) {
+      if (p + i == l) {
+        if (i == buffer.length) {
+          // Though this looks like a well-formed number, it's too long to continue reading. Give up
+          // and let the application handle this as an unquoted literal.
+          return PEEKED_NONE;
+        }
+        if (!fillBuffer(i + 1)) {
+          break;
+        }
+        p = pos;
+        l = limit;
+      }
+
+      char c = buffer[p + i];
+      switch (c) {
+      case '-':
+        if (last == NUMBER_CHAR_NONE) {
+          negative = true;
+          last = NUMBER_CHAR_SIGN;
+          continue;
+        } else if (last == NUMBER_CHAR_EXP_E) {
+          last = NUMBER_CHAR_EXP_SIGN;
+          continue;
+        }
+        return PEEKED_NONE;
+
+      case '+':
+        if (last == NUMBER_CHAR_EXP_E) {
+          last = NUMBER_CHAR_EXP_SIGN;
+          continue;
+        }
+        return PEEKED_NONE;
+
+      case 'e':
+      case 'E':
+        if (last == NUMBER_CHAR_DIGIT || last == NUMBER_CHAR_FRACTION_DIGIT) {
+          last = NUMBER_CHAR_EXP_E;
+          continue;
+        }
+        return PEEKED_NONE;
+
+      case '.':
+        if (last == NUMBER_CHAR_DIGIT) {
+          last = NUMBER_CHAR_DECIMAL;
+          continue;
+        }
+        return PEEKED_NONE;
+
+      default:
+        if (c < '0' || c > '9') {
+          if (!isLiteral(c)) {
+            break charactersOfNumber;
+          }
+          return PEEKED_NONE;
+        }
+        if (last == NUMBER_CHAR_SIGN || last == NUMBER_CHAR_NONE) {
+          value = -(c - '0');
+          last = NUMBER_CHAR_DIGIT;
+        } else if (last == NUMBER_CHAR_DIGIT) {
+          if (value == 0) {
+            return PEEKED_NONE; // Leading '0' prefix is not allowed (since it could be octal).
+          }
+          long newValue = value * 10 - (c - '0');
+          fitsInLong &= value > MIN_INCOMPLETE_INTEGER
+              || (value == MIN_INCOMPLETE_INTEGER && newValue < value);
+          value = newValue;
+        } else if (last == NUMBER_CHAR_DECIMAL) {
+          last = NUMBER_CHAR_FRACTION_DIGIT;
+        } else if (last == NUMBER_CHAR_EXP_E || last == NUMBER_CHAR_EXP_SIGN) {
+          last = NUMBER_CHAR_EXP_DIGIT;
+        }
+      }
+    }
+
+    // We've read a complete number. Decide if it's a PEEKED_LONG or a PEEKED_NUMBER.
+    if (last == NUMBER_CHAR_DIGIT && fitsInLong && (value != Long.MIN_VALUE || negative) && (value!=0 || false==negative)) {
+      peekedLong = negative ? value : -value;
+      pos += i;
+      return peeked = PEEKED_LONG;
+    } else if (last == NUMBER_CHAR_DIGIT || last == NUMBER_CHAR_FRACTION_DIGIT
+        || last == NUMBER_CHAR_EXP_DIGIT) {
+      peekedNumberLength = i;
+      return peeked = PEEKED_NUMBER;
+    } else {
+      return PEEKED_NONE;
+    }
+  }
+
+  private boolean isLiteral(char c) throws IOException {
+    switch (c) {
+    case '/':
+    case '\\':
+    case ';':
+    case '#':
+    case '=':
+      checkLenient(); // fall-through
+    case '{':
+    case '}':
+    case '[':
+    case ']':
+    case ':':
+    case ',':
+    case ' ':
+    case '\t':
+    case '\f':
+    case '\r':
+    case '\n':
+      return false;
+    default:
+      return true;
+    }
+  }
+
+  /**
+   * Returns the next token, a {@link com.google.gson.stream.JsonToken#NAME property name}, and
+   * consumes it.
+   *
+   * @throws java.io.IOException if the next token in the stream is not a property
+   *     name.
+   */
+  public String nextName() throws IOException {
+    int p = peeked;
+    if (p == PEEKED_NONE) {
+      p = doPeek();
+    }
+    String result;
+    if (p == PEEKED_UNQUOTED_NAME) {
+      result = nextUnquotedValue();
+    } else if (p == PEEKED_SINGLE_QUOTED_NAME) {
+      result = nextQuotedValue('\'');
+    } else if (p == PEEKED_DOUBLE_QUOTED_NAME) {
+      result = nextQuotedValue('"');
+    } else {
+      throw new IllegalStateException("Expected a name but was " + peek() + locationString());
+    }
+    peeked = PEEKED_NONE;
+    pathNames[stackSize - 1] = result;
+    return result;
+  }
+
+  /**
+   * Returns the {@link com.google.gson.stream.JsonToken#STRING string} value of the next token,
+   * consuming it. If the next token is a number, this method will return its
+   * string form.
+   *
+   * @throws IllegalStateException if the next token is not a string or if
+   *     this reader is closed.
+   */
+  public String nextString() throws IOException {
+    int p = peeked;
+    if (p == PEEKED_NONE) {
+      p = doPeek();
+    }
+    String result;
+    if (p == PEEKED_UNQUOTED) {
+      result = nextUnquotedValue();
+    } else if (p == PEEKED_SINGLE_QUOTED) {
+      result = nextQuotedValue('\'');
+    } else if (p == PEEKED_DOUBLE_QUOTED) {
+      result = nextQuotedValue('"');
+    } else if (p == PEEKED_BUFFERED) {
+      result = peekedString;
+      peekedString = null;
+    } else if (p == PEEKED_LONG) {
+      result = Long.toString(peekedLong);
+    } else if (p == PEEKED_NUMBER) {
+      result = new String(buffer, pos, peekedNumberLength);
+      pos += peekedNumberLength;
+    } else {
+      throw new IllegalStateException("Expected a string but was " + peek() + locationString());
+    }
+    peeked = PEEKED_NONE;
+    pathIndices[stackSize - 1]++;
+    return result;
+  }
+
+  /**
+   * Returns the {@link com.google.gson.stream.JsonToken#BOOLEAN boolean} value of the next token,
+   * consuming it.
+   *
+   * @throws IllegalStateException if the next token is not a boolean or if
+   *     this reader is closed.
+   */
+  public boolean nextBoolean() throws IOException {
+    int p = peeked;
+    if (p == PEEKED_NONE) {
+      p = doPeek();
+    }
+    if (p == PEEKED_TRUE) {
+      peeked = PEEKED_NONE;
+      pathIndices[stackSize - 1]++;
+      return true;
+    } else if (p == PEEKED_FALSE) {
+      peeked = PEEKED_NONE;
+      pathIndices[stackSize - 1]++;
+      return false;
+    }
+    throw new IllegalStateException("Expected a boolean but was " + peek() + locationString());
+  }
+
+  /**
+   * Consumes the next token from the JSON stream and asserts that it is a
+   * literal null.
+   *
+   * @throws IllegalStateException if the next token is not null or if this
+   *     reader is closed.
+   */
+  public void nextNull() throws IOException {
+    int p = peeked;
+    if (p == PEEKED_NONE) {
+      p = doPeek();
+    }
+    if (p == PEEKED_NULL) {
+      peeked = PEEKED_NONE;
+      pathIndices[stackSize - 1]++;
+    } else {
+      throw new IllegalStateException("Expected null but was " + peek() + locationString());
+    }
+  }
+
+  /**
+   * Returns the {@link com.google.gson.stream.JsonToken#NUMBER double} value of the next token,
+   * consuming it. If the next token is a string, this method will attempt to
+   * parse it as a double using {@link Double#parseDouble(String)}.
+   *
+   * @throws IllegalStateException if the next token is not a literal value.
+   * @throws NumberFormatException if the next literal value cannot be parsed
+   *     as a double, or is non-finite.
+   */
+  public double nextDouble() throws IOException {
+    int p = peeked;
+    if (p == PEEKED_NONE) {
+      p = doPeek();
+    }
+
+    if (p == PEEKED_LONG) {
+      peeked = PEEKED_NONE;
+      pathIndices[stackSize - 1]++;
+      return (double) peekedLong;
+    }
+
+    if (p == PEEKED_NUMBER) {
+      peekedString = new String(buffer, pos, peekedNumberLength);
+      pos += peekedNumberLength;
+    } else if (p == PEEKED_SINGLE_QUOTED || p == PEEKED_DOUBLE_QUOTED) {
+      peekedString = nextQuotedValue(p == PEEKED_SINGLE_QUOTED ? '\'' : '"');
+    } else if (p == PEEKED_UNQUOTED) {
+      peekedString = nextUnquotedValue();
+    } else if (p != PEEKED_BUFFERED) {
+      throw new IllegalStateException("Expected a double but was " + peek() + locationString());
+    }
+
+    peeked = PEEKED_BUFFERED;
+    double result = Double.parseDouble(peekedString); // don't catch this NumberFormatException.
+    if (!lenient && (Double.isNaN(result) || Double.isInfinite(result))) {
+      throw new MalformedJsonException(
+          "JSON forbids NaN and infinities: " + result + locationString());
+    }
+    peekedString = null;
+    peeked = PEEKED_NONE;
+    pathIndices[stackSize - 1]++;
+    return result;
+  }
+
+  /**
+   * Returns the {@link com.google.gson.stream.JsonToken#NUMBER long} value of the next token,
+   * consuming it. If the next token is a string, this method will attempt to
+   * parse it as a long. If the next token's numeric value cannot be exactly
+   * represented by a Java {@code long}, this method throws.
+   *
+   * @throws IllegalStateException if the next token is not a literal value.
+   * @throws NumberFormatException if the next literal value cannot be parsed
+   *     as a number, or exactly represented as a long.
+   */
+  public long nextLong() throws IOException {
+    int p = peeked;
+    if (p == PEEKED_NONE) {
+      p = doPeek();
+    }
+
+    if (p == PEEKED_LONG) {
+      peeked = PEEKED_NONE;
+      pathIndices[stackSize - 1]++;
+      return peekedLong;
+    }
+
+    if (p == PEEKED_NUMBER) {
+      peekedString = new String(buffer, pos, peekedNumberLength);
+      pos += peekedNumberLength;
+    } else if (p == PEEKED_SINGLE_QUOTED || p == PEEKED_DOUBLE_QUOTED || p == PEEKED_UNQUOTED) {
+      if (p == PEEKED_UNQUOTED) {
+        peekedString = nextUnquotedValue();
+      } else {
+        peekedString = nextQuotedValue(p == PEEKED_SINGLE_QUOTED ? '\'' : '"');
+      }
+      try {
+        long result = Long.parseLong(peekedString);
+        peeked = PEEKED_NONE;
+        pathIndices[stackSize - 1]++;
+        return result;
+      } catch (NumberFormatException ignored) {
+        // Fall back to parse as a double below.
+      }
+    } else {
+      throw new IllegalStateException("Expected a long but was " + peek() + locationString());
+    }
+
+    peeked = PEEKED_BUFFERED;
+    double asDouble = Double.parseDouble(peekedString); // don't catch this NumberFormatException.
+    long result = (long) asDouble;
+    if (result != asDouble) { // Make sure no precision was lost casting to 'long'.
+      throw new NumberFormatException("Expected a long but was " + peekedString + locationString());
+    }
+    peekedString = null;
+    peeked = PEEKED_NONE;
+    pathIndices[stackSize - 1]++;
+    return result;
+  }
+
+  /**
+   * Returns the string up to but not including {@code quote}, unescaping any
+   * character escape sequences encountered along the way. The opening quote
+   * should have already been read. This consumes the closing quote, but does
+   * not include it in the returned string.
+   *
+   * @param quote either ' or ".
+   * @throws NumberFormatException if any unicode escape sequences are
+   *     malformed.
+   */
+  private String nextQuotedValue(char quote) throws IOException {
+    // Like nextNonWhitespace, this uses locals 'p' and 'l' to save inner-loop field access.
+    char[] buffer = this.buffer;
+    StringBuilder builder = null;
+    while (true) {
+      int p = pos;
+      int l = limit;
+      /* the index of the first character not yet appended to the builder. */
+      int start = p;
+      while (p < l) {
+        int c = buffer[p++];
+
+        if (c == quote) {
+          pos = p;
+          int len = p - start - 1;
+          if (builder == null) {
+            return new String(buffer, start, len);
+          } else {
+            builder.append(buffer, start, len);
+            return builder.toString();
+          }
+        } else if (c == '\\') {
+          pos = p;
+          int len = p - start - 1;
+          if (builder == null) {
+            int estimatedLength = (len + 1) * 2;
+            builder = new StringBuilder(Math.max(estimatedLength, 16));
+          }
+          builder.append(buffer, start, len);
+          builder.append(readEscapeCharacter());
+          p = pos;
+          l = limit;
+          start = p;
+        } else if (c == '\n') {
+          lineNumber++;
+          lineStart = p;
+        }
+      }
+
+      if (builder == null) {
+        int estimatedLength = (p - start) * 2;
+        builder = new StringBuilder(Math.max(estimatedLength, 16));
+      }
+      builder.append(buffer, start, p - start);
+      pos = p;
+      if (!fillBuffer(1)) {
+        throw syntaxError("Unterminated string");
+      }
+    }
+  }
+
+  /**
+   * Returns an unquoted value as a string.
+   */
+  @SuppressWarnings("fallthrough")
+  private String nextUnquotedValue() throws IOException {
+    StringBuilder builder = null;
+    int i = 0;
+
+    findNonLiteralCharacter:
+    while (true) {
+      for (; pos + i < limit; i++) {
+        switch (buffer[pos + i]) {
+        case '/':
+        case '\\':
+        case ';':
+        case '#':
+        case '=':
+          checkLenient(); // fall-through
+        case '{':
+        case '}':
+        case '[':
+        case ']':
+        case ':':
+        case ',':
+        case ' ':
+        case '\t':
+        case '\f':
+        case '\r':
+        case '\n':
+          break findNonLiteralCharacter;
+        }
+      }
+
+      // Attempt to load the entire literal into the buffer at once.
+      if (i < buffer.length) {
+        if (fillBuffer(i + 1)) {
+          continue;
+        } else {
+          break;
+        }
+      }
+
+      // use a StringBuilder when the value is too long. This is too long to be a number!
+      if (builder == null) {
+        builder = new StringBuilder(Math.max(i,16));
+      }
+      builder.append(buffer, pos, i);
+      pos += i;
+      i = 0;
+      if (!fillBuffer(1)) {
+        break;
+      }
+    }
+   
+    String result = (null == builder) ? new String(buffer, pos, i) : builder.append(buffer, pos, i).toString();
+    pos += i;
+    return result;
+  }
+
+  private void skipQuotedValue(char quote) throws IOException {
+    // Like nextNonWhitespace, this uses locals 'p' and 'l' to save inner-loop field access.
+    char[] buffer = this.buffer;
+    do {
+      int p = pos;
+      int l = limit;
+      /* the index of the first character not yet appended to the builder. */
+      while (p < l) {
+        int c = buffer[p++];
+        if (c == quote) {
+          pos = p;
+          return;
+        } else if (c == '\\') {
+          pos = p;
+          readEscapeCharacter();
+          p = pos;
+          l = limit;
+        } else if (c == '\n') {
+          lineNumber++;
+          lineStart = p;
+        }
+      }
+      pos = p;
+    } while (fillBuffer(1));
+    throw syntaxError("Unterminated string");
+  }
+
+  private void skipUnquotedValue() throws IOException {
+    do {
+      int i = 0;
+      for (; pos + i < limit; i++) {
+        switch (buffer[pos + i]) {
+        case '/':
+        case '\\':
+        case ';':
+        case '#':
+        case '=':
+          checkLenient(); // fall-through
+        case '{':
+        case '}':
+        case '[':
+        case ']':
+        case ':':
+        case ',':
+        case ' ':
+        case '\t':
+        case '\f':
+        case '\r':
+        case '\n':
+          pos += i;
+          return;
+        }
+      }
+      pos += i;
+    } while (fillBuffer(1));
+  }
+
+  /**
+   * Returns the {@link com.google.gson.stream.JsonToken#NUMBER int} value of the next token,
+   * consuming it. If the next token is a string, this method will attempt to
+   * parse it as an int. If the next token's numeric value cannot be exactly
+   * represented by a Java {@code int}, this method throws.
+   *
+   * @throws IllegalStateException if the next token is not a literal value.
+   * @throws NumberFormatException if the next literal value cannot be parsed
+   *     as a number, or exactly represented as an int.
+   */
+  public int nextInt() throws IOException {
+    int p = peeked;
+    if (p == PEEKED_NONE) {
+      p = doPeek();
+    }
+
+    int result;
+    if (p == PEEKED_LONG) {
+      result = (int) peekedLong;
+      if (peekedLong != result) { // Make sure no precision was lost casting to 'int'.
+        throw new NumberFormatException("Expected an int but was " + peekedLong + locationString());
+      }
+      peeked = PEEKED_NONE;
+      pathIndices[stackSize - 1]++;
+      return result;
+    }
+
+    if (p == PEEKED_NUMBER) {
+      peekedString = new String(buffer, pos, peekedNumberLength);
+      pos += peekedNumberLength;
+    } else if (p == PEEKED_SINGLE_QUOTED || p == PEEKED_DOUBLE_QUOTED || p == PEEKED_UNQUOTED) {
+      if (p == PEEKED_UNQUOTED) {
+        peekedString = nextUnquotedValue();
+      } else {
+        peekedString = nextQuotedValue(p == PEEKED_SINGLE_QUOTED ? '\'' : '"');
+      }
+      try {
+        result = Integer.parseInt(peekedString);
+        peeked = PEEKED_NONE;
+        pathIndices[stackSize - 1]++;
+        return result;
+      } catch (NumberFormatException ignored) {
+        // Fall back to parse as a double below.
+      }
+    } else {
+      throw new IllegalStateException("Expected an int but was " + peek() + locationString());
+    }
+
+    peeked = PEEKED_BUFFERED;
+    double asDouble = Double.parseDouble(peekedString); // don't catch this NumberFormatException.
+    result = (int) asDouble;
+    if (result != asDouble) { // Make sure no precision was lost casting to 'int'.
+      throw new NumberFormatException("Expected an int but was " + peekedString + locationString());
+    }
+    peekedString = null;
+    peeked = PEEKED_NONE;
+    pathIndices[stackSize - 1]++;
+    return result;
+  }
+
+  /**
+   * Closes this JSON reader and the underlying {@link java.io.Reader}.
+   */
+  public void close() throws IOException {
+    peeked = PEEKED_NONE;
+    stack[0] = JsonScope.CLOSED;
+    stackSize = 1;
+    in.close();
+  }
+
+  /**
+   * Skips the next value recursively. If it is an object or array, all nested
+   * elements are skipped. This method is intended for use when the JSON token
+   * stream contains unrecognized or unhandled values.
+   */
+  public void skipValue() throws IOException {
+    int count = 0;
+    do {
+      int p = peeked;
+      if (p == PEEKED_NONE) {
+        p = doPeek();
+      }
+
+      if (p == PEEKED_BEGIN_ARRAY) {
+        push(JsonScope.EMPTY_ARRAY);
+        count++;
+      } else if (p == PEEKED_BEGIN_OBJECT) {
+        push(JsonScope.EMPTY_OBJECT);
+        count++;
+      } else if (p == PEEKED_END_ARRAY) {
+        stackSize--;
+        count--;
+      } else if (p == PEEKED_END_OBJECT) {
+        stackSize--;
+        count--;
+      } else if (p == PEEKED_UNQUOTED_NAME || p == PEEKED_UNQUOTED) {
+        skipUnquotedValue();
+      } else if (p == PEEKED_SINGLE_QUOTED || p == PEEKED_SINGLE_QUOTED_NAME) {
+        skipQuotedValue('\'');
+      } else if (p == PEEKED_DOUBLE_QUOTED || p == PEEKED_DOUBLE_QUOTED_NAME) {
+        skipQuotedValue('"');
+      } else if (p == PEEKED_NUMBER) {
+        pos += peekedNumberLength;
+      }
+      peeked = PEEKED_NONE;
+    } while (count != 0);
+
+    pathIndices[stackSize - 1]++;
+    pathNames[stackSize - 1] = "null";
+  }
+
+  private void push(int newTop) {
+    if (stackSize == stack.length) {
+      int newLength = stackSize * 2;
+      stack = Arrays.copyOf(stack, newLength);
+      pathIndices = Arrays.copyOf(pathIndices, newLength);
+      pathNames = Arrays.copyOf(pathNames, newLength);
+    }
+    stack[stackSize++] = newTop;
+  }
+
+  /**
+   * Returns true once {@code limit - pos >= minimum}. If the data is
+   * exhausted before that many characters are available, this returns
+   * false.
+   */
+  private boolean fillBuffer(int minimum) throws IOException {
+    char[] buffer = this.buffer;
+    lineStart -= pos;
+    if (limit != pos) {
+      limit -= pos;
+      System.arraycopy(buffer, pos, buffer, 0, limit);
+    } else {
+      limit = 0;
+    }
+
+    pos = 0;
+    int total;
+    while ((total = in.read(buffer, limit, buffer.length - limit)) != -1) {
+      limit += total;
+
+      // if this is the first read, consume an optional byte order mark (BOM) if it exists
+      if (lineNumber == 0 && lineStart == 0 && limit > 0 && buffer[0] == '\ufeff') {
+        pos++;
+        lineStart++;
+        minimum++;
+      }
+
+      if (limit >= minimum) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  /**
+   * Returns the next character in the stream that is neither whitespace nor a
+   * part of a comment. When this returns, the returned character is always at
+   * {@code buffer[pos-1]}; this means the caller can always push back the
+   * returned character by decrementing {@code pos}.
+   */
+  private int nextNonWhitespace(boolean throwOnEof) throws IOException {
+    /*
+     * This code uses ugly local variables 'p' and 'l' representing the 'pos'
+     * and 'limit' fields respectively. Using locals rather than fields saves
+     * a few field reads for each whitespace character in a pretty-printed
+     * document, resulting in a 5% speedup. We need to flush 'p' to its field
+     * before any (potentially indirect) call to fillBuffer() and reread both
+     * 'p' and 'l' after any (potentially indirect) call to the same method.
+     */
+    char[] buffer = this.buffer;
+    int p = pos;
+    int l = limit;
+    while (true) {
+      if (p == l) {
+        pos = p;
+        if (!fillBuffer(1)) {
+          break;
+        }
+        p = pos;
+        l = limit;
+      }
+
+      int c = buffer[p++];
+      if (c == '\n') {
+        lineNumber++;
+        lineStart = p;
+        continue;
+      } else if (c == ' ' || c == '\r' || c == '\t') {
+        continue;
+      }
+
+      if (c == '/') {
+        pos = p;
+        if (p == l) {
+          pos--; // push back '/' so it's still in the buffer when this method returns
+          boolean charsLoaded = fillBuffer(2);
+          pos++; // consume the '/' again
+          if (!charsLoaded) {
+            return c;
+          }
+        }
+
+        checkLenient();
+        char peek = buffer[pos];
+        switch (peek) {
+        case '*':
+          // skip a /* c-style comment */
+          pos++;
+          if (!skipTo("*/")) {
+            throw syntaxError("Unterminated comment");
+          }
+          p = pos + 2;
+          l = limit;
+          continue;
+
+        case '/':
+          // skip a // end-of-line comment
+          pos++;
+          skipToEndOfLine();
+          p = pos;
+          l = limit;
+          continue;
+
+        default:
+          return c;
+        }
+      } else if (c == '#') {
+        pos = p;
+        /*
+         * Skip a # hash end-of-line comment. The JSON RFC doesn't
+         * specify this behaviour, but it's required to parse
+         * existing documents. See http://b/2571423.
+         */
+        checkLenient();
+        skipToEndOfLine();
+        p = pos;
+        l = limit;
+      } else {
+        pos = p;
+        return c;
+      }
+    }
+    if (throwOnEof) {
+      throw new EOFException("End of input" + locationString());
+    } else {
+      return -1;
+    }
+  }
+
+  private void checkLenient() throws IOException {
+    if (!lenient) {
+      throw syntaxError("Use JsonReader.setLenient(true) to accept malformed JSON");
+    }
+  }
+
+  /**
+   * Advances the position until after the next newline character. If the line
+   * is terminated by "\r\n", the '\n' must be consumed as whitespace by the
+   * caller.
+   */
+  private void skipToEndOfLine() throws IOException {
+    while (pos < limit || fillBuffer(1)) {
+      char c = buffer[pos++];
+      if (c == '\n') {
+        lineNumber++;
+        lineStart = pos;
+        break;
+      } else if (c == '\r') {
+        break;
+      }
+    }
+  }
+
+  /**
+   * @param toFind a string to search for. Must not contain a newline.
+   */
+  private boolean skipTo(String toFind) throws IOException {
+    int length = toFind.length();
+    outer:
+    for (; pos + length <= limit || fillBuffer(length); pos++) {
+      if (buffer[pos] == '\n') {
+        lineNumber++;
+        lineStart = pos + 1;
+        continue;
+      }
+      for (int c = 0; c < length; c++) {
+        if (buffer[pos + c] != toFind.charAt(c)) {
+          continue outer;
+        }
+      }
+      return true;
+    }
+    return false;
+  }
+
+  @Override public String toString() {
+    return getClass().getSimpleName() + locationString();
+  }
+
+  String locationString() {
+    int line = lineNumber + 1;
+    int column = pos - lineStart + 1;
+    return " at line " + line + " column " + column + " path " + getPath();
+  }
+
+  /**
+   * Returns a <a href="http://goessner.net/articles/JsonPath/">JsonPath</a> to
+   * the current location in the JSON value.
+   */
+  public String getPath() {
+    StringBuilder result = new StringBuilder().append('$');
+    for (int i = 0, size = stackSize; i < size; i++) {
+      switch (stack[i]) {
+        case JsonScope.EMPTY_ARRAY:
+        case JsonScope.NONEMPTY_ARRAY:
+          result.append('[').append(pathIndices[i]).append(']');
+          break;
+
+        case JsonScope.EMPTY_OBJECT:
+        case JsonScope.DANGLING_NAME:
+        case JsonScope.NONEMPTY_OBJECT:
+          result.append('.');
+          if (pathNames[i] != null) {
+            result.append(pathNames[i]);
+          }
+          break;
+
+        case JsonScope.NONEMPTY_DOCUMENT:
+        case JsonScope.EMPTY_DOCUMENT:
+        case JsonScope.CLOSED:
+          break;
+      }
+    }
+    return result.toString();
+  }
+
+  /**
+   * Unescapes the character identified by the character or characters that
+   * immediately follow a backslash. The backslash '\' should have already
+   * been read. This supports both unicode escapes "u000A" and two-character
+   * escapes "\n".
+   *
+   * @throws NumberFormatException if any unicode escape sequences are
+   *     malformed.
+   */
+  private char readEscapeCharacter() throws IOException {
+    if (pos == limit && !fillBuffer(1)) {
+      throw syntaxError("Unterminated escape sequence");
+    }
+
+    char escaped = buffer[pos++];
+    switch (escaped) {
+    case 'u':
+      if (pos + 4 > limit && !fillBuffer(4)) {
+        throw syntaxError("Unterminated escape sequence");
+      }
+      // Equivalent to Integer.parseInt(stringPool.get(buffer, pos, 4), 16);
+      char result = 0;
+      for (int i = pos, end = i + 4; i < end; i++) {
+        char c = buffer[i];
+        result <<= 4;
+        if (c >= '0' && c <= '9') {
+          result += (c - '0');
+        } else if (c >= 'a' && c <= 'f') {
+          result += (c - 'a' + 10);
+        } else if (c >= 'A' && c <= 'F') {
+          result += (c - 'A' + 10);
+        } else {
+          throw new NumberFormatException("\\u" + new String(buffer, pos, 4));
+        }
+      }
+      pos += 4;
+      return result;
+
+    case 't':
+      return '\t';
+
+    case 'b':
+      return '\b';
+
+    case 'n':
+      return '\n';
+
+    case 'r':
+      return '\r';
+
+    case 'f':
+      return '\f';
+
+    case '\n':
+      lineNumber++;
+      lineStart = pos;
+      // fall-through
+
+    case '\'':
+    case '"':
+    case '\\':
+    case '/':	
+    	return escaped;
+    default:
+    	// throw error when none of the above cases are matched
+    	throw syntaxError("Invalid escape sequence");
+    }
+  }
+
+  /**
+   * Throws a new IO exception with the given message and a context snippet
+   * with this reader's content.
+   */
+  private IOException syntaxError(String message) throws IOException {
+    throw new MalformedJsonException(message + locationString());
+  }
+
+  /**
+   * Consumes the non-execute prefix if it exists.
+   */
+  private void consumeNonExecutePrefix() throws IOException {
+    // fast forward through the leading whitespace
+    nextNonWhitespace(true);
+    pos--;
+
+    if (pos + NON_EXECUTE_PREFIX.length > limit && !fillBuffer(NON_EXECUTE_PREFIX.length)) {
+      return;
+    }
+
+    for (int i = 0; i < NON_EXECUTE_PREFIX.length; i++) {
+      if (buffer[pos + i] != NON_EXECUTE_PREFIX[i]) {
+        return; // not a security token!
+      }
+    }
+
+    // we consumed a security token!
+    pos += NON_EXECUTE_PREFIX.length;
+  }
+
+  static {
+    JsonReaderInternalAccess.INSTANCE = new JsonReaderInternalAccess() {
+      @Override public void promoteNameToValue(JsonReader reader) throws IOException {
+        if (reader instanceof JsonTreeReader) {
+          ((JsonTreeReader)reader).promoteNameToValue();
+          return;
+        }
+        int p = reader.peeked;
+        if (p == PEEKED_NONE) {
+          p = reader.doPeek();
+        }
+        if (p == PEEKED_DOUBLE_QUOTED_NAME) {
+          reader.peeked = PEEKED_DOUBLE_QUOTED;
+        } else if (p == PEEKED_SINGLE_QUOTED_NAME) {
+          reader.peeked = PEEKED_SINGLE_QUOTED;
+        } else if (p == PEEKED_UNQUOTED_NAME) {
+          reader.peeked = PEEKED_UNQUOTED;
+        } else {
+          throw new IllegalStateException(
+              "Expected a name but was " + reader.peek() + reader.locationString());
+        }
+      }
+    };
+  }
+}
diff --git a/gson/src/main/java/com/google/gson/stream/JsonScope.java b/gson/src/main/java/com/google/gson/stream/JsonScope.java
new file mode 100644
index 0000000000000000000000000000000000000000..da6913727afcc90fab5ae5f98e53ade56e5316f1
--- /dev/null
+++ b/gson/src/main/java/com/google/gson/stream/JsonScope.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.gson.stream;
+
+/**
+ * Lexical scoping elements within a JSON reader or writer.
+ *
+ * @author Jesse Wilson
+ * @since 1.6
+ */
+final class JsonScope {
+
+    /**
+     * An array with no elements requires no separators or newlines before
+     * it is closed.
+     */
+    static final int EMPTY_ARRAY = 1;
+
+    /**
+     * A array with at least one value requires a comma and newline before
+     * the next element.
+     */
+    static final int NONEMPTY_ARRAY = 2;
+
+    /**
+     * An object with no name/value pairs requires no separators or newlines
+     * before it is closed.
+     */
+    static final int EMPTY_OBJECT = 3;
+
+    /**
+     * An object whose most recent element is a key. The next element must
+     * be a value.
+     */
+    static final int DANGLING_NAME = 4;
+
+    /**
+     * An object with at least one name/value pair requires a comma and
+     * newline before the next element.
+     */
+    static final int NONEMPTY_OBJECT = 5;
+
+    /**
+     * No object or array has been started.
+     */
+    static final int EMPTY_DOCUMENT = 6;
+
+    /**
+     * A document with at an array or object.
+     */
+    static final int NONEMPTY_DOCUMENT = 7;
+
+    /**
+     * A document that's been closed and cannot be accessed.
+     */
+    static final int CLOSED = 8;
+}
diff --git a/gson/src/main/java/com/google/gson/stream/JsonToken.java b/gson/src/main/java/com/google/gson/stream/JsonToken.java
new file mode 100644
index 0000000000000000000000000000000000000000..f1025b3f4868b33480fe6d3066e7a986c42d69b5
--- /dev/null
+++ b/gson/src/main/java/com/google/gson/stream/JsonToken.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.gson.stream;
+
+/**
+ * A structure, name or value type in a JSON-encoded string.
+ *
+ * @author Jesse Wilson
+ * @since 1.6
+ */
+public enum JsonToken {
+
+  /**
+   * The opening of a JSON array. Written using {@link JsonWriter#beginArray}
+   * and read using {@link JsonReader#beginArray}.
+   */
+  BEGIN_ARRAY,
+
+  /**
+   * The closing of a JSON array. Written using {@link JsonWriter#endArray}
+   * and read using {@link JsonReader#endArray}.
+   */
+  END_ARRAY,
+
+  /**
+   * The opening of a JSON object. Written using {@link JsonWriter#beginObject}
+   * and read using {@link JsonReader#beginObject}.
+   */
+  BEGIN_OBJECT,
+
+  /**
+   * The closing of a JSON object. Written using {@link JsonWriter#endObject}
+   * and read using {@link JsonReader#endObject}.
+   */
+  END_OBJECT,
+
+  /**
+   * A JSON property name. Within objects, tokens alternate between names and
+   * their values. Written using {@link JsonWriter#name} and read using {@link
+   * JsonReader#nextName}
+   */
+  NAME,
+
+  /**
+   * A JSON string.
+   */
+  STRING,
+
+  /**
+   * A JSON number represented in this API by a Java {@code double}, {@code
+   * long}, or {@code int}.
+   */
+  NUMBER,
+
+  /**
+   * A JSON {@code true} or {@code false}.
+   */
+  BOOLEAN,
+
+  /**
+   * A JSON {@code null}.
+   */
+  NULL,
+
+  /**
+   * The end of the JSON stream. This sentinel value is returned by {@link
+   * JsonReader#peek()} to signal that the JSON-encoded value has no more
+   * tokens.
+   */
+  END_DOCUMENT
+}
diff --git a/gson/src/main/java/com/google/gson/stream/JsonWriter.java b/gson/src/main/java/com/google/gson/stream/JsonWriter.java
new file mode 100644
index 0000000000000000000000000000000000000000..84eaa0a7c4bb22f49d167e3308a69f9e173a1a0c
--- /dev/null
+++ b/gson/src/main/java/com/google/gson/stream/JsonWriter.java
@@ -0,0 +1,658 @@
+/*
+ * Copyright (C) 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.gson.stream;
+
+import java.io.Closeable;
+import java.io.Flushable;
+import java.io.IOException;
+import java.io.Writer;
+import java.util.Arrays;
+
+import static com.google.gson.stream.JsonScope.DANGLING_NAME;
+import static com.google.gson.stream.JsonScope.EMPTY_ARRAY;
+import static com.google.gson.stream.JsonScope.EMPTY_DOCUMENT;
+import static com.google.gson.stream.JsonScope.EMPTY_OBJECT;
+import static com.google.gson.stream.JsonScope.NONEMPTY_ARRAY;
+import static com.google.gson.stream.JsonScope.NONEMPTY_DOCUMENT;
+import static com.google.gson.stream.JsonScope.NONEMPTY_OBJECT;
+
+/**
+ * Writes a JSON (<a href="http://www.ietf.org/rfc/rfc7159.txt">RFC 7159</a>)
+ * encoded value to a stream, one token at a time. The stream includes both
+ * literal values (strings, numbers, booleans and nulls) as well as the begin
+ * and end delimiters of objects and arrays.
+ *
+ * <h3>Encoding JSON</h3>
+ * To encode your data as JSON, create a new {@code JsonWriter}. Each JSON
+ * document must contain one top-level array or object. Call methods on the
+ * writer as you walk the structure's contents, nesting arrays and objects as
+ * necessary:
+ * <ul>
+ *   <li>To write <strong>arrays</strong>, first call {@link #beginArray()}.
+ *       Write each of the array's elements with the appropriate {@link #value}
+ *       methods or by nesting other arrays and objects. Finally close the array
+ *       using {@link #endArray()}.
+ *   <li>To write <strong>objects</strong>, first call {@link #beginObject()}.
+ *       Write each of the object's properties by alternating calls to
+ *       {@link #name} with the property's value. Write property values with the
+ *       appropriate {@link #value} method or by nesting other objects or arrays.
+ *       Finally close the object using {@link #endObject()}.
+ * </ul>
+ *
+ * <h3>Example</h3>
+ * Suppose we'd like to encode a stream of messages such as the following: <pre> {@code
+ * [
+ *   {
+ *     "id": 912345678901,
+ *     "text": "How do I stream JSON in Java?",
+ *     "geo": null,
+ *     "user": {
+ *       "name": "json_newb",
+ *       "followers_count": 41
+ *      }
+ *   },
+ *   {
+ *     "id": 912345678902,
+ *     "text": "@json_newb just use JsonWriter!",
+ *     "geo": [50.454722, -104.606667],
+ *     "user": {
+ *       "name": "jesse",
+ *       "followers_count": 2
+ *     }
+ *   }
+ * ]}</pre>
+ * This code encodes the above structure: <pre>   {@code
+ *   public void writeJsonStream(OutputStream out, List<Message> messages) throws IOException {
+ *     JsonWriter writer = new JsonWriter(new OutputStreamWriter(out, "UTF-8"));
+ *     writer.setIndent("    ");
+ *     writeMessagesArray(writer, messages);
+ *     writer.close();
+ *   }
+ *
+ *   public void writeMessagesArray(JsonWriter writer, List<Message> messages) throws IOException {
+ *     writer.beginArray();
+ *     for (Message message : messages) {
+ *       writeMessage(writer, message);
+ *     }
+ *     writer.endArray();
+ *   }
+ *
+ *   public void writeMessage(JsonWriter writer, Message message) throws IOException {
+ *     writer.beginObject();
+ *     writer.name("id").value(message.getId());
+ *     writer.name("text").value(message.getText());
+ *     if (message.getGeo() != null) {
+ *       writer.name("geo");
+ *       writeDoublesArray(writer, message.getGeo());
+ *     } else {
+ *       writer.name("geo").nullValue();
+ *     }
+ *     writer.name("user");
+ *     writeUser(writer, message.getUser());
+ *     writer.endObject();
+ *   }
+ *
+ *   public void writeUser(JsonWriter writer, User user) throws IOException {
+ *     writer.beginObject();
+ *     writer.name("name").value(user.getName());
+ *     writer.name("followers_count").value(user.getFollowersCount());
+ *     writer.endObject();
+ *   }
+ *
+ *   public void writeDoublesArray(JsonWriter writer, List<Double> doubles) throws IOException {
+ *     writer.beginArray();
+ *     for (Double value : doubles) {
+ *       writer.value(value);
+ *     }
+ *     writer.endArray();
+ *   }}</pre>
+ *
+ * <p>Each {@code JsonWriter} may be used to write a single JSON stream.
+ * Instances of this class are not thread safe. Calls that would result in a
+ * malformed JSON string will fail with an {@link IllegalStateException}.
+ *
+ * @author Jesse Wilson
+ * @since 1.6
+ */
+public class JsonWriter implements Closeable, Flushable {
+
+  /*
+   * From RFC 7159, "All Unicode characters may be placed within the
+   * quotation marks except for the characters that must be escaped:
+   * quotation mark, reverse solidus, and the control characters
+   * (U+0000 through U+001F)."
+   *
+   * We also escape '\u2028' and '\u2029', which JavaScript interprets as
+   * newline characters. This prevents eval() from failing with a syntax
+   * error. http://code.google.com/p/google-gson/issues/detail?id=341
+   */
+  private static final String[] REPLACEMENT_CHARS;
+  private static final String[] HTML_SAFE_REPLACEMENT_CHARS;
+  static {
+    REPLACEMENT_CHARS = new String[128];
+    for (int i = 0; i <= 0x1f; i++) {
+      REPLACEMENT_CHARS[i] = String.format("\\u%04x", (int) i);
+    }
+    REPLACEMENT_CHARS['"'] = "\\\"";
+    REPLACEMENT_CHARS['\\'] = "\\\\";
+    REPLACEMENT_CHARS['\t'] = "\\t";
+    REPLACEMENT_CHARS['\b'] = "\\b";
+    REPLACEMENT_CHARS['\n'] = "\\n";
+    REPLACEMENT_CHARS['\r'] = "\\r";
+    REPLACEMENT_CHARS['\f'] = "\\f";
+    HTML_SAFE_REPLACEMENT_CHARS = REPLACEMENT_CHARS.clone();
+    HTML_SAFE_REPLACEMENT_CHARS['<'] = "\\u003c";
+    HTML_SAFE_REPLACEMENT_CHARS['>'] = "\\u003e";
+    HTML_SAFE_REPLACEMENT_CHARS['&'] = "\\u0026";
+    HTML_SAFE_REPLACEMENT_CHARS['='] = "\\u003d";
+    HTML_SAFE_REPLACEMENT_CHARS['\''] = "\\u0027";
+  }
+
+  /** The output data, containing at most one top-level array or object. */
+  private final Writer out;
+
+  private int[] stack = new int[32];
+  private int stackSize = 0;
+  {
+    push(EMPTY_DOCUMENT);
+  }
+
+  /**
+   * A string containing a full set of spaces for a single level of
+   * indentation, or null for no pretty printing.
+   */
+  private String indent;
+
+  /**
+   * The name/value separator; either ":" or ": ".
+   */
+  private String separator = ":";
+
+  private boolean lenient;
+
+  private boolean htmlSafe;
+
+  private String deferredName;
+
+  private boolean serializeNulls = true;
+
+  /**
+   * Creates a new instance that writes a JSON-encoded stream to {@code out}.
+   * For best performance, ensure {@link Writer} is buffered; wrapping in
+   * {@link java.io.BufferedWriter BufferedWriter} if necessary.
+   */
+  public JsonWriter(Writer out) {
+    if (out == null) {
+      throw new NullPointerException("out == null");
+    }
+    this.out = out;
+  }
+
+  /**
+   * Sets the indentation string to be repeated for each level of indentation
+   * in the encoded document. If {@code indent.isEmpty()} the encoded document
+   * will be compact. Otherwise the encoded document will be more
+   * human-readable.
+   *
+   * @param indent a string containing only whitespace.
+   */
+  public final void setIndent(String indent) {
+    if (indent.length() == 0) {
+      this.indent = null;
+      this.separator = ":";
+    } else {
+      this.indent = indent;
+      this.separator = ": ";
+    }
+  }
+
+  /**
+   * Configure this writer to relax its syntax rules. By default, this writer
+   * only emits well-formed JSON as specified by <a
+   * href="http://www.ietf.org/rfc/rfc7159.txt">RFC 7159</a>. Setting the writer
+   * to lenient permits the following:
+   * <ul>
+   *   <li>Top-level values of any type. With strict writing, the top-level
+   *       value must be an object or an array.
+   *   <li>Numbers may be {@link Double#isNaN() NaNs} or {@link
+   *       Double#isInfinite() infinities}.
+   * </ul>
+   */
+  public final void setLenient(boolean lenient) {
+    this.lenient = lenient;
+  }
+
+  /**
+   * Returns true if this writer has relaxed syntax rules.
+   */
+  public boolean isLenient() {
+    return lenient;
+  }
+
+  /**
+   * Configure this writer to emit JSON that's safe for direct inclusion in HTML
+   * and XML documents. This escapes the HTML characters {@code <}, {@code >},
+   * {@code &} and {@code =} before writing them to the stream. Without this
+   * setting, your XML/HTML encoder should replace these characters with the
+   * corresponding escape sequences.
+   */
+  public final void setHtmlSafe(boolean htmlSafe) {
+    this.htmlSafe = htmlSafe;
+  }
+
+  /**
+   * Returns true if this writer writes JSON that's safe for inclusion in HTML
+   * and XML documents.
+   */
+  public final boolean isHtmlSafe() {
+    return htmlSafe;
+  }
+
+  /**
+   * Sets whether object members are serialized when their value is null.
+   * This has no impact on array elements. The default is true.
+   */
+  public final void setSerializeNulls(boolean serializeNulls) {
+    this.serializeNulls = serializeNulls;
+  }
+
+  /**
+   * Returns true if object members are serialized when their value is null.
+   * This has no impact on array elements. The default is true.
+   */
+  public final boolean getSerializeNulls() {
+    return serializeNulls;
+  }
+
+  /**
+   * Begins encoding a new array. Each call to this method must be paired with
+   * a call to {@link #endArray}.
+   *
+   * @return this writer.
+   */
+  public JsonWriter beginArray() throws IOException {
+    writeDeferredName();
+    return open(EMPTY_ARRAY, '[');
+  }
+
+  /**
+   * Ends encoding the current array.
+   *
+   * @return this writer.
+   */
+  public JsonWriter endArray() throws IOException {
+    return close(EMPTY_ARRAY, NONEMPTY_ARRAY, ']');
+  }
+
+  /**
+   * Begins encoding a new object. Each call to this method must be paired
+   * with a call to {@link #endObject}.
+   *
+   * @return this writer.
+   */
+  public JsonWriter beginObject() throws IOException {
+    writeDeferredName();
+    return open(EMPTY_OBJECT, '{');
+  }
+
+  /**
+   * Ends encoding the current object.
+   *
+   * @return this writer.
+   */
+  public JsonWriter endObject() throws IOException {
+    return close(EMPTY_OBJECT, NONEMPTY_OBJECT, '}');
+  }
+
+  /**
+   * Enters a new scope by appending any necessary whitespace and the given
+   * bracket.
+   */
+  private JsonWriter open(int empty, char openBracket) throws IOException {
+    beforeValue();
+    push(empty);
+    out.write(openBracket);
+    return this;
+  }
+
+  /**
+   * Closes the current scope by appending any necessary whitespace and the
+   * given bracket.
+   */
+  private JsonWriter close(int empty, int nonempty, char closeBracket)
+      throws IOException {
+    int context = peek();
+    if (context != nonempty && context != empty) {
+      throw new IllegalStateException("Nesting problem.");
+    }
+    if (deferredName != null) {
+      throw new IllegalStateException("Dangling name: " + deferredName);
+    }
+
+    stackSize--;
+    if (context == nonempty) {
+      newline();
+    }
+    out.write(closeBracket);
+    return this;
+  }
+
+  private void push(int newTop) {
+    if (stackSize == stack.length) {
+      stack = Arrays.copyOf(stack, stackSize * 2);
+    }
+    stack[stackSize++] = newTop;
+  }
+
+  /**
+   * Returns the value on the top of the stack.
+   */
+  private int peek() {
+    if (stackSize == 0) {
+      throw new IllegalStateException("JsonWriter is closed.");
+    }
+    return stack[stackSize - 1];
+  }
+
+  /**
+   * Replace the value on the top of the stack with the given value.
+   */
+  private void replaceTop(int topOfStack) {
+    stack[stackSize - 1] = topOfStack;
+  }
+
+  /**
+   * Encodes the property name.
+   *
+   * @param name the name of the forthcoming value. May not be null.
+   * @return this writer.
+   */
+  public JsonWriter name(String name) throws IOException {
+    if (name == null) {
+      throw new NullPointerException("name == null");
+    }
+    if (deferredName != null) {
+      throw new IllegalStateException();
+    }
+    if (stackSize == 0) {
+      throw new IllegalStateException("JsonWriter is closed.");
+    }
+    deferredName = name;
+    return this;
+  }
+
+  private void writeDeferredName() throws IOException {
+    if (deferredName != null) {
+      beforeName();
+      string(deferredName);
+      deferredName = null;
+    }
+  }
+
+  /**
+   * Encodes {@code value}.
+   *
+   * @param value the literal string value, or null to encode a null literal.
+   * @return this writer.
+   */
+  public JsonWriter value(String value) throws IOException {
+    if (value == null) {
+      return nullValue();
+    }
+    writeDeferredName();
+    beforeValue();
+    string(value);
+    return this;
+  }
+
+  /**
+   * Writes {@code value} directly to the writer without quoting or
+   * escaping.
+   *
+   * @param value the literal string value, or null to encode a null literal.
+   * @return this writer.
+   */
+  public JsonWriter jsonValue(String value) throws IOException {
+    if (value == null) {
+      return nullValue();
+    }
+    writeDeferredName();
+    beforeValue();
+    out.append(value);
+    return this;
+  }
+
+  /**
+   * Encodes {@code null}.
+   *
+   * @return this writer.
+   */
+  public JsonWriter nullValue() throws IOException {
+    if (deferredName != null) {
+      if (serializeNulls) {
+        writeDeferredName();
+      } else {
+        deferredName = null;
+        return this; // skip the name and the value
+      }
+    }
+    beforeValue();
+    out.write("null");
+    return this;
+  }
+
+  /**
+   * Encodes {@code value}.
+   *
+   * @return this writer.
+   */
+  public JsonWriter value(boolean value) throws IOException {
+    writeDeferredName();
+    beforeValue();
+    out.write(value ? "true" : "false");
+    return this;
+  }
+
+  /**
+   * Encodes {@code value}.
+   *
+   * @return this writer.
+   */
+  public JsonWriter value(Boolean value) throws IOException {
+    if (value == null) {
+      return nullValue();
+    }
+    writeDeferredName();
+    beforeValue();
+    out.write(value ? "true" : "false");
+    return this;
+  }
+
+  /**
+   * Encodes {@code value}.
+   *
+   * @param value a finite value. May not be {@link Double#isNaN() NaNs} or
+   *     {@link Double#isInfinite() infinities}.
+   * @return this writer.
+   */
+  public JsonWriter value(double value) throws IOException {
+    writeDeferredName();
+    if (!lenient && (Double.isNaN(value) || Double.isInfinite(value))) {
+      throw new IllegalArgumentException("Numeric values must be finite, but was " + value);
+    }
+    beforeValue();
+    out.append(Double.toString(value));
+    return this;
+  }
+
+  /**
+   * Encodes {@code value}.
+   *
+   * @return this writer.
+   */
+  public JsonWriter value(long value) throws IOException {
+    writeDeferredName();
+    beforeValue();
+    out.write(Long.toString(value));
+    return this;
+  }
+
+  /**
+   * Encodes {@code value}.
+   *
+   * @param value a finite value. May not be {@link Double#isNaN() NaNs} or
+   *     {@link Double#isInfinite() infinities}.
+   * @return this writer.
+   */
+  public JsonWriter value(Number value) throws IOException {
+    if (value == null) {
+      return nullValue();
+    }
+
+    writeDeferredName();
+    String string = value.toString();
+    if (!lenient
+        && (string.equals("-Infinity") || string.equals("Infinity") || string.equals("NaN"))) {
+      throw new IllegalArgumentException("Numeric values must be finite, but was " + value);
+    }
+    beforeValue();
+    out.append(string);
+    return this;
+  }
+
+  /**
+   * Ensures all buffered data is written to the underlying {@link Writer}
+   * and flushes that writer.
+   */
+  public void flush() throws IOException {
+    if (stackSize == 0) {
+      throw new IllegalStateException("JsonWriter is closed.");
+    }
+    out.flush();
+  }
+
+  /**
+   * Flushes and closes this writer and the underlying {@link Writer}.
+   *
+   * @throws IOException if the JSON document is incomplete.
+   */
+  public void close() throws IOException {
+    out.close();
+
+    int size = stackSize;
+    if (size > 1 || size == 1 && stack[size - 1] != NONEMPTY_DOCUMENT) {
+      throw new IOException("Incomplete document");
+    }
+    stackSize = 0;
+  }
+
+  private void string(String value) throws IOException {
+    String[] replacements = htmlSafe ? HTML_SAFE_REPLACEMENT_CHARS : REPLACEMENT_CHARS;
+    out.write('\"');
+    int last = 0;
+    int length = value.length();
+    for (int i = 0; i < length; i++) {
+      char c = value.charAt(i);
+      String replacement;
+      if (c < 128) {
+        replacement = replacements[c];
+        if (replacement == null) {
+          continue;
+        }
+      } else if (c == '\u2028') {
+        replacement = "\\u2028";
+      } else if (c == '\u2029') {
+        replacement = "\\u2029";
+      } else {
+        continue;
+      }
+      if (last < i) {
+        out.write(value, last, i - last);
+      }
+      out.write(replacement);
+      last = i + 1;
+    }
+    if (last < length) {
+      out.write(value, last, length - last);
+    }
+    out.write('\"');
+  }
+
+  private void newline() throws IOException {
+    if (indent == null) {
+      return;
+    }
+
+    out.write('\n');
+    for (int i = 1, size = stackSize; i < size; i++) {
+      out.write(indent);
+    }
+  }
+
+  /**
+   * Inserts any necessary separators and whitespace before a name. Also
+   * adjusts the stack to expect the name's value.
+   */
+  private void beforeName() throws IOException {
+    int context = peek();
+    if (context == NONEMPTY_OBJECT) { // first in object
+      out.write(',');
+    } else if (context != EMPTY_OBJECT) { // not in an object!
+      throw new IllegalStateException("Nesting problem.");
+    }
+    newline();
+    replaceTop(DANGLING_NAME);
+  }
+
+  /**
+   * Inserts any necessary separators and whitespace before a literal value,
+   * inline array, or inline object. Also adjusts the stack to expect either a
+   * closing bracket or another element.
+   */
+  @SuppressWarnings("fallthrough")
+  private void beforeValue() throws IOException {
+    switch (peek()) {
+    case NONEMPTY_DOCUMENT:
+      if (!lenient) {
+        throw new IllegalStateException(
+            "JSON must have only one top-level value.");
+      }
+      // fall-through
+    case EMPTY_DOCUMENT: // first in document
+      replaceTop(NONEMPTY_DOCUMENT);
+      break;
+
+    case EMPTY_ARRAY: // first in array
+      replaceTop(NONEMPTY_ARRAY);
+      newline();
+      break;
+
+    case NONEMPTY_ARRAY: // another in array
+      out.append(',');
+      newline();
+      break;
+
+    case DANGLING_NAME: // value for name
+      out.append(separator);
+      replaceTop(NONEMPTY_OBJECT);
+      break;
+
+    default:
+      throw new IllegalStateException("Nesting problem.");
+    }
+  }
+}
diff --git a/gson/src/main/java/com/google/gson/stream/MalformedJsonException.java b/gson/src/main/java/com/google/gson/stream/MalformedJsonException.java
new file mode 100644
index 0000000000000000000000000000000000000000..9da70ebccdc8970ed094cd8c94f7a80d0f2a922a
--- /dev/null
+++ b/gson/src/main/java/com/google/gson/stream/MalformedJsonException.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.gson.stream;
+
+import java.io.IOException;
+
+/**
+ * Thrown when a reader encounters malformed JSON. Some syntax errors can be
+ * ignored by calling {@link JsonReader#setLenient(boolean)}.
+ */
+public final class MalformedJsonException extends IOException {
+  private static final long serialVersionUID = 1L;
+
+  public MalformedJsonException(String msg) {
+    super(msg);
+  }
+
+  public MalformedJsonException(String msg, Throwable throwable) {
+    super(msg);
+    // Using initCause() instead of calling super() because Java 1.5 didn't retrofit IOException
+    // with a constructor with Throwable. This was done in Java 1.6
+    initCause(throwable);
+  }
+
+  public MalformedJsonException(Throwable throwable) {
+    // Using initCause() instead of calling super() because Java 1.5 didn't retrofit IOException
+    // with a constructor with Throwable. This was done in Java 1.6
+    initCause(throwable);
+  }
+}
diff --git a/gson/src/main/java/plugins/danyfel80/library/gson/GsonPlugin.java b/gson/src/main/java/plugins/danyfel80/library/gson/GsonPlugin.java
new file mode 100644
index 0000000000000000000000000000000000000000..9fd4860d29e74be77456beff451c4ca69adea3a8
--- /dev/null
+++ b/gson/src/main/java/plugins/danyfel80/library/gson/GsonPlugin.java
@@ -0,0 +1,12 @@
+package plugins.danyfel80.library.gson;
+import icy.plugin.abstract_.Plugin;
+import icy.plugin.interface_.PluginLibrary;
+
+/**
+ * Icy wrapper for the Gson library.
+ * @author Daniel Felipe Gonzalez Obando
+ */
+public class GsonPlugin extends Plugin implements PluginLibrary
+{
+
+}