<classpath>
<classpathentry excluding="ro/ieval/unical/DatabaseInteract.java|ro/ieval/unical/MySQLiteHelper.java" kind="src" path="src"/>
<classpathentry kind="src" path="gen"/>
+ <classpathentry kind="src" path="gson">
+ <attributes>
+ <attribute name="ignore_optional_problems" value="true"/>
+ </attributes>
+ </classpathentry>
<classpathentry kind="con" path="com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"/>
<classpathentry exported="true" kind="con" path="com.android.ide.eclipse.adt.DEPENDENCIES"/>
<classpathentry exported="true" kind="con" path="com.android.ide.eclipse.adt.LIBRARIES"/>
--- /dev/null
+/*
+ * 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.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.Locale;
+import java.util.TimeZone;
+
+/**
+ * This type adapter supports three subclasses of date: Date, Timestamp, and
+ * java.sql.Date.
+ *
+ * @author Inderjeet Singh
+ * @author Joel Leitch
+ */
+final class DefaultDateTypeAdapter implements JsonSerializer<Date>, JsonDeserializer<Date> {
+
+ // TODO: migrate to streaming adapter
+
+ private final DateFormat enUsFormat;
+ private final DateFormat localFormat;
+ private final DateFormat iso8601Format;
+
+ DefaultDateTypeAdapter() {
+ this(DateFormat.getDateTimeInstance(DateFormat.DEFAULT, DateFormat.DEFAULT, Locale.US),
+ DateFormat.getDateTimeInstance(DateFormat.DEFAULT, DateFormat.DEFAULT));
+ }
+
+ DefaultDateTypeAdapter(String datePattern) {
+ this(new SimpleDateFormat(datePattern, Locale.US), new SimpleDateFormat(datePattern));
+ }
+
+ DefaultDateTypeAdapter(int style) {
+ this(DateFormat.getDateInstance(style, Locale.US), DateFormat.getDateInstance(style));
+ }
+
+ public DefaultDateTypeAdapter(int dateStyle, int timeStyle) {
+ this(DateFormat.getDateTimeInstance(dateStyle, timeStyle, Locale.US),
+ DateFormat.getDateTimeInstance(dateStyle, timeStyle));
+ }
+
+ DefaultDateTypeAdapter(DateFormat enUsFormat, DateFormat localFormat) {
+ this.enUsFormat = enUsFormat;
+ this.localFormat = localFormat;
+ this.iso8601Format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.US);
+ this.iso8601Format.setTimeZone(TimeZone.getTimeZone("UTC"));
+ }
+
+ // These methods need to be synchronized since JDK DateFormat classes are not thread-safe
+ // See issue 162
+ public JsonElement serialize(Date src, Type typeOfSrc, JsonSerializationContext context) {
+ synchronized (localFormat) {
+ String dateFormatAsString = enUsFormat.format(src);
+ return new JsonPrimitive(dateFormatAsString);
+ }
+ }
+
+ public Date deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
+ throws JsonParseException {
+ if (!(json instanceof JsonPrimitive)) {
+ throw new JsonParseException("The date should be a string value");
+ }
+ Date date = deserializeToDate(json);
+ if (typeOfT == Date.class) {
+ return date;
+ } else if (typeOfT == Timestamp.class) {
+ return new Timestamp(date.getTime());
+ } else if (typeOfT == java.sql.Date.class) {
+ return new java.sql.Date(date.getTime());
+ } else {
+ throw new IllegalArgumentException(getClass() + " cannot deserialize to " + typeOfT);
+ }
+ }
+
+ private Date deserializeToDate(JsonElement json) {
+ synchronized (localFormat) {
+ try {
+ return localFormat.parse(json.getAsString());
+ } catch (ParseException ignored) {
+ }
+ try {
+ return enUsFormat.parse(json.getAsString());
+ } catch (ParseException ignored) {
+ }
+ try {
+ return iso8601Format.parse(json.getAsString());
+ } catch (ParseException e) {
+ throw new JsonSyntaxException(json.getAsString(), e);
+ }
+ }
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append(DefaultDateTypeAdapter.class.getSimpleName());
+ sb.append('(').append(localFormat.getClass().getSimpleName()).append(')');
+ return sb.toString();
+ }
+}
--- /dev/null
+/*
+ * 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 false then that class or field type
+ * will not be part of the JSON output. For deserialization, if {@link #shouldSkipClass(Class)}
+ * returns false, 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<?> excludedThisClass;
+ *
+ * public SpecificClassExclusionStrategy(Class<?> excludedThisClass) {
+ * this.excludedThisClass = excludedThisClass;
+ * }
+ *
+ * public boolean shouldSkipClass(Class<?> 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 @interface FooAnnotation {
+ * // some implementation here
+ * }
+ *
+ * // Excludes any field (or class) that is tagged with an "@FooAnnotation"
+ * private static class FooAnnotationExclusionStrategy implements ExclusionStrategy {
+ * public boolean shouldSkipClass(Class<?> 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);
+}
--- /dev/null
+/*
+ * 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<String> red;
+ * }
+ *
+ * Type listParmeterizedType = new TypeToken<List<String>>() {}.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<String> 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;
+ }
+
+ /**
+ * This is exposed internally only for the removing synthetic fields from the JSON output.
+ *
+ * @return true if the field is synthetic; otherwise false
+ * @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();
+ }
+}
--- /dev/null
+/*
+ * 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() {
+ 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" ---> "JSON Field Name":</p>
+ * <ul>
+ * <li>someFieldName ---> SomeFieldName</li>
+ * <li>_someFieldName ---> _SomeFieldName</li>
+ * </ul>
+ */
+ UPPER_CAMEL_CASE() {
+ 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" ---> "JSON Field Name":</p>
+ * <ul>
+ * <li>someFieldName ---> Some Field Name</li>
+ * <li>_someFieldName ---> _Some Field Name</li>
+ * </ul>
+ *
+ * @since 1.4
+ */
+ UPPER_CAMEL_CASE_WITH_SPACES() {
+ 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" ---> "JSON Field Name":</p>
+ * <ul>
+ * <li>someFieldName ---> some_field_name</li>
+ * <li>_someFieldName ---> _some_field_name</li>
+ * <li>aStringField ---> a_string_field</li>
+ * <li>aURL ---> a_u_r_l</li>
+ * </ul>
+ */
+ LOWER_CASE_WITH_UNDERSCORES() {
+ 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" ---> "JSON Field Name":</p>
+ * <ul>
+ * <li>someFieldName ---> some-field-name</li>
+ * <li>_someFieldName ---> _some-field-name</li>
+ * <li>aStringField ---> a-string-field</li>
+ * <li>aURL ---> 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() {
+ 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}.
+ */
+ private static String separateCamelCase(String name, String separator) {
+ StringBuilder translation = new StringBuilder();
+ for (int i = 0; i < name.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.
+ */
+ private static String upperCaseFirstLetter(String name) {
+ StringBuilder fieldNameBuilder = new StringBuilder();
+ int index = 0;
+ char firstCharacter = name.charAt(index);
+
+ while (index < name.length() - 1) {
+ if (Character.isLetter(firstCharacter)) {
+ break;
+ }
+
+ fieldNameBuilder.append(firstCharacter);
+ firstCharacter = name.charAt(++index);
+ }
+
+ if (index == name.length()) {
+ return fieldNameBuilder.toString();
+ }
+
+ if (!Character.isUpperCase(firstCharacter)) {
+ String modifiedTarget = modifyString(Character.toUpperCase(firstCharacter), name, ++index);
+ return fieldNameBuilder.append(modifiedTarget).toString();
+ } else {
+ return name;
+ }
+ }
+
+ private static String modifyString(char firstCharacter, String srcString, int indexOfSubstring) {
+ return (indexOfSubstring < srcString.length())
+ ? firstCharacter + srcString.substring(indexOfSubstring)
+ : String.valueOf(firstCharacter);
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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.ConstructorConstructor;
+import com.google.gson.internal.Excluder;
+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.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;
+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.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 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.
+ *
+ * <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>
+ *
+ * <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 deserialing a {@code ParameterizedType}:
+ *
+ * <pre>
+ * Type listType = new TypeToken<List<String>>() {}.getType();
+ * List<String> target = new LinkedList<String>();
+ * target.add("blah");
+ *
+ * Gson gson = new Gson();
+ * String json = gson.toJson(target, listType);
+ * List<String> target2 = gson.fromJson(json, listType);
+ * </pre></p>
+ *
+ * <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;
+
+ 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
+ = Collections.synchronizedMap(new HashMap<TypeToken<?>, TypeAdapter<?>>());
+
+ private final List<TypeAdapterFactory> factories;
+ private final ConstructorConstructor constructorConstructor;
+
+ private final boolean serializeNulls;
+ private final boolean htmlSafe;
+ private final boolean generateNonExecutableJson;
+ private final boolean prettyPrinting;
+
+ final JsonDeserializationContext deserializationContext = new JsonDeserializationContext() {
+ @SuppressWarnings("unchecked")
+ public <T> T deserialize(JsonElement json, Type typeOfT) throws JsonParseException {
+ return (T) fromJson(json, typeOfT);
+ }
+ };
+
+ final JsonSerializationContext serializationContext = new JsonSerializationContext() {
+ public JsonElement serialize(Object src) {
+ return toJsonTree(src);
+ }
+ public JsonElement serialize(Object src, Type typeOfSrc) {
+ return toJsonTree(src, typeOfSrc);
+ }
+ };
+
+ /**
+ * 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>"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(), false, false, DEFAULT_JSON_NON_EXECUTABLE,
+ true, false, false, LongSerializationPolicy.DEFAULT,
+ Collections.<TypeAdapterFactory>emptyList());
+ }
+
+ Gson(final Excluder excluder, final FieldNamingStrategy fieldNamingPolicy,
+ final Map<Type, InstanceCreator<?>> instanceCreators, boolean serializeNulls,
+ boolean complexMapKeySerialization, boolean generateNonExecutableGson, boolean htmlSafe,
+ boolean prettyPrinting, boolean serializeSpecialFloatingPointValues,
+ LongSerializationPolicy longSerializationPolicy,
+ List<TypeAdapterFactory> typeAdapterFactories) {
+ this.constructorConstructor = new ConstructorConstructor(instanceCreators);
+ this.serializeNulls = serializeNulls;
+ this.generateNonExecutableJson = generateNonExecutableGson;
+ this.htmlSafe = htmlSafe;
+ this.prettyPrinting = prettyPrinting;
+
+ 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);
+
+ // user's type adapters
+ factories.addAll(typeAdapterFactories);
+
+ // 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);
+ factories.add(TypeAdapters.newFactory(long.class, Long.class,
+ longAdapter(longSerializationPolicy)));
+ 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.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.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.ENUM_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));
+ factories.add(new ReflectiveTypeAdapterFactory(
+ constructorConstructor, fieldNamingPolicy, excluder));
+
+ this.factories = Collections.unmodifiableList(factories);
+ }
+
+ 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);
+ }
+ };
+ }
+
+ private 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 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());
+ }
+ };
+ }
+
+ /**
+ * 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);
+ 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 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 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) {
+ 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<Collection<Foo>>(){}.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<Collection<Foo>>(){}.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<Collection<Foo>>(){}.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);
+ } 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 RuntimeException(e);
+ }
+ }
+
+ /**
+ * Returns a new JSON writer configured for this GSON and with the non-execute
+ * prefix if that is configured.
+ */
+ private 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;
+ }
+
+ /**
+ * 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);
+ } 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
+ * @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<Collection<Foo>>(){}.getType();
+ * </pre>
+ * @return an object of type T from the string
+ * @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
+ * @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 = new JsonReader(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<Collection<Foo>>(){}.getType();
+ * </pre>
+ * @return an object of type T from the json
+ * @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 = new JsonReader(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}.
+ * 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);
+ } 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
+ * @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<Collection<Foo>>(){}.getType();
+ * </pre>
+ * @return an object of type T from the json
+ * @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();
+ }
+}
--- /dev/null
+/*
+ * 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.TypeAdapters;
+import com.google.gson.reflect.TypeToken;
+
+/**
+ * <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>
+ *
+ * <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>
+ * </p>
+ *
+ * @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;
+ private String datePattern;
+ private int dateStyle = DateFormat.DEFAULT;
+ private int timeStyle = DateFormat.DEFAULT;
+ private boolean complexMapKeySerialization;
+ private boolean serializeSpecialFloatingPointValues;
+ private boolean escapeHtmlChars = true;
+ private boolean prettyPrinting;
+ private boolean generateNonExecutableJson;
+
+ /**
+ * 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() {
+ }
+
+ /**
+ * 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));
+ * }
+ *
+ * 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 serializaiton/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 escapes HTML characters such as < > 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(0,
+ 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>();
+ factories.addAll(this.factories);
+ Collections.reverse(factories);
+ factories.addAll(this.hierarchyFactories);
+ addTypeAdaptersForDate(datePattern, dateStyle, timeStyle, factories);
+
+ return new Gson(excluder, fieldNamingPolicy, instanceCreators,
+ serializeNulls, complexMapKeySerialization,
+ generateNonExecutableJson, escapeHtmlChars, prettyPrinting,
+ serializeSpecialFloatingPointValues, longSerializationPolicy, factories);
+ }
+
+ private void addTypeAdaptersForDate(String datePattern, int dateStyle, int timeStyle,
+ List<TypeAdapterFactory> factories) {
+ DefaultDateTypeAdapter dateTypeAdapter;
+ if (datePattern != null && !"".equals(datePattern.trim())) {
+ dateTypeAdapter = new DefaultDateTypeAdapter(datePattern);
+ } else if (dateStyle != DateFormat.DEFAULT && timeStyle != DateFormat.DEFAULT) {
+ dateTypeAdapter = new DefaultDateTypeAdapter(dateStyle, timeStyle);
+ } else {
+ return;
+ }
+
+ factories.add(TreeTypeAdapter.newFactory(TypeToken.get(Date.class), dateTypeAdapter));
+ factories.add(TreeTypeAdapter.newFactory(TypeToken.get(Timestamp.class), dateTypeAdapter));
+ factories.add(TreeTypeAdapter.newFactory(TypeToken.get(java.sql.Date.class), dateTypeAdapter));
+ }
+}
--- /dev/null
+/*
+ * 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<T> {
+ * private final Class<T> clazz;
+ * private final long value;
+ * public Id(Class<T> 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<Id> {
+ * 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);
+}
--- /dev/null
+/*
+ * 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>();
+ }
+
+ @Override
+ JsonArray deepCopy() {
+ JsonArray result = new JsonArray();
+ for (JsonElement element : elements) {
+ result.add(element.deepCopy());
+ }
+ return result;
+ }
+
+ /**
+ * 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);
+ }
+
+ /**
+ * 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 elemetns 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();
+ }
+}
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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<T> {
+ * private final Class<T> clazz;
+ * private final long value;
+ * public Id(Class<T> 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<Id>() {
+ * 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;
+}
--- /dev/null
+/*
+ * 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.
+ */
+ 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 ClassCastException} 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 ClassCastException} 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("This is not a JSON Array.");
+ }
+
+ /**
+ * convenience method to get this element as a {@link JsonPrimitive}. If the element is of some
+ * other type, a {@link ClassCastException} 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("This is not a JSON Primitive.");
+ }
+
+ /**
+ * convenience method to get this element as a {@link JsonNull}. If the element is of some
+ * other type, a {@link ClassCastException} 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("This is not a JSON Null.");
+ }
+
+ /**
+ * 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 Boolean} value.
+ *
+ * @return get this element as a {@link 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.
+ */
+ Boolean getAsBooleanWrapper() {
+ 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 this element as a primitive character value.
+ *
+ * @return get this element as a primitive char value.
+ * @throws ClassCastException if the element is of not a {@link JsonPrimitive} and is not a valid
+ * char value.
+ * @throws IllegalStateException if the element is of the type {@link JsonArray} but contains
+ * more than a single element.
+ * @since 1.3
+ */
+ 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);
+ }
+ }
+}
--- /dev/null
+/*
+ * 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);
+ }
+}
--- /dev/null
+/*\r
+ * Copyright (C) 2008 Google Inc.\r
+ *\r
+ * Licensed under the Apache License, Version 2.0 (the "License");\r
+ * you may not use this file except in compliance with the License.\r
+ * You may obtain a copy of the License at\r
+ *\r
+ * http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ */\r
+\r
+package com.google.gson;\r
+\r
+/**\r
+ * A class representing a Json {@code null} value.\r
+ *\r
+ * @author Inderjeet Singh\r
+ * @author Joel Leitch\r
+ * @since 1.2\r
+ */\r
+public final class JsonNull extends JsonElement {\r
+ /**\r
+ * singleton for JsonNull\r
+ *\r
+ * @since 1.8\r
+ */\r
+ public static final JsonNull INSTANCE = new JsonNull();\r
+\r
+ /**\r
+ * Creates a new JsonNull object.\r
+ * Deprecated since Gson version 1.8. Use {@link #INSTANCE} instead\r
+ */\r
+ @Deprecated\r
+ public JsonNull() {\r
+ // Do nothing\r
+ }\r
+\r
+ @Override\r
+ JsonNull deepCopy() {\r
+ return INSTANCE;\r
+ }\r
+\r
+ /**\r
+ * All instances of JsonNull have the same hash code since they are indistinguishable\r
+ */\r
+ @Override\r
+ public int hashCode() {\r
+ return JsonNull.class.hashCode();\r
+ }\r
+\r
+ /**\r
+ * All instances of JsonNull are the same\r
+ */\r
+ @Override\r
+ public boolean equals(Object other) {\r
+ return this == other || other instanceof JsonNull;\r
+ }\r
+}\r
--- /dev/null
+/*
+ * 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>();
+
+ @Override
+ 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) {
+ if (value == null) {
+ value = JsonNull.INSTANCE;
+ }
+ members.put(property, 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, createJsonElement(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, createJsonElement(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, createJsonElement(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, createJsonElement(value));
+ }
+
+ /**
+ * Creates the proper {@link JsonElement} object from the given {@code value} object.
+ *
+ * @param value the object to generate the {@link JsonElement} for
+ * @return a {@link JsonPrimitive} if the {@code value} is not null, otherwise a {@link JsonNull}
+ */
+ private JsonElement createJsonElement(Object value) {
+ return 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();
+ }
+
+ /**
+ * 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();
+ }
+}
--- /dev/null
+/*
+ * 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);
+ }
+}
--- /dev/null
+/*\r
+ * Copyright (C) 2009 Google Inc.\r
+ *\r
+ * Licensed under the Apache License, Version 2.0 (the "License");\r
+ * you may not use this file except in compliance with the License.\r
+ * You may obtain a copy of the License at\r
+ *\r
+ * http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ */\r
+package com.google.gson;\r
+\r
+import java.io.IOException;\r
+import java.io.Reader;\r
+import java.io.StringReader;\r
+\r
+import com.google.gson.internal.Streams;\r
+import com.google.gson.stream.JsonReader;\r
+import com.google.gson.stream.JsonToken;\r
+import com.google.gson.stream.MalformedJsonException;\r
+\r
+/**\r
+ * A parser to parse Json into a parse tree of {@link JsonElement}s\r
+ *\r
+ * @author Inderjeet Singh\r
+ * @author Joel Leitch\r
+ * @since 1.3\r
+ */\r
+public final class JsonParser {\r
+\r
+ /**\r
+ * Parses the specified JSON string into a parse tree\r
+ *\r
+ * @param json JSON text\r
+ * @return a parse tree of {@link JsonElement}s corresponding to the specified JSON\r
+ * @throws JsonParseException if the specified text is not valid JSON\r
+ * @since 1.3\r
+ */\r
+ public JsonElement parse(String json) throws JsonSyntaxException {\r
+ return parse(new StringReader(json));\r
+ }\r
+\r
+ /**\r
+ * Parses the specified JSON string into a parse tree\r
+ *\r
+ * @param json JSON text\r
+ * @return a parse tree of {@link JsonElement}s corresponding to the specified JSON\r
+ * @throws JsonParseException if the specified text is not valid JSON\r
+ * @since 1.3\r
+ */\r
+ public JsonElement parse(Reader json) throws JsonIOException, JsonSyntaxException {\r
+ try {\r
+ JsonReader jsonReader = new JsonReader(json);\r
+ JsonElement element = parse(jsonReader);\r
+ if (!element.isJsonNull() && jsonReader.peek() != JsonToken.END_DOCUMENT) {\r
+ throw new JsonSyntaxException("Did not consume the entire document.");\r
+ }\r
+ return element;\r
+ } catch (MalformedJsonException e) {\r
+ throw new JsonSyntaxException(e);\r
+ } catch (IOException e) {\r
+ throw new JsonIOException(e);\r
+ } catch (NumberFormatException e) {\r
+ throw new JsonSyntaxException(e);\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Returns the next value from the JSON stream as a parse tree.\r
+ *\r
+ * @throws JsonParseException if there is an IOException or if the specified\r
+ * text is not valid JSON\r
+ * @since 1.6\r
+ */\r
+ public JsonElement parse(JsonReader json) throws JsonIOException, JsonSyntaxException {\r
+ boolean lenient = json.isLenient();\r
+ json.setLenient(true);\r
+ try {\r
+ return Streams.parse(json);\r
+ } catch (StackOverflowError e) {\r
+ throw new JsonParseException("Failed parsing JSON source: " + json + " to Json", e);\r
+ } catch (OutOfMemoryError e) {\r
+ throw new JsonParseException("Failed parsing JSON source: " + json + " to Json", e);\r
+ } finally {\r
+ json.setLenient(lenient);\r
+ }\r
+ }\r
+}\r
--- /dev/null
+/*
+ * 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 com.google.gson.internal.$Gson$Preconditions;
+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 static final Class<?>[] PRIMITIVE_TYPES = { int.class, long.class, short.class,
+ float.class, double.class, byte.class, boolean.class, char.class, Integer.class, Long.class,
+ Short.class, Float.class, Double.class, Byte.class, Boolean.class, Character.class };
+
+ private Object value;
+
+ /**
+ * Create a primitive containing a boolean value.
+ *
+ * @param bool the value to create the primitive with.
+ */
+ public JsonPrimitive(Boolean bool) {
+ setValue(bool);
+ }
+
+ /**
+ * Create a primitive containing a {@link Number}.
+ *
+ * @param number the value to create the primitive with.
+ */
+ public JsonPrimitive(Number number) {
+ setValue(number);
+ }
+
+ /**
+ * Create a primitive containing a String value.
+ *
+ * @param string the value to create the primitive with.
+ */
+ public JsonPrimitive(String string) {
+ setValue(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) {
+ setValue(c);
+ }
+
+ /**
+ * Create a primitive using the specified Object. It must be an instance of {@link Number}, a
+ * Java primitive type, or a String.
+ *
+ * @param primitive the value to create the primitive with.
+ */
+ JsonPrimitive(Object primitive) {
+ setValue(primitive);
+ }
+
+ @Override
+ JsonPrimitive deepCopy() {
+ return this;
+ }
+
+ void setValue(Object primitive) {
+ if (primitive instanceof Character) {
+ // convert characters to strings since in JSON, characters are represented as a single
+ // character string
+ char c = ((Character) primitive).charValue();
+ this.value = String.valueOf(c);
+ } else {
+ $Gson$Preconditions.checkArgument(primitive instanceof Number
+ || isPrimitiveOrString(primitive));
+ this.value = primitive;
+ }
+ }
+
+ /**
+ * 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 {@link Boolean}.
+ *
+ * @return get this element as a {@link Boolean}.
+ */
+ @Override
+ Boolean getAsBooleanWrapper() {
+ return (Boolean) value;
+ }
+
+ /**
+ * 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 getAsBooleanWrapper().booleanValue();
+ } else {
+ // 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 getAsBooleanWrapper().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);
+ }
+
+ private static boolean isPrimitiveOrString(Object target) {
+ if (target instanceof String) {
+ return true;
+ }
+
+ Class<?> classOfPrimitive = target.getClass();
+ for (Class<?> standardPrimitive : PRIMITIVE_TYPES) {
+ if (standardPrimitive.isAssignableFrom(classOfPrimitive)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @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;
+ }
+}
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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<T> {
+ * private final Class<T> clazz;
+ * private final long value;
+ *
+ * public Id(Class<T> clazz, long value) {
+ * this.clazz = clazz;
+ * this.value = value;
+ * }
+ *
+ * public long getValue() {
+ * return value;
+ * }
+ * }
+ * </pre></p>
+ *
+ * <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<Id>() {
+ * public JsonElement serialize(Id id, Type typeOfId, JsonSerializationContext context) {
+ * return new JsonPrimitive(id.getValue());
+ * }
+ * }
+ * </pre></p>
+ *
+ * <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);
+}
--- /dev/null
+/*
+ * 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();
+ }
+}
--- /dev/null
+/*
+ * 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);
+ }
+}
--- /dev/null
+/*
+ * 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() {
+ 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() {
+ 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);
+}
--- /dev/null
+/*
+ * 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.$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;
+
+/**
+ * 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.
+ */
+final class TreeTypeAdapter<T> extends TypeAdapter<T> {
+ private final JsonSerializer<T> serializer;
+ private final JsonDeserializer<T> deserializer;
+ private final Gson gson;
+ private final TypeToken<T> typeToken;
+ private final TypeAdapterFactory skipPast;
+
+ /** The delegate is lazily created because it may not be needed, and creating it may fail. */
+ private TypeAdapter<T> delegate;
+
+ private 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(), gson.deserializationContext);
+ }
+
+ @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(), gson.serializationContext);
+ 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 class SingleTypeFactory implements TypeAdapterFactory {
+ private final TypeToken<?> exactType;
+ private final boolean matchRawType;
+ private final Class<?> hierarchyType;
+ private final JsonSerializer<?> serializer;
+ private final JsonDeserializer<?> deserializer;
+
+ private 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
+ 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;
+ }
+ }
+}
--- /dev/null
+/*
+ * 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) throws IOException {
+ StringWriter stringWriter = new StringWriter();
+ toJson(stringWriter, value);
+ 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);
+ }
+ }
+}
--- /dev/null
+/*
+ * 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 TypeAdapter.Factory {
+ * 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 TypeAdapter.Factory {
+ * 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);
+}
--- /dev/null
+/*
+ * 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.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 {
+ * @Expose private String firstName;
+ * @Expose(serialize = false) private String lastName;
+ * @Expose (serialize = false, deserialize = false) private String emailAddress;
+ * private String password;
+ * }
+ * </pre></p>
+ * 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
+ */
+@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;
+}
--- /dev/null
+/*
+ * 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.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 SomeClassWithFields {
+ * @SerializedName("name") private final String someField;
+ * private final String someOtherField;
+ *
+ * public SomeClassWithFields(String a, String b) {
+ * this.someField = a;
+ * this.someOtherField = b;
+ * }
+ * }
+ * </pre>
+ *
+ * <p>The following shows the output that is generated when serializing an instance of the
+ * above example class:</p>
+ * <pre>
+ * SomeClassWithFields objectToSerialize = new SomeClassWithFields("a", "b");
+ * Gson gson = new Gson();
+ * String jsonRepresentation = gson.toJson(objectToSerialize);
+ * System.out.println(jsonRepresentation);
+ *
+ * ===== OUTPUT =====
+ * {"name":"a","someOtherField":"b"}
+ * </pre>
+ *
+ * <p>NOTE: The value you specify in this annotation must be a valid JSON field name.</p>
+ *
+ * @see com.google.gson.FieldNamingPolicy
+ *
+ * @author Inderjeet Singh
+ * @author Joel Leitch
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.FIELD)
+public @interface SerializedName {
+
+ /**
+ * @return the desired name of the field when it is serialized
+ */
+ String value();
+}
--- /dev/null
+/*
+ * 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.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;
+ * @Since(1.0) private String emailAddress;
+ * @Since(1.0) private String password;
+ * @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
+ */
+@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();
+}
--- /dev/null
+/*
+ * 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.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;
+ * @Until(1.1) private String emailAddress;
+ * @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
+ */
+@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();
+}
--- /dev/null
+/**
+ * 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
--- /dev/null
+/*\r
+ * Copyright (C) 2008 Google Inc.\r
+ *\r
+ * Licensed under the Apache License, Version 2.0 (the "License");\r
+ * you may not use this file except in compliance with the License.\r
+ * You may obtain a copy of the License at\r
+ *\r
+ * http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ */\r
+\r
+package com.google.gson.internal;\r
+\r
+/**\r
+ * A simple utility class used to check method Preconditions.\r
+ *\r
+ * <pre>\r
+ * public long divideBy(long value) {\r
+ * Preconditions.checkArgument(value != 0);\r
+ * return this.value / value;\r
+ * }\r
+ * </pre>\r
+ *\r
+ * @author Inderjeet Singh\r
+ * @author Joel Leitch\r
+ */\r
+public final class $Gson$Preconditions {\r
+ public static <T> T checkNotNull(T obj) {\r
+ if (obj == null) {\r
+ throw new NullPointerException();\r
+ }\r
+ return obj;\r
+ }\r
+\r
+ public static void checkArgument(boolean condition) {\r
+ if (!condition) {\r
+ throw new IllegalArgumentException();\r
+ }\r
+ }\r
+}\r
--- /dev/null
+/**
+ * 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 static com.google.gson.internal.$Gson$Preconditions.checkArgument;
+import static com.google.gson.internal.$Gson$Preconditions.checkNotNull;
+
+import java.io.Serializable;
+import java.lang.reflect.Array;
+import java.lang.reflect.GenericArrayType;
+import java.lang.reflect.GenericDeclaration;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.lang.reflect.TypeVariable;
+import java.lang.reflect.WildcardType;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Map;
+import java.util.NoSuchElementException;
+import java.util.Properties;
+
+/**
+ * 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() {}
+
+ /**
+ * Returns a new parameterized type, applying {@code typeArguments} to
+ * {@code rawType} and enclosed by {@code ownerType}.
+ *
+ * @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}.
+ */
+ public static WildcardType subtypeOf(Type bound) {
+ return new WildcardTypeImpl(new Type[] { bound }, 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}.
+ */
+ public static WildcardType supertypeOf(Type bound) {
+ return new WildcardTypeImpl(new Type[] { Object.class }, new Type[] { bound });
+ }
+
+ /**
+ * 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}.
+ */
+ 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));
+ }
+
+ /**
+ * Returns 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;
+ }
+ }
+
+ private 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>}.
+ */
+ 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 supertype a superclass of, or interface implemented by, this.
+ */
+ static Type getSupertype(Type context, Class<?> contextRawType, Class<?> supertype) {
+ 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.
+ */
+ 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.
+ */
+ 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.
+ */
+ 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) {
+ // 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;
+ 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);
+ 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);
+ 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);
+ 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]);
+ 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]);
+ if (lowerBound != originalLowerBound[0]) {
+ return supertypeOf(lowerBound);
+ }
+ } else if (originalUpperBound.length == 1) {
+ Type upperBound = resolve(context, contextRawType, originalUpperBound[0]);
+ 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; i < array.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.
+ */
+ private static Class<?> declaringClassOf(TypeVariable<?> typeVariable) {
+ GenericDeclaration genericDeclaration = typeVariable.getGenericDeclaration();
+ return genericDeclaration instanceof Class
+ ? (Class<?>) genericDeclaration
+ : null;
+ }
+
+ private 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;
+ checkArgument(ownerType != null || rawTypeAsClass.getEnclosingClass() == null);
+ checkArgument(ownerType == null || rawTypeAsClass.getEnclosingClass() != null);
+ }
+
+ this.ownerType = ownerType == null ? null : canonicalize(ownerType);
+ this.rawType = canonicalize(rawType);
+ this.typeArguments = typeArguments.clone();
+ for (int t = 0; t < this.typeArguments.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() {
+ StringBuilder stringBuilder = new StringBuilder(30 * (typeArguments.length + 1));
+ stringBuilder.append(typeToString(rawType));
+
+ if (typeArguments.length == 0) {
+ return stringBuilder.toString();
+ }
+
+ stringBuilder.append("<").append(typeToString(typeArguments[0]));
+ for (int i = 1; i < typeArguments.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;
+ }
+}
--- /dev/null
+/*
+ * 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.InstanceCreator;
+import com.google.gson.JsonIOException;
+import com.google.gson.reflect.TypeToken;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.EnumSet;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
+import java.util.LinkedList;
+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;
+
+/**
+ * Returns a function that can construct an instance of a requested type.
+ */
+public final class ConstructorConstructor {
+ private final Map<Type, InstanceCreator<?>> instanceCreators;
+
+ 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>() {
+ 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>() {
+ 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()) {
+ constructor.setAccessible(true);
+ }
+ return new ObjectConstructor<T>() {
+ @SuppressWarnings("unchecked") // T is the same raw type as is requested
+ 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
+ * subytpes.
+ */
+ @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>() {
+ public T construct() {
+ return (T) new TreeSet<Object>();
+ }
+ };
+ } else if (EnumSet.class.isAssignableFrom(rawType)) {
+ return new ObjectConstructor<T>() {
+ @SuppressWarnings("rawtypes")
+ 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>() {
+ public T construct() {
+ return (T) new LinkedHashSet<Object>();
+ }
+ };
+ } else if (Queue.class.isAssignableFrom(rawType)) {
+ return new ObjectConstructor<T>() {
+ public T construct() {
+ return (T) new LinkedList<Object>();
+ }
+ };
+ } else {
+ return new ObjectConstructor<T>() {
+ public T construct() {
+ return (T) new ArrayList<Object>();
+ }
+ };
+ }
+ }
+
+ if (Map.class.isAssignableFrom(rawType)) {
+ if (SortedMap.class.isAssignableFrom(rawType)) {
+ return new ObjectConstructor<T>() {
+ 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>() {
+ public T construct() {
+ return (T) new LinkedHashMap<Object, Object>();
+ }
+ };
+ } else {
+ return new ObjectConstructor<T>() {
+ 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")
+ 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 + ". "
+ + "Register an InstanceCreator with Gson for this type may fix this problem."), e);
+ }
+ }
+ };
+ }
+
+ @Override public String toString() {
+ return instanceCreators.toString();
+ }
+}
--- /dev/null
+/*
+ * 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();
+ }
+ }
+
+ 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();
+ final boolean skipSerialize = excludeClass(rawType, true);
+ final boolean skipDeserialize = excludeClass(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;
+ }
+
+ public boolean excludeClass(Class<?> clazz, boolean serialize) {
+ 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;
+ }
+
+ 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;
+ }
+}
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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;
+
+ 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);
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * 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;
+
+ 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();
+ }
+ }
+
+ 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);
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Contains static utility methods pertaining to primitive types and their
+ * corresponding wrapper types.
+ *
+ * @author Kevin Bourrillion
+ */
+public final class Primitives {
+ private Primitives() {}
+
+ /** A map from primitive types to their corresponding wrapper types. */
+ private static final Map<Class<?>, Class<?>> PRIMITIVE_TO_WRAPPER_TYPE;
+
+ /** A map from wrapper types to their corresponding primitive types. */
+ private static final Map<Class<?>, Class<?>> WRAPPER_TO_PRIMITIVE_TYPE;
+
+ // Sad that we can't use a BiMap. :(
+
+ static {
+ Map<Class<?>, Class<?>> primToWrap = new HashMap<Class<?>, Class<?>>(16);
+ Map<Class<?>, Class<?>> wrapToPrim = new HashMap<Class<?>, Class<?>>(16);
+
+ add(primToWrap, wrapToPrim, boolean.class, Boolean.class);
+ add(primToWrap, wrapToPrim, byte.class, Byte.class);
+ add(primToWrap, wrapToPrim, char.class, Character.class);
+ add(primToWrap, wrapToPrim, double.class, Double.class);
+ add(primToWrap, wrapToPrim, float.class, Float.class);
+ add(primToWrap, wrapToPrim, int.class, Integer.class);
+ add(primToWrap, wrapToPrim, long.class, Long.class);
+ add(primToWrap, wrapToPrim, short.class, Short.class);
+ add(primToWrap, wrapToPrim, void.class, Void.class);
+
+ PRIMITIVE_TO_WRAPPER_TYPE = Collections.unmodifiableMap(primToWrap);
+ WRAPPER_TO_PRIMITIVE_TYPE = Collections.unmodifiableMap(wrapToPrim);
+ }
+
+ private static void add(Map<Class<?>, Class<?>> forward,
+ Map<Class<?>, Class<?>> backward, Class<?> key, Class<?> value) {
+ forward.put(key, value);
+ backward.put(value, key);
+ }
+
+ /**
+ * Returns true if this type is a primitive.
+ */
+ public static boolean isPrimitive(Type type) {
+ return PRIMITIVE_TO_WRAPPER_TYPE.containsKey(type);
+ }
+
+ /**
+ * 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 WRAPPER_TO_PRIMITIVE_TYPE.containsKey(
+ $Gson$Preconditions.checkNotNull(type));
+ }
+
+ /**
+ * 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>
+ */
+ public static <T> Class<T> wrap(Class<T> type) {
+ // cast is safe: long.class and Long.class are both of type Class<Long>
+ @SuppressWarnings("unchecked")
+ Class<T> wrapped = (Class<T>) PRIMITIVE_TO_WRAPPER_TYPE.get(
+ $Gson$Preconditions.checkNotNull(type));
+ return (wrapped == null) ? type : wrapped;
+ }
+
+ /**
+ * 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>
+ */
+ public static <T> Class<T> unwrap(Class<T> type) {
+ // cast is safe: long.class and Long.class are both of type Class<Long>
+ @SuppressWarnings("unchecked")
+ Class<T> unwrapped = (Class<T>) WRAPPER_TO_PRIMITIVE_TYPE.get(
+ $Gson$Preconditions.checkNotNull(type));
+ return (unwrapped == null) ? type : unwrapped;
+ }
+}
--- /dev/null
+/*
+ * 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 {
+ /**
+ * 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();
+
+ private 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);
+ }
+ }
+ }
+
+}
--- /dev/null
+/*
+ * 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;
+
+/**
+ * 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 {
+ return (T) allocateInstance.invoke(unsafe, c);
+ }
+ };
+ } 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 {
+ return (T) newInstance.invoke(null, c, Object.class);
+ }
+ };
+ } 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 {
+ return (T) newInstance.invoke(null, c, constructorId);
+ }
+ };
+ } catch (Exception ignored) {
+ }
+
+ // give up
+ return new UnsafeAllocator() {
+ @Override
+ public <T> T newInstance(Class<T> c) {
+ throw new UnsupportedOperationException("Cannot allocate " + c);
+ }
+ };
+ }
+}
--- /dev/null
+/*
+ * 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"})
+ 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;
+ }
+
+ 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();
+ Object array = Array.newInstance(componentType, list.size());
+ for (int i = 0; i < list.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();
+ }
+}
--- /dev/null
+/*
+ * 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;
+ }
+
+ 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;
+ }
+
+ 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;
+ }
+
+ 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();
+ }
+ }
+}
--- /dev/null
+/*
+ * 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;
+import java.util.Date;
+import java.util.Locale;
+import java.util.TimeZone;
+
+/**
+ * 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
+ public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) {
+ return typeToken.getRawType() == Date.class ? (TypeAdapter<T>) new DateTypeAdapter() : null;
+ }
+ };
+
+ private final DateFormat enUsFormat
+ = DateFormat.getDateTimeInstance(DateFormat.DEFAULT, DateFormat.DEFAULT, Locale.US);
+ private final DateFormat localFormat
+ = DateFormat.getDateTimeInstance(DateFormat.DEFAULT, DateFormat.DEFAULT);
+ private final DateFormat iso8601Format = buildIso8601Format();
+
+ private static DateFormat buildIso8601Format() {
+ DateFormat iso8601Format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.US);
+ iso8601Format.setTimeZone(TimeZone.getTimeZone("UTC"));
+ return iso8601Format;
+ }
+
+ @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) {
+ try {
+ return localFormat.parse(json);
+ } catch (ParseException ignored) {
+ }
+ try {
+ return enUsFormat.parse(json);
+ } catch (ParseException ignored) {
+ }
+ try {
+ return iso8601Format.parse(json);
+ } 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 = enUsFormat.format(value);
+ out.value(dateFormatAsString);
+ }
+}
--- /dev/null
+/*
+ * 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.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 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();
+
+ private final List<Object> stack = new ArrayList<Object>();
+
+ public JsonTreeReader(JsonElement element) {
+ super(UNREADABLE_READER);
+ stack.add(element);
+ }
+
+ @Override public void beginArray() throws IOException {
+ expect(JsonToken.BEGIN_ARRAY);
+ JsonArray array = (JsonArray) peekStack();
+ stack.add(array.iterator());
+ }
+
+ @Override public void endArray() throws IOException {
+ expect(JsonToken.END_ARRAY);
+ popStack(); // empty iterator
+ popStack(); // array
+ }
+
+ @Override public void beginObject() throws IOException {
+ expect(JsonToken.BEGIN_OBJECT);
+ JsonObject object = (JsonObject) peekStack();
+ stack.add(object.entrySet().iterator());
+ }
+
+ @Override public void endObject() throws IOException {
+ expect(JsonToken.END_OBJECT);
+ popStack(); // empty iterator
+ popStack(); // object
+ }
+
+ @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 (stack.isEmpty()) {
+ return JsonToken.END_DOCUMENT;
+ }
+
+ Object o = peekStack();
+ if (o instanceof Iterator) {
+ boolean isObject = stack.get(stack.size() - 2) instanceof JsonObject;
+ Iterator<?> iterator = (Iterator<?>) o;
+ if (iterator.hasNext()) {
+ if (isObject) {
+ return JsonToken.NAME;
+ } else {
+ stack.add(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.get(stack.size() - 1);
+ }
+
+ private Object popStack() {
+ return stack.remove(stack.size() - 1);
+ }
+
+ private void expect(JsonToken expected) throws IOException {
+ if (peek() != expected) {
+ throw new IllegalStateException("Expected " + expected + " but was " + peek());
+ }
+ }
+
+ @Override public String nextName() throws IOException {
+ expect(JsonToken.NAME);
+ Iterator<?> i = (Iterator<?>) peekStack();
+ Map.Entry<?, ?> entry = (Map.Entry<?, ?>) i.next();
+ stack.add(entry.getValue());
+ return (String) entry.getKey();
+ }
+
+ @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);
+ }
+ return ((JsonPrimitive) popStack()).getAsString();
+ }
+
+ @Override public boolean nextBoolean() throws IOException {
+ expect(JsonToken.BOOLEAN);
+ return ((JsonPrimitive) popStack()).getAsBoolean();
+ }
+
+ @Override public void nextNull() throws IOException {
+ expect(JsonToken.NULL);
+ popStack();
+ }
+
+ @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);
+ }
+ double result = ((JsonPrimitive) peekStack()).getAsDouble();
+ if (!isLenient() && (Double.isNaN(result) || Double.isInfinite(result))) {
+ throw new NumberFormatException("JSON forbids NaN and infinities: " + result);
+ }
+ popStack();
+ 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);
+ }
+ long result = ((JsonPrimitive) peekStack()).getAsLong();
+ popStack();
+ 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);
+ }
+ int result = ((JsonPrimitive) peekStack()).getAsInt();
+ popStack();
+ return result;
+ }
+
+ @Override public void close() throws IOException {
+ stack.clear();
+ stack.add(SENTINEL_CLOSED);
+ }
+
+ @Override public void skipValue() throws IOException {
+ if (peek() == JsonToken.NAME) {
+ nextName();
+ } else {
+ popStack();
+ }
+ }
+
+ @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();
+ stack.add(entry.getValue());
+ stack.add(new JsonPrimitive((String)entry.getKey()));
+ }
+}
--- /dev/null
+/*
+ * 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(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);
+ }
+}
--- /dev/null
+/*
+ * 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;
+ private final boolean complexMapKeySerialization;
+
+ public MapTypeAdapterFactory(ConstructorConstructor constructorConstructor,
+ boolean complexMapKeySerialization) {
+ this.constructorConstructor = constructorConstructor;
+ this.complexMapKeySerialization = complexMapKeySerialization;
+ }
+
+ 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;
+ }
+
+ 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;
+ }
+
+ 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; i < keys.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; i < keys.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();
+ }
+ }
+ }
+}
--- /dev/null
+/*
+ * 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")
+ 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;
+
+ private 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);
+ }
+}
--- /dev/null
+/*
+ * 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.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.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.LinkedHashMap;
+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;
+
+ public ReflectiveTypeAdapterFactory(ConstructorConstructor constructorConstructor,
+ FieldNamingStrategy fieldNamingPolicy, Excluder excluder) {
+ this.constructorConstructor = constructorConstructor;
+ this.fieldNamingPolicy = fieldNamingPolicy;
+ this.excluder = excluder;
+ }
+
+ public boolean excludeField(Field f, boolean serialize) {
+ return !excluder.excludeClass(f.getType(), serialize) && !excluder.excludeField(f, serialize);
+ }
+
+ private String getFieldName(Field f) {
+ SerializedName serializedName = f.getAnnotation(SerializedName.class);
+ return serializedName == null ? fieldNamingPolicy.translateName(f) : serializedName.value();
+ }
+
+ 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...
+ return new ReflectiveTypeAdapterFactory.BoundField(name, serialize, deserialize) {
+ final TypeAdapter<?> typeAdapter = context.getAdapter(fieldType);
+ @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 =
+ new TypeAdapterRuntimeTypeWrapper(context, this.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);
+ }
+ }
+ };
+ }
+
+ 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;
+ }
+ field.setAccessible(true);
+ Type fieldType = $Gson$Types.resolve(type.getType(), raw, field.getGenericType());
+ BoundField boundField = createBoundField(context, field, getFieldName(field),
+ TypeToken.get(fieldType), serialize, deserialize);
+ BoundField previous = result.put(boundField.name, boundField);
+ 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 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;
+
+ private 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.serialized) {
+ out.name(boundField.name);
+ boundField.write(out, value);
+ }
+ }
+ } catch (IllegalAccessException e) {
+ throw new AssertionError();
+ }
+ out.endObject();
+ }
+ }
+}
--- /dev/null
+/*
+ * 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
+ 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));
+ }
+}
--- /dev/null
+/*
+ * 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
+ 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));
+ }
+}
--- /dev/null
+/*\r
+ * Copyright (C) 2011 Google Inc.\r
+ *\r
+ * Licensed under the Apache License, Version 2.0 (the "License");\r
+ * you may not use this file except in compliance with the License.\r
+ * You may obtain a copy of the License at\r
+ *\r
+ * http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ */\r
+package com.google.gson.internal.bind;\r
+\r
+import com.google.gson.Gson;\r
+import com.google.gson.TypeAdapter;\r
+import com.google.gson.reflect.TypeToken;\r
+import com.google.gson.stream.JsonReader;\r
+import com.google.gson.stream.JsonWriter;\r
+import java.io.IOException;\r
+import java.lang.reflect.Type;\r
+import java.lang.reflect.TypeVariable;\r
+\r
+final class TypeAdapterRuntimeTypeWrapper<T> extends TypeAdapter<T> {\r
+ private final Gson context;\r
+ private final TypeAdapter<T> delegate;\r
+ private final Type type;\r
+\r
+ TypeAdapterRuntimeTypeWrapper(Gson context, TypeAdapter<T> delegate, Type type) {\r
+ this.context = context;\r
+ this.delegate = delegate;\r
+ this.type = type;\r
+ }\r
+\r
+ @Override\r
+ public T read(JsonReader in) throws IOException {\r
+ return delegate.read(in);\r
+ }\r
+\r
+ @SuppressWarnings({"rawtypes", "unchecked"})\r
+ @Override\r
+ public void write(JsonWriter out, T value) throws IOException {\r
+ // Order of preference for choosing type adapters\r
+ // First preference: a type adapter registered for the runtime type\r
+ // Second preference: a type adapter registered for the declared type\r
+ // Third preference: reflective type adapter for the runtime type (if it is a sub class of the declared type)\r
+ // Fourth preference: reflective type adapter for the declared type\r
+\r
+ TypeAdapter chosen = delegate;\r
+ Type runtimeType = getRuntimeTypeIfMoreSpecific(type, value);\r
+ if (runtimeType != type) {\r
+ TypeAdapter runtimeTypeAdapter = context.getAdapter(TypeToken.get(runtimeType));\r
+ if (!(runtimeTypeAdapter instanceof ReflectiveTypeAdapterFactory.Adapter)) {\r
+ // The user registered a type adapter for the runtime type, so we will use that\r
+ chosen = runtimeTypeAdapter;\r
+ } else if (!(delegate instanceof ReflectiveTypeAdapterFactory.Adapter)) {\r
+ // The user registered a type adapter for Base class, so we prefer it over the\r
+ // reflective type adapter for the runtime type\r
+ chosen = delegate;\r
+ } else {\r
+ // Use the type adapter for runtime type\r
+ chosen = runtimeTypeAdapter;\r
+ }\r
+ }\r
+ chosen.write(out, value);\r
+ }\r
+\r
+ /**\r
+ * Finds a compatible runtime type if it is more specific\r
+ */\r
+ private Type getRuntimeTypeIfMoreSpecific(Type type, Object value) {\r
+ if (value != null\r
+ && (type == Object.class || type instanceof TypeVariable<?> || type instanceof Class<?>)) {\r
+ type = value.getClass();\r
+ }\r
+ return type;\r
+ }\r
+}\r
--- /dev/null
+/*
+ * 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.TypeAdapterFactory;
+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.BitSet;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.GregorianCalendar;
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Map;
+import java.util.StringTokenizer;
+import java.util.UUID;
+
+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.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() {}
+
+ @SuppressWarnings("rawtypes")
+ public static final TypeAdapter<Class> CLASS = new TypeAdapter<Class>() {
+ @Override
+ public void write(JsonWriter out, Class value) throws IOException {
+ if (value == null) {
+ out.nullValue();
+ } else {
+ 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 {
+ if (in.peek() == JsonToken.NULL) {
+ in.nextNull();
+ return null;
+ } else {
+ throw new UnsupportedOperationException(
+ "Attempted to deserialize a java.lang.Class. Forgot to register a type adapter?");
+ }
+ }
+ };
+ public static final TypeAdapterFactory CLASS_FACTORY = newFactory(Class.class, CLASS);
+
+ public static final TypeAdapter<BitSet> BIT_SET = new TypeAdapter<BitSet>() {
+ public BitSet read(JsonReader in) throws IOException {
+ if (in.peek() == JsonToken.NULL) {
+ in.nextNull();
+ return null;
+ }
+
+ 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;
+ }
+
+ public void write(JsonWriter out, BitSet src) throws IOException {
+ if (src == null) {
+ out.nullValue();
+ return;
+ }
+
+ out.beginArray();
+ for (int i = 0; i < src.length(); i++) {
+ int value = (src.get(i)) ? 1 : 0;
+ out.value(value);
+ }
+ out.endArray();
+ }
+ };
+
+ 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 {
+ if (in.peek() == JsonToken.NULL) {
+ in.nextNull();
+ return null;
+ } else if (in.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 {
+ if (value == null) {
+ out.nullValue();
+ return;
+ }
+ 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<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:
+ 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 TypeAdapterFactory TIMESTAMP_FACTORY = new TypeAdapterFactory() {
+ @SuppressWarnings("unchecked") // we use a runtime check to make sure the 'T's equal
+ 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();
+ }
+ nameToConstant.put(name, constant);
+ constantToName.put(constant, name);
+ }
+ } catch (NoSuchFieldException e) {
+ throw new AssertionError();
+ }
+ }
+ public T read(JsonReader in) throws IOException {
+ if (in.peek() == JsonToken.NULL) {
+ in.nextNull();
+ return null;
+ }
+ return nameToConstant.get(in.nextString());
+ }
+
+ public void write(JsonWriter out, T value) throws IOException {
+ out.value(value == null ? null : constantToName.get(value));
+ }
+ }
+
+ public static final TypeAdapterFactory ENUM_FACTORY = newEnumTypeHierarchyFactory();
+
+ public static TypeAdapterFactory newEnumTypeHierarchyFactory() {
+ return new TypeAdapterFactory() {
+ @SuppressWarnings({"rawtypes", "unchecked"})
+ 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
+ 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
+ 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
+ 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
+ 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 + "]";
+ }
+ };
+ }
+
+ public static <TT> TypeAdapterFactory newTypeHierarchyFactory(
+ final Class<TT> clazz, final TypeAdapter<TT> typeAdapter) {
+ return new TypeAdapterFactory() {
+ @SuppressWarnings("unchecked")
+ public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) {
+ return clazz.isAssignableFrom(typeToken.getRawType()) ? (TypeAdapter<T>) typeAdapter : null;
+ }
+ @Override public String toString() {
+ return "Factory[typeHierarchy=" + clazz.getName() + ",adapter=" + typeAdapter + "]";
+ }
+ };
+ }
+}
--- /dev/null
+/**
+ * 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
--- /dev/null
+/**
+ * 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
--- /dev/null
+/*
+ * 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);
+ }
+}
--- /dev/null
+/**
+ * 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
--- /dev/null
+/*
+ * 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;
+
+/**
+ * Reads a JSON (<a href="http://www.ietf.org/rfc/rfc4627.txt">RFC 4627</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"/><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;
+
+ private 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;
+ }
+
+ /**
+ * 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 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);
+ peeked = PEEKED_NONE;
+ } else {
+ throw new IllegalStateException("Expected BEGIN_ARRAY but was " + peek()
+ + " at line " + getLineNumber() + " column " + getColumnNumber());
+ }
+ }
+
+ /**
+ * 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--;
+ peeked = PEEKED_NONE;
+ } else {
+ throw new IllegalStateException("Expected END_ARRAY but was " + peek()
+ + " at line " + getLineNumber() + " column " + getColumnNumber());
+ }
+ }
+
+ /**
+ * 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()
+ + " at line " + getLineNumber() + " column " + getColumnNumber());
+ }
+ }
+
+ /**
+ * 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--;
+ peeked = PEEKED_NONE;
+ } else {
+ throw new IllegalStateException("Expected END_OBJECT but was " + peek()
+ + " at line " + getLineNumber() + " column " + getColumnNumber());
+ }
+ }
+
+ /**
+ * 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();
+ }
+ }
+
+ private 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 '"':
+ if (stackSize == 1) {
+ checkLenient();
+ }
+ 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.
+ }
+
+ if (stackSize == 1) {
+ checkLenient(); // Top-level value isn't an array or an object.
+ }
+
+ 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)) {
+ 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()
+ + " at line " + getLineNumber() + " column " + getColumnNumber());
+ }
+ peeked = PEEKED_NONE;
+ 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()
+ + " at line " + getLineNumber() + " column " + getColumnNumber());
+ }
+ peeked = PEEKED_NONE;
+ 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;
+ return true;
+ } else if (p == PEEKED_FALSE) {
+ peeked = PEEKED_NONE;
+ return false;
+ }
+ throw new IllegalStateException("Expected a boolean but was " + peek()
+ + " at line " + getLineNumber() + " column " + getColumnNumber());
+ }
+
+ /**
+ * 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;
+ } else {
+ throw new IllegalStateException("Expected null but was " + peek()
+ + " at line " + getLineNumber() + " column " + getColumnNumber());
+ }
+ }
+
+ /**
+ * 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;
+ 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()
+ + " at line " + getLineNumber() + " column " + getColumnNumber());
+ }
+
+ 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
+ + " at line " + getLineNumber() + " column " + getColumnNumber());
+ }
+ peekedString = null;
+ peeked = PEEKED_NONE;
+ 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;
+ return 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 ? '\'' : '"');
+ try {
+ long result = Long.parseLong(peekedString);
+ peeked = PEEKED_NONE;
+ return result;
+ } catch (NumberFormatException ignored) {
+ // Fall back to parse as a double below.
+ }
+ } else {
+ throw new IllegalStateException("Expected a long but was " + peek()
+ + " at line " + getLineNumber() + " column " + getColumnNumber());
+ }
+
+ 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
+ + " at line " + getLineNumber() + " column " + getColumnNumber());
+ }
+ peekedString = null;
+ peeked = PEEKED_NONE;
+ 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 = new StringBuilder();
+ 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;
+ builder.append(buffer, start, p - start - 1);
+ return builder.toString();
+ } else if (c == '\\') {
+ pos = p;
+ builder.append(buffer, start, p - start - 1);
+ builder.append(readEscapeCharacter());
+ p = pos;
+ l = limit;
+ start = p;
+ } else if (c == '\n') {
+ lineNumber++;
+ lineStart = p;
+ }
+ }
+
+ 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();
+ }
+ builder.append(buffer, pos, i);
+ pos += i;
+ i = 0;
+ if (!fillBuffer(1)) {
+ break;
+ }
+ }
+
+ String result;
+ if (builder == null) {
+ result = new String(buffer, pos, i);
+ } else {
+ builder.append(buffer, pos, i);
+ result = builder.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
+ + " at line " + getLineNumber() + " column " + getColumnNumber());
+ }
+ peeked = PEEKED_NONE;
+ return result;
+ }
+
+ 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 ? '\'' : '"');
+ try {
+ result = Integer.parseInt(peekedString);
+ peeked = PEEKED_NONE;
+ return result;
+ } catch (NumberFormatException ignored) {
+ // Fall back to parse as a double below.
+ }
+ } else {
+ throw new IllegalStateException("Expected an int but was " + peek()
+ + " at line " + getLineNumber() + " column " + getColumnNumber());
+ }
+
+ 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
+ + " at line " + getLineNumber() + " column " + getColumnNumber());
+ }
+ peekedString = null;
+ peeked = PEEKED_NONE;
+ 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);
+ }
+
+ private void push(int newTop) {
+ if (stackSize == stack.length) {
+ int[] newStack = new int[stackSize * 2];
+ System.arraycopy(stack, 0, newStack, 0, stackSize);
+ stack = newStack;
+ }
+ 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;
+ }
+
+ private int getLineNumber() {
+ return lineNumber + 1;
+ }
+
+ private int getColumnNumber() {
+ return pos - lineStart + 1;
+ }
+
+ /**
+ * 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"
+ + " at line " + getLineNumber() + " column " + getColumnNumber());
+ } 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 {
+ outer:
+ for (; pos + toFind.length() <= limit || fillBuffer(toFind.length()); pos++) {
+ if (buffer[pos] == '\n') {
+ lineNumber++;
+ lineStart = pos + 1;
+ continue;
+ }
+ for (int c = 0; c < toFind.length(); c++) {
+ if (buffer[pos + c] != toFind.charAt(c)) {
+ continue outer;
+ }
+ }
+ return true;
+ }
+ return false;
+ }
+
+ @Override public String toString() {
+ return getClass().getSimpleName()
+ + " at line " + getLineNumber() + " column " + getColumnNumber();
+ }
+
+ /**
+ * 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 '\\':
+ default:
+ return escaped;
+ }
+ }
+
+ /**
+ * 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
+ + " at line " + getLineNumber() + " column " + getColumnNumber());
+ }
+
+ /**
+ * 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() + " "
+ + " at line " + reader.getLineNumber() + " column " + reader.getColumnNumber());
+ }
+ }
+ };
+ }
+}
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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
+}
--- /dev/null
+/*
+ * 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 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/rfc4627.txt">RFC 4627</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.setIndentSpaces(4);
+ * 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 4627, "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/rfc4627.txt">RFC 4627</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, String openBracket) throws IOException {
+ beforeValue(true);
+ 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, String 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) {
+ int[] newStack = new int[stackSize * 2];
+ System.arraycopy(stack, 0, newStack, 0, stackSize);
+ stack = newStack;
+ }
+ 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(false);
+ string(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(false);
+ out.write("null");
+ return this;
+ }
+
+ /**
+ * Encodes {@code value}.
+ *
+ * @return this writer.
+ */
+ public JsonWriter value(boolean value) throws IOException {
+ writeDeferredName();
+ beforeValue(false);
+ 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 {
+ if (Double.isNaN(value) || Double.isInfinite(value)) {
+ throw new IllegalArgumentException("Numeric values must be finite, but was " + value);
+ }
+ writeDeferredName();
+ beforeValue(false);
+ out.append(Double.toString(value));
+ return this;
+ }
+
+ /**
+ * Encodes {@code value}.
+ *
+ * @return this writer.
+ */
+ public JsonWriter value(long value) throws IOException {
+ writeDeferredName();
+ beforeValue(false);
+ 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(false);
+ 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.
+ *
+ * @param root true if the value is a new array or object, the two values
+ * permitted as top-level elements.
+ */
+ @SuppressWarnings("fallthrough")
+ private void beforeValue(boolean root) 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
+ if (!lenient && !root) {
+ throw new IllegalStateException(
+ "JSON must start with an array or an object.");
+ }
+ 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.");
+ }
+ }
+}
--- /dev/null
+/*
+ * 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);
+ }
+}
+++ /dev/null
-/*
- * 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.text.ParseException;
-import java.text.SimpleDateFormat;
-import java.util.Date;
-import java.util.Locale;
-import java.util.TimeZone;
-
-/**
- * This type adapter supports three subclasses of date: Date, Timestamp, and
- * java.sql.Date.
- *
- * @author Inderjeet Singh
- * @author Joel Leitch
- */
-final class DefaultDateTypeAdapter implements JsonSerializer<Date>, JsonDeserializer<Date> {
-
- // TODO: migrate to streaming adapter
-
- private final DateFormat enUsFormat;
- private final DateFormat localFormat;
- private final DateFormat iso8601Format;
-
- DefaultDateTypeAdapter() {
- this(DateFormat.getDateTimeInstance(DateFormat.DEFAULT, DateFormat.DEFAULT, Locale.US),
- DateFormat.getDateTimeInstance(DateFormat.DEFAULT, DateFormat.DEFAULT));
- }
-
- DefaultDateTypeAdapter(String datePattern) {
- this(new SimpleDateFormat(datePattern, Locale.US), new SimpleDateFormat(datePattern));
- }
-
- DefaultDateTypeAdapter(int style) {
- this(DateFormat.getDateInstance(style, Locale.US), DateFormat.getDateInstance(style));
- }
-
- public DefaultDateTypeAdapter(int dateStyle, int timeStyle) {
- this(DateFormat.getDateTimeInstance(dateStyle, timeStyle, Locale.US),
- DateFormat.getDateTimeInstance(dateStyle, timeStyle));
- }
-
- DefaultDateTypeAdapter(DateFormat enUsFormat, DateFormat localFormat) {
- this.enUsFormat = enUsFormat;
- this.localFormat = localFormat;
- this.iso8601Format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.US);
- this.iso8601Format.setTimeZone(TimeZone.getTimeZone("UTC"));
- }
-
- // These methods need to be synchronized since JDK DateFormat classes are not thread-safe
- // See issue 162
- public JsonElement serialize(Date src, Type typeOfSrc, JsonSerializationContext context) {
- synchronized (localFormat) {
- String dateFormatAsString = enUsFormat.format(src);
- return new JsonPrimitive(dateFormatAsString);
- }
- }
-
- public Date deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
- throws JsonParseException {
- if (!(json instanceof JsonPrimitive)) {
- throw new JsonParseException("The date should be a string value");
- }
- Date date = deserializeToDate(json);
- if (typeOfT == Date.class) {
- return date;
- } else if (typeOfT == Timestamp.class) {
- return new Timestamp(date.getTime());
- } else if (typeOfT == java.sql.Date.class) {
- return new java.sql.Date(date.getTime());
- } else {
- throw new IllegalArgumentException(getClass() + " cannot deserialize to " + typeOfT);
- }
- }
-
- private Date deserializeToDate(JsonElement json) {
- synchronized (localFormat) {
- try {
- return localFormat.parse(json.getAsString());
- } catch (ParseException ignored) {
- }
- try {
- return enUsFormat.parse(json.getAsString());
- } catch (ParseException ignored) {
- }
- try {
- return iso8601Format.parse(json.getAsString());
- } catch (ParseException e) {
- throw new JsonSyntaxException(json.getAsString(), e);
- }
- }
- }
-
- @Override
- public String toString() {
- StringBuilder sb = new StringBuilder();
- sb.append(DefaultDateTypeAdapter.class.getSimpleName());
- sb.append('(').append(localFormat.getClass().getSimpleName()).append(')');
- return sb.toString();
- }
-}
+++ /dev/null
-/*
- * 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 false then that class or field type
- * will not be part of the JSON output. For deserialization, if {@link #shouldSkipClass(Class)}
- * returns false, 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<?> excludedThisClass;
- *
- * public SpecificClassExclusionStrategy(Class<?> excludedThisClass) {
- * this.excludedThisClass = excludedThisClass;
- * }
- *
- * public boolean shouldSkipClass(Class<?> 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 @interface FooAnnotation {
- * // some implementation here
- * }
- *
- * // Excludes any field (or class) that is tagged with an "@FooAnnotation"
- * private static class FooAnnotationExclusionStrategy implements ExclusionStrategy {
- * public boolean shouldSkipClass(Class<?> 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);
-}
+++ /dev/null
-/*
- * 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<String> red;
- * }
- *
- * Type listParmeterizedType = new TypeToken<List<String>>() {}.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<String> 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;
- }
-
- /**
- * This is exposed internally only for the removing synthetic fields from the JSON output.
- *
- * @return true if the field is synthetic; otherwise false
- * @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();
- }
-}
+++ /dev/null
-/*
- * 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() {
- 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" ---> "JSON Field Name":</p>
- * <ul>
- * <li>someFieldName ---> SomeFieldName</li>
- * <li>_someFieldName ---> _SomeFieldName</li>
- * </ul>
- */
- UPPER_CAMEL_CASE() {
- 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" ---> "JSON Field Name":</p>
- * <ul>
- * <li>someFieldName ---> Some Field Name</li>
- * <li>_someFieldName ---> _Some Field Name</li>
- * </ul>
- *
- * @since 1.4
- */
- UPPER_CAMEL_CASE_WITH_SPACES() {
- 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" ---> "JSON Field Name":</p>
- * <ul>
- * <li>someFieldName ---> some_field_name</li>
- * <li>_someFieldName ---> _some_field_name</li>
- * <li>aStringField ---> a_string_field</li>
- * <li>aURL ---> a_u_r_l</li>
- * </ul>
- */
- LOWER_CASE_WITH_UNDERSCORES() {
- 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" ---> "JSON Field Name":</p>
- * <ul>
- * <li>someFieldName ---> some-field-name</li>
- * <li>_someFieldName ---> _some-field-name</li>
- * <li>aStringField ---> a-string-field</li>
- * <li>aURL ---> 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() {
- 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}.
- */
- private static String separateCamelCase(String name, String separator) {
- StringBuilder translation = new StringBuilder();
- for (int i = 0; i < name.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.
- */
- private static String upperCaseFirstLetter(String name) {
- StringBuilder fieldNameBuilder = new StringBuilder();
- int index = 0;
- char firstCharacter = name.charAt(index);
-
- while (index < name.length() - 1) {
- if (Character.isLetter(firstCharacter)) {
- break;
- }
-
- fieldNameBuilder.append(firstCharacter);
- firstCharacter = name.charAt(++index);
- }
-
- if (index == name.length()) {
- return fieldNameBuilder.toString();
- }
-
- if (!Character.isUpperCase(firstCharacter)) {
- String modifiedTarget = modifyString(Character.toUpperCase(firstCharacter), name, ++index);
- return fieldNameBuilder.append(modifiedTarget).toString();
- } else {
- return name;
- }
- }
-
- private static String modifyString(char firstCharacter, String srcString, int indexOfSubstring) {
- return (indexOfSubstring < srcString.length())
- ? firstCharacter + srcString.substring(indexOfSubstring)
- : String.valueOf(firstCharacter);
- }
-}
\ No newline at end of file
+++ /dev/null
-/*
- * 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);
-}
+++ /dev/null
-/*
- * 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.ConstructorConstructor;
-import com.google.gson.internal.Excluder;
-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.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;
-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.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-/**
- * 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.
- *
- * <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>
- *
- * <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 deserialing a {@code ParameterizedType}:
- *
- * <pre>
- * Type listType = new TypeToken<List<String>>() {}.getType();
- * List<String> target = new LinkedList<String>();
- * target.add("blah");
- *
- * Gson gson = new Gson();
- * String json = gson.toJson(target, listType);
- * List<String> target2 = gson.fromJson(json, listType);
- * </pre></p>
- *
- * <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;
-
- 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
- = Collections.synchronizedMap(new HashMap<TypeToken<?>, TypeAdapter<?>>());
-
- private final List<TypeAdapterFactory> factories;
- private final ConstructorConstructor constructorConstructor;
-
- private final boolean serializeNulls;
- private final boolean htmlSafe;
- private final boolean generateNonExecutableJson;
- private final boolean prettyPrinting;
-
- final JsonDeserializationContext deserializationContext = new JsonDeserializationContext() {
- @SuppressWarnings("unchecked")
- public <T> T deserialize(JsonElement json, Type typeOfT) throws JsonParseException {
- return (T) fromJson(json, typeOfT);
- }
- };
-
- final JsonSerializationContext serializationContext = new JsonSerializationContext() {
- public JsonElement serialize(Object src) {
- return toJsonTree(src);
- }
- public JsonElement serialize(Object src, Type typeOfSrc) {
- return toJsonTree(src, typeOfSrc);
- }
- };
-
- /**
- * 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>"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(), false, false, DEFAULT_JSON_NON_EXECUTABLE,
- true, false, false, LongSerializationPolicy.DEFAULT,
- Collections.<TypeAdapterFactory>emptyList());
- }
-
- Gson(final Excluder excluder, final FieldNamingStrategy fieldNamingPolicy,
- final Map<Type, InstanceCreator<?>> instanceCreators, boolean serializeNulls,
- boolean complexMapKeySerialization, boolean generateNonExecutableGson, boolean htmlSafe,
- boolean prettyPrinting, boolean serializeSpecialFloatingPointValues,
- LongSerializationPolicy longSerializationPolicy,
- List<TypeAdapterFactory> typeAdapterFactories) {
- this.constructorConstructor = new ConstructorConstructor(instanceCreators);
- this.serializeNulls = serializeNulls;
- this.generateNonExecutableJson = generateNonExecutableGson;
- this.htmlSafe = htmlSafe;
- this.prettyPrinting = prettyPrinting;
-
- 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);
-
- // user's type adapters
- factories.addAll(typeAdapterFactories);
-
- // 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);
- factories.add(TypeAdapters.newFactory(long.class, Long.class,
- longAdapter(longSerializationPolicy)));
- 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.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.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.ENUM_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));
- factories.add(new ReflectiveTypeAdapterFactory(
- constructorConstructor, fieldNamingPolicy, excluder));
-
- this.factories = Collections.unmodifiableList(factories);
- }
-
- 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);
- }
- };
- }
-
- private 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 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());
- }
- };
- }
-
- /**
- * 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);
- 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 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 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) {
- 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<Collection<Foo>>(){}.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<Collection<Foo>>(){}.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<Collection<Foo>>(){}.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);
- } 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 RuntimeException(e);
- }
- }
-
- /**
- * Returns a new JSON writer configured for this GSON and with the non-execute
- * prefix if that is configured.
- */
- private 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;
- }
-
- /**
- * 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);
- } 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
- * @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<Collection<Foo>>(){}.getType();
- * </pre>
- * @return an object of type T from the string
- * @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
- * @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 = new JsonReader(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<Collection<Foo>>(){}.getType();
- * </pre>
- * @return an object of type T from the json
- * @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 = new JsonReader(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}.
- * 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);
- } 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
- * @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<Collection<Foo>>(){}.getType();
- * </pre>
- * @return an object of type T from the json
- * @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();
- }
-}
+++ /dev/null
-/*
- * 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.TypeAdapters;
-import com.google.gson.reflect.TypeToken;
-
-/**
- * <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>
- *
- * <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>
- * </p>
- *
- * @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;
- private String datePattern;
- private int dateStyle = DateFormat.DEFAULT;
- private int timeStyle = DateFormat.DEFAULT;
- private boolean complexMapKeySerialization;
- private boolean serializeSpecialFloatingPointValues;
- private boolean escapeHtmlChars = true;
- private boolean prettyPrinting;
- private boolean generateNonExecutableJson;
-
- /**
- * 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() {
- }
-
- /**
- * 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));
- * }
- *
- * 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 serializaiton/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 escapes HTML characters such as < > 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(0,
- 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>();
- factories.addAll(this.factories);
- Collections.reverse(factories);
- factories.addAll(this.hierarchyFactories);
- addTypeAdaptersForDate(datePattern, dateStyle, timeStyle, factories);
-
- return new Gson(excluder, fieldNamingPolicy, instanceCreators,
- serializeNulls, complexMapKeySerialization,
- generateNonExecutableJson, escapeHtmlChars, prettyPrinting,
- serializeSpecialFloatingPointValues, longSerializationPolicy, factories);
- }
-
- private void addTypeAdaptersForDate(String datePattern, int dateStyle, int timeStyle,
- List<TypeAdapterFactory> factories) {
- DefaultDateTypeAdapter dateTypeAdapter;
- if (datePattern != null && !"".equals(datePattern.trim())) {
- dateTypeAdapter = new DefaultDateTypeAdapter(datePattern);
- } else if (dateStyle != DateFormat.DEFAULT && timeStyle != DateFormat.DEFAULT) {
- dateTypeAdapter = new DefaultDateTypeAdapter(dateStyle, timeStyle);
- } else {
- return;
- }
-
- factories.add(TreeTypeAdapter.newFactory(TypeToken.get(Date.class), dateTypeAdapter));
- factories.add(TreeTypeAdapter.newFactory(TypeToken.get(Timestamp.class), dateTypeAdapter));
- factories.add(TreeTypeAdapter.newFactory(TypeToken.get(java.sql.Date.class), dateTypeAdapter));
- }
-}
+++ /dev/null
-/*
- * 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<T> {
- * private final Class<T> clazz;
- * private final long value;
- * public Id(Class<T> 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<Id> {
- * 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);
-}
+++ /dev/null
-/*
- * 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>();
- }
-
- @Override
- JsonArray deepCopy() {
- JsonArray result = new JsonArray();
- for (JsonElement element : elements) {
- result.add(element.deepCopy());
- }
- return result;
- }
-
- /**
- * 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);
- }
-
- /**
- * 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 elemetns 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();
- }
-}
+++ /dev/null
-/*
- * 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
+++ /dev/null
-/*
- * 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<T> {
- * private final Class<T> clazz;
- * private final long value;
- * public Id(Class<T> 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<Id>() {
- * 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;
-}
+++ /dev/null
-/*
- * 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.
- */
- 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 ClassCastException} 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 ClassCastException} 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("This is not a JSON Array.");
- }
-
- /**
- * convenience method to get this element as a {@link JsonPrimitive}. If the element is of some
- * other type, a {@link ClassCastException} 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("This is not a JSON Primitive.");
- }
-
- /**
- * convenience method to get this element as a {@link JsonNull}. If the element is of some
- * other type, a {@link ClassCastException} 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("This is not a JSON Null.");
- }
-
- /**
- * 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 Boolean} value.
- *
- * @return get this element as a {@link 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.
- */
- Boolean getAsBooleanWrapper() {
- 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 this element as a primitive character value.
- *
- * @return get this element as a primitive char value.
- * @throws ClassCastException if the element is of not a {@link JsonPrimitive} and is not a valid
- * char value.
- * @throws IllegalStateException if the element is of the type {@link JsonArray} but contains
- * more than a single element.
- * @since 1.3
- */
- 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);
- }
- }
-}
+++ /dev/null
-/*
- * 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);
- }
-}
+++ /dev/null
-/*\r
- * Copyright (C) 2008 Google Inc.\r
- *\r
- * Licensed under the Apache License, Version 2.0 (the "License");\r
- * you may not use this file except in compliance with the License.\r
- * You may obtain a copy of the License at\r
- *\r
- * http://www.apache.org/licenses/LICENSE-2.0\r
- *\r
- * Unless required by applicable law or agreed to in writing, software\r
- * distributed under the License is distributed on an "AS IS" BASIS,\r
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- * See the License for the specific language governing permissions and\r
- * limitations under the License.\r
- */\r
-\r
-package com.google.gson;\r
-\r
-/**\r
- * A class representing a Json {@code null} value.\r
- *\r
- * @author Inderjeet Singh\r
- * @author Joel Leitch\r
- * @since 1.2\r
- */\r
-public final class JsonNull extends JsonElement {\r
- /**\r
- * singleton for JsonNull\r
- *\r
- * @since 1.8\r
- */\r
- public static final JsonNull INSTANCE = new JsonNull();\r
-\r
- /**\r
- * Creates a new JsonNull object.\r
- * Deprecated since Gson version 1.8. Use {@link #INSTANCE} instead\r
- */\r
- @Deprecated\r
- public JsonNull() {\r
- // Do nothing\r
- }\r
-\r
- @Override\r
- JsonNull deepCopy() {\r
- return INSTANCE;\r
- }\r
-\r
- /**\r
- * All instances of JsonNull have the same hash code since they are indistinguishable\r
- */\r
- @Override\r
- public int hashCode() {\r
- return JsonNull.class.hashCode();\r
- }\r
-\r
- /**\r
- * All instances of JsonNull are the same\r
- */\r
- @Override\r
- public boolean equals(Object other) {\r
- return this == other || other instanceof JsonNull;\r
- }\r
-}\r
+++ /dev/null
-/*
- * 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>();
-
- @Override
- 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) {
- if (value == null) {
- value = JsonNull.INSTANCE;
- }
- members.put(property, 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, createJsonElement(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, createJsonElement(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, createJsonElement(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, createJsonElement(value));
- }
-
- /**
- * Creates the proper {@link JsonElement} object from the given {@code value} object.
- *
- * @param value the object to generate the {@link JsonElement} for
- * @return a {@link JsonPrimitive} if the {@code value} is not null, otherwise a {@link JsonNull}
- */
- private JsonElement createJsonElement(Object value) {
- return 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();
- }
-
- /**
- * 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();
- }
-}
+++ /dev/null
-/*
- * 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);
- }
-}
+++ /dev/null
-/*\r
- * Copyright (C) 2009 Google Inc.\r
- *\r
- * Licensed under the Apache License, Version 2.0 (the "License");\r
- * you may not use this file except in compliance with the License.\r
- * You may obtain a copy of the License at\r
- *\r
- * http://www.apache.org/licenses/LICENSE-2.0\r
- *\r
- * Unless required by applicable law or agreed to in writing, software\r
- * distributed under the License is distributed on an "AS IS" BASIS,\r
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- * See the License for the specific language governing permissions and\r
- * limitations under the License.\r
- */\r
-package com.google.gson;\r
-\r
-import java.io.IOException;\r
-import java.io.Reader;\r
-import java.io.StringReader;\r
-\r
-import com.google.gson.internal.Streams;\r
-import com.google.gson.stream.JsonReader;\r
-import com.google.gson.stream.JsonToken;\r
-import com.google.gson.stream.MalformedJsonException;\r
-\r
-/**\r
- * A parser to parse Json into a parse tree of {@link JsonElement}s\r
- *\r
- * @author Inderjeet Singh\r
- * @author Joel Leitch\r
- * @since 1.3\r
- */\r
-public final class JsonParser {\r
-\r
- /**\r
- * Parses the specified JSON string into a parse tree\r
- *\r
- * @param json JSON text\r
- * @return a parse tree of {@link JsonElement}s corresponding to the specified JSON\r
- * @throws JsonParseException if the specified text is not valid JSON\r
- * @since 1.3\r
- */\r
- public JsonElement parse(String json) throws JsonSyntaxException {\r
- return parse(new StringReader(json));\r
- }\r
-\r
- /**\r
- * Parses the specified JSON string into a parse tree\r
- *\r
- * @param json JSON text\r
- * @return a parse tree of {@link JsonElement}s corresponding to the specified JSON\r
- * @throws JsonParseException if the specified text is not valid JSON\r
- * @since 1.3\r
- */\r
- public JsonElement parse(Reader json) throws JsonIOException, JsonSyntaxException {\r
- try {\r
- JsonReader jsonReader = new JsonReader(json);\r
- JsonElement element = parse(jsonReader);\r
- if (!element.isJsonNull() && jsonReader.peek() != JsonToken.END_DOCUMENT) {\r
- throw new JsonSyntaxException("Did not consume the entire document.");\r
- }\r
- return element;\r
- } catch (MalformedJsonException e) {\r
- throw new JsonSyntaxException(e);\r
- } catch (IOException e) {\r
- throw new JsonIOException(e);\r
- } catch (NumberFormatException e) {\r
- throw new JsonSyntaxException(e);\r
- }\r
- }\r
-\r
- /**\r
- * Returns the next value from the JSON stream as a parse tree.\r
- *\r
- * @throws JsonParseException if there is an IOException or if the specified\r
- * text is not valid JSON\r
- * @since 1.6\r
- */\r
- public JsonElement parse(JsonReader json) throws JsonIOException, JsonSyntaxException {\r
- boolean lenient = json.isLenient();\r
- json.setLenient(true);\r
- try {\r
- return Streams.parse(json);\r
- } catch (StackOverflowError e) {\r
- throw new JsonParseException("Failed parsing JSON source: " + json + " to Json", e);\r
- } catch (OutOfMemoryError e) {\r
- throw new JsonParseException("Failed parsing JSON source: " + json + " to Json", e);\r
- } finally {\r
- json.setLenient(lenient);\r
- }\r
- }\r
-}\r
+++ /dev/null
-/*
- * 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 com.google.gson.internal.$Gson$Preconditions;
-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 static final Class<?>[] PRIMITIVE_TYPES = { int.class, long.class, short.class,
- float.class, double.class, byte.class, boolean.class, char.class, Integer.class, Long.class,
- Short.class, Float.class, Double.class, Byte.class, Boolean.class, Character.class };
-
- private Object value;
-
- /**
- * Create a primitive containing a boolean value.
- *
- * @param bool the value to create the primitive with.
- */
- public JsonPrimitive(Boolean bool) {
- setValue(bool);
- }
-
- /**
- * Create a primitive containing a {@link Number}.
- *
- * @param number the value to create the primitive with.
- */
- public JsonPrimitive(Number number) {
- setValue(number);
- }
-
- /**
- * Create a primitive containing a String value.
- *
- * @param string the value to create the primitive with.
- */
- public JsonPrimitive(String string) {
- setValue(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) {
- setValue(c);
- }
-
- /**
- * Create a primitive using the specified Object. It must be an instance of {@link Number}, a
- * Java primitive type, or a String.
- *
- * @param primitive the value to create the primitive with.
- */
- JsonPrimitive(Object primitive) {
- setValue(primitive);
- }
-
- @Override
- JsonPrimitive deepCopy() {
- return this;
- }
-
- void setValue(Object primitive) {
- if (primitive instanceof Character) {
- // convert characters to strings since in JSON, characters are represented as a single
- // character string
- char c = ((Character) primitive).charValue();
- this.value = String.valueOf(c);
- } else {
- $Gson$Preconditions.checkArgument(primitive instanceof Number
- || isPrimitiveOrString(primitive));
- this.value = primitive;
- }
- }
-
- /**
- * 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 {@link Boolean}.
- *
- * @return get this element as a {@link Boolean}.
- */
- @Override
- Boolean getAsBooleanWrapper() {
- return (Boolean) value;
- }
-
- /**
- * 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 getAsBooleanWrapper().booleanValue();
- } else {
- // 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 getAsBooleanWrapper().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);
- }
-
- private static boolean isPrimitiveOrString(Object target) {
- if (target instanceof String) {
- return true;
- }
-
- Class<?> classOfPrimitive = target.getClass();
- for (Class<?> standardPrimitive : PRIMITIVE_TYPES) {
- if (standardPrimitive.isAssignableFrom(classOfPrimitive)) {
- return true;
- }
- }
- return false;
- }
-
- @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;
- }
-}
+++ /dev/null
-/*
- * 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);
-}
+++ /dev/null
-/*
- * 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<T> {
- * private final Class<T> clazz;
- * private final long value;
- *
- * public Id(Class<T> clazz, long value) {
- * this.clazz = clazz;
- * this.value = value;
- * }
- *
- * public long getValue() {
- * return value;
- * }
- * }
- * </pre></p>
- *
- * <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<Id>() {
- * public JsonElement serialize(Id id, Type typeOfId, JsonSerializationContext context) {
- * return new JsonPrimitive(id.getValue());
- * }
- * }
- * </pre></p>
- *
- * <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);
-}
+++ /dev/null
-/*
- * 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();
- }
-}
+++ /dev/null
-/*
- * 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);
- }
-}
+++ /dev/null
-/*
- * 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() {
- 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() {
- 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);
-}
+++ /dev/null
-/*
- * 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.$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;
-
-/**
- * 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.
- */
-final class TreeTypeAdapter<T> extends TypeAdapter<T> {
- private final JsonSerializer<T> serializer;
- private final JsonDeserializer<T> deserializer;
- private final Gson gson;
- private final TypeToken<T> typeToken;
- private final TypeAdapterFactory skipPast;
-
- /** The delegate is lazily created because it may not be needed, and creating it may fail. */
- private TypeAdapter<T> delegate;
-
- private 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(), gson.deserializationContext);
- }
-
- @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(), gson.serializationContext);
- 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 class SingleTypeFactory implements TypeAdapterFactory {
- private final TypeToken<?> exactType;
- private final boolean matchRawType;
- private final Class<?> hierarchyType;
- private final JsonSerializer<?> serializer;
- private final JsonDeserializer<?> deserializer;
-
- private 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
- 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;
- }
- }
-}
+++ /dev/null
-/*
- * 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) throws IOException {
- StringWriter stringWriter = new StringWriter();
- toJson(stringWriter, value);
- 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);
- }
- }
-}
+++ /dev/null
-/*
- * 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 TypeAdapter.Factory {
- * 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 TypeAdapter.Factory {
- * 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);
-}
+++ /dev/null
-/*
- * 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.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 {
- * @Expose private String firstName;
- * @Expose(serialize = false) private String lastName;
- * @Expose (serialize = false, deserialize = false) private String emailAddress;
- * private String password;
- * }
- * </pre></p>
- * 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
- */
-@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;
-}
+++ /dev/null
-/*
- * 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.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 SomeClassWithFields {
- * @SerializedName("name") private final String someField;
- * private final String someOtherField;
- *
- * public SomeClassWithFields(String a, String b) {
- * this.someField = a;
- * this.someOtherField = b;
- * }
- * }
- * </pre>
- *
- * <p>The following shows the output that is generated when serializing an instance of the
- * above example class:</p>
- * <pre>
- * SomeClassWithFields objectToSerialize = new SomeClassWithFields("a", "b");
- * Gson gson = new Gson();
- * String jsonRepresentation = gson.toJson(objectToSerialize);
- * System.out.println(jsonRepresentation);
- *
- * ===== OUTPUT =====
- * {"name":"a","someOtherField":"b"}
- * </pre>
- *
- * <p>NOTE: The value you specify in this annotation must be a valid JSON field name.</p>
- *
- * @see com.google.gson.FieldNamingPolicy
- *
- * @author Inderjeet Singh
- * @author Joel Leitch
- */
-@Retention(RetentionPolicy.RUNTIME)
-@Target(ElementType.FIELD)
-public @interface SerializedName {
-
- /**
- * @return the desired name of the field when it is serialized
- */
- String value();
-}
+++ /dev/null
-/*
- * 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.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;
- * @Since(1.0) private String emailAddress;
- * @Since(1.0) private String password;
- * @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
- */
-@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();
-}
+++ /dev/null
-/*
- * 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.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;
- * @Until(1.1) private String emailAddress;
- * @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
- */
-@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();
-}
+++ /dev/null
-/**
- * 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
+++ /dev/null
-/*\r
- * Copyright (C) 2008 Google Inc.\r
- *\r
- * Licensed under the Apache License, Version 2.0 (the "License");\r
- * you may not use this file except in compliance with the License.\r
- * You may obtain a copy of the License at\r
- *\r
- * http://www.apache.org/licenses/LICENSE-2.0\r
- *\r
- * Unless required by applicable law or agreed to in writing, software\r
- * distributed under the License is distributed on an "AS IS" BASIS,\r
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- * See the License for the specific language governing permissions and\r
- * limitations under the License.\r
- */\r
-\r
-package com.google.gson.internal;\r
-\r
-/**\r
- * A simple utility class used to check method Preconditions.\r
- *\r
- * <pre>\r
- * public long divideBy(long value) {\r
- * Preconditions.checkArgument(value != 0);\r
- * return this.value / value;\r
- * }\r
- * </pre>\r
- *\r
- * @author Inderjeet Singh\r
- * @author Joel Leitch\r
- */\r
-public final class $Gson$Preconditions {\r
- public static <T> T checkNotNull(T obj) {\r
- if (obj == null) {\r
- throw new NullPointerException();\r
- }\r
- return obj;\r
- }\r
-\r
- public static void checkArgument(boolean condition) {\r
- if (!condition) {\r
- throw new IllegalArgumentException();\r
- }\r
- }\r
-}\r
+++ /dev/null
-/**
- * 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 static com.google.gson.internal.$Gson$Preconditions.checkArgument;
-import static com.google.gson.internal.$Gson$Preconditions.checkNotNull;
-
-import java.io.Serializable;
-import java.lang.reflect.Array;
-import java.lang.reflect.GenericArrayType;
-import java.lang.reflect.GenericDeclaration;
-import java.lang.reflect.ParameterizedType;
-import java.lang.reflect.Type;
-import java.lang.reflect.TypeVariable;
-import java.lang.reflect.WildcardType;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Map;
-import java.util.NoSuchElementException;
-import java.util.Properties;
-
-/**
- * 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() {}
-
- /**
- * Returns a new parameterized type, applying {@code typeArguments} to
- * {@code rawType} and enclosed by {@code ownerType}.
- *
- * @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}.
- */
- public static WildcardType subtypeOf(Type bound) {
- return new WildcardTypeImpl(new Type[] { bound }, 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}.
- */
- public static WildcardType supertypeOf(Type bound) {
- return new WildcardTypeImpl(new Type[] { Object.class }, new Type[] { bound });
- }
-
- /**
- * 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}.
- */
- 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));
- }
-
- /**
- * Returns 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;
- }
- }
-
- private 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>}.
- */
- 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 supertype a superclass of, or interface implemented by, this.
- */
- static Type getSupertype(Type context, Class<?> contextRawType, Class<?> supertype) {
- 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.
- */
- 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.
- */
- 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.
- */
- 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) {
- // 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;
- 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);
- 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);
- 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);
- 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]);
- 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]);
- if (lowerBound != originalLowerBound[0]) {
- return supertypeOf(lowerBound);
- }
- } else if (originalUpperBound.length == 1) {
- Type upperBound = resolve(context, contextRawType, originalUpperBound[0]);
- 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; i < array.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.
- */
- private static Class<?> declaringClassOf(TypeVariable<?> typeVariable) {
- GenericDeclaration genericDeclaration = typeVariable.getGenericDeclaration();
- return genericDeclaration instanceof Class
- ? (Class<?>) genericDeclaration
- : null;
- }
-
- private 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;
- checkArgument(ownerType != null || rawTypeAsClass.getEnclosingClass() == null);
- checkArgument(ownerType == null || rawTypeAsClass.getEnclosingClass() != null);
- }
-
- this.ownerType = ownerType == null ? null : canonicalize(ownerType);
- this.rawType = canonicalize(rawType);
- this.typeArguments = typeArguments.clone();
- for (int t = 0; t < this.typeArguments.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() {
- StringBuilder stringBuilder = new StringBuilder(30 * (typeArguments.length + 1));
- stringBuilder.append(typeToString(rawType));
-
- if (typeArguments.length == 0) {
- return stringBuilder.toString();
- }
-
- stringBuilder.append("<").append(typeToString(typeArguments[0]));
- for (int i = 1; i < typeArguments.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;
- }
-}
+++ /dev/null
-/*
- * 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.InstanceCreator;
-import com.google.gson.JsonIOException;
-import com.google.gson.reflect.TypeToken;
-
-import java.lang.reflect.Constructor;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.ParameterizedType;
-import java.lang.reflect.Type;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.EnumSet;
-import java.util.LinkedHashMap;
-import java.util.LinkedHashSet;
-import java.util.LinkedList;
-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;
-
-/**
- * Returns a function that can construct an instance of a requested type.
- */
-public final class ConstructorConstructor {
- private final Map<Type, InstanceCreator<?>> instanceCreators;
-
- 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>() {
- 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>() {
- 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()) {
- constructor.setAccessible(true);
- }
- return new ObjectConstructor<T>() {
- @SuppressWarnings("unchecked") // T is the same raw type as is requested
- 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
- * subytpes.
- */
- @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>() {
- public T construct() {
- return (T) new TreeSet<Object>();
- }
- };
- } else if (EnumSet.class.isAssignableFrom(rawType)) {
- return new ObjectConstructor<T>() {
- @SuppressWarnings("rawtypes")
- 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>() {
- public T construct() {
- return (T) new LinkedHashSet<Object>();
- }
- };
- } else if (Queue.class.isAssignableFrom(rawType)) {
- return new ObjectConstructor<T>() {
- public T construct() {
- return (T) new LinkedList<Object>();
- }
- };
- } else {
- return new ObjectConstructor<T>() {
- public T construct() {
- return (T) new ArrayList<Object>();
- }
- };
- }
- }
-
- if (Map.class.isAssignableFrom(rawType)) {
- if (SortedMap.class.isAssignableFrom(rawType)) {
- return new ObjectConstructor<T>() {
- 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>() {
- public T construct() {
- return (T) new LinkedHashMap<Object, Object>();
- }
- };
- } else {
- return new ObjectConstructor<T>() {
- 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")
- 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 + ". "
- + "Register an InstanceCreator with Gson for this type may fix this problem."), e);
- }
- }
- };
- }
-
- @Override public String toString() {
- return instanceCreators.toString();
- }
-}
+++ /dev/null
-/*
- * 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();
- }
- }
-
- 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();
- final boolean skipSerialize = excludeClass(rawType, true);
- final boolean skipDeserialize = excludeClass(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;
- }
-
- public boolean excludeClass(Class<?> clazz, boolean serialize) {
- 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;
- }
-
- 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;
- }
-}
+++ /dev/null
-/*
- * 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;
-}
+++ /dev/null
-/*
- * 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;
-
- 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);
- }
-}
\ No newline at end of file
+++ /dev/null
-/*
- * 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;
-
- 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();
- }
- }
-
- 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);
- }
-}
\ No newline at end of file
+++ /dev/null
-/*
- * 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
+++ /dev/null
-/*
- * 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;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Map;
-
-/**
- * Contains static utility methods pertaining to primitive types and their
- * corresponding wrapper types.
- *
- * @author Kevin Bourrillion
- */
-public final class Primitives {
- private Primitives() {}
-
- /** A map from primitive types to their corresponding wrapper types. */
- private static final Map<Class<?>, Class<?>> PRIMITIVE_TO_WRAPPER_TYPE;
-
- /** A map from wrapper types to their corresponding primitive types. */
- private static final Map<Class<?>, Class<?>> WRAPPER_TO_PRIMITIVE_TYPE;
-
- // Sad that we can't use a BiMap. :(
-
- static {
- Map<Class<?>, Class<?>> primToWrap = new HashMap<Class<?>, Class<?>>(16);
- Map<Class<?>, Class<?>> wrapToPrim = new HashMap<Class<?>, Class<?>>(16);
-
- add(primToWrap, wrapToPrim, boolean.class, Boolean.class);
- add(primToWrap, wrapToPrim, byte.class, Byte.class);
- add(primToWrap, wrapToPrim, char.class, Character.class);
- add(primToWrap, wrapToPrim, double.class, Double.class);
- add(primToWrap, wrapToPrim, float.class, Float.class);
- add(primToWrap, wrapToPrim, int.class, Integer.class);
- add(primToWrap, wrapToPrim, long.class, Long.class);
- add(primToWrap, wrapToPrim, short.class, Short.class);
- add(primToWrap, wrapToPrim, void.class, Void.class);
-
- PRIMITIVE_TO_WRAPPER_TYPE = Collections.unmodifiableMap(primToWrap);
- WRAPPER_TO_PRIMITIVE_TYPE = Collections.unmodifiableMap(wrapToPrim);
- }
-
- private static void add(Map<Class<?>, Class<?>> forward,
- Map<Class<?>, Class<?>> backward, Class<?> key, Class<?> value) {
- forward.put(key, value);
- backward.put(value, key);
- }
-
- /**
- * Returns true if this type is a primitive.
- */
- public static boolean isPrimitive(Type type) {
- return PRIMITIVE_TO_WRAPPER_TYPE.containsKey(type);
- }
-
- /**
- * 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 WRAPPER_TO_PRIMITIVE_TYPE.containsKey(
- $Gson$Preconditions.checkNotNull(type));
- }
-
- /**
- * 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>
- */
- public static <T> Class<T> wrap(Class<T> type) {
- // cast is safe: long.class and Long.class are both of type Class<Long>
- @SuppressWarnings("unchecked")
- Class<T> wrapped = (Class<T>) PRIMITIVE_TO_WRAPPER_TYPE.get(
- $Gson$Preconditions.checkNotNull(type));
- return (wrapped == null) ? type : wrapped;
- }
-
- /**
- * 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>
- */
- public static <T> Class<T> unwrap(Class<T> type) {
- // cast is safe: long.class and Long.class are both of type Class<Long>
- @SuppressWarnings("unchecked")
- Class<T> unwrapped = (Class<T>) WRAPPER_TO_PRIMITIVE_TYPE.get(
- $Gson$Preconditions.checkNotNull(type));
- return (unwrapped == null) ? type : unwrapped;
- }
-}
+++ /dev/null
-/*
- * 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 {
- /**
- * 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();
-
- private 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);
- }
- }
- }
-
-}
+++ /dev/null
-/*
- * 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;
-
-/**
- * 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 {
- return (T) allocateInstance.invoke(unsafe, c);
- }
- };
- } 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 {
- return (T) newInstance.invoke(null, c, Object.class);
- }
- };
- } 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 {
- return (T) newInstance.invoke(null, c, constructorId);
- }
- };
- } catch (Exception ignored) {
- }
-
- // give up
- return new UnsafeAllocator() {
- @Override
- public <T> T newInstance(Class<T> c) {
- throw new UnsupportedOperationException("Cannot allocate " + c);
- }
- };
- }
-}
+++ /dev/null
-/*
- * 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"})
- 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;
- }
-
- 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();
- Object array = Array.newInstance(componentType, list.size());
- for (int i = 0; i < list.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();
- }
-}
+++ /dev/null
-/*
- * 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;
- }
-
- 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;
- }
-
- 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;
- }
-
- 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();
- }
- }
-}
+++ /dev/null
-/*
- * 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;
-import java.util.Date;
-import java.util.Locale;
-import java.util.TimeZone;
-
-/**
- * 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
- public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) {
- return typeToken.getRawType() == Date.class ? (TypeAdapter<T>) new DateTypeAdapter() : null;
- }
- };
-
- private final DateFormat enUsFormat
- = DateFormat.getDateTimeInstance(DateFormat.DEFAULT, DateFormat.DEFAULT, Locale.US);
- private final DateFormat localFormat
- = DateFormat.getDateTimeInstance(DateFormat.DEFAULT, DateFormat.DEFAULT);
- private final DateFormat iso8601Format = buildIso8601Format();
-
- private static DateFormat buildIso8601Format() {
- DateFormat iso8601Format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.US);
- iso8601Format.setTimeZone(TimeZone.getTimeZone("UTC"));
- return iso8601Format;
- }
-
- @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) {
- try {
- return localFormat.parse(json);
- } catch (ParseException ignored) {
- }
- try {
- return enUsFormat.parse(json);
- } catch (ParseException ignored) {
- }
- try {
- return iso8601Format.parse(json);
- } 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 = enUsFormat.format(value);
- out.value(dateFormatAsString);
- }
-}
+++ /dev/null
-/*
- * 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.ArrayList;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-
-/**
- * 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();
-
- private final List<Object> stack = new ArrayList<Object>();
-
- public JsonTreeReader(JsonElement element) {
- super(UNREADABLE_READER);
- stack.add(element);
- }
-
- @Override public void beginArray() throws IOException {
- expect(JsonToken.BEGIN_ARRAY);
- JsonArray array = (JsonArray) peekStack();
- stack.add(array.iterator());
- }
-
- @Override public void endArray() throws IOException {
- expect(JsonToken.END_ARRAY);
- popStack(); // empty iterator
- popStack(); // array
- }
-
- @Override public void beginObject() throws IOException {
- expect(JsonToken.BEGIN_OBJECT);
- JsonObject object = (JsonObject) peekStack();
- stack.add(object.entrySet().iterator());
- }
-
- @Override public void endObject() throws IOException {
- expect(JsonToken.END_OBJECT);
- popStack(); // empty iterator
- popStack(); // object
- }
-
- @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 (stack.isEmpty()) {
- return JsonToken.END_DOCUMENT;
- }
-
- Object o = peekStack();
- if (o instanceof Iterator) {
- boolean isObject = stack.get(stack.size() - 2) instanceof JsonObject;
- Iterator<?> iterator = (Iterator<?>) o;
- if (iterator.hasNext()) {
- if (isObject) {
- return JsonToken.NAME;
- } else {
- stack.add(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.get(stack.size() - 1);
- }
-
- private Object popStack() {
- return stack.remove(stack.size() - 1);
- }
-
- private void expect(JsonToken expected) throws IOException {
- if (peek() != expected) {
- throw new IllegalStateException("Expected " + expected + " but was " + peek());
- }
- }
-
- @Override public String nextName() throws IOException {
- expect(JsonToken.NAME);
- Iterator<?> i = (Iterator<?>) peekStack();
- Map.Entry<?, ?> entry = (Map.Entry<?, ?>) i.next();
- stack.add(entry.getValue());
- return (String) entry.getKey();
- }
-
- @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);
- }
- return ((JsonPrimitive) popStack()).getAsString();
- }
-
- @Override public boolean nextBoolean() throws IOException {
- expect(JsonToken.BOOLEAN);
- return ((JsonPrimitive) popStack()).getAsBoolean();
- }
-
- @Override public void nextNull() throws IOException {
- expect(JsonToken.NULL);
- popStack();
- }
-
- @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);
- }
- double result = ((JsonPrimitive) peekStack()).getAsDouble();
- if (!isLenient() && (Double.isNaN(result) || Double.isInfinite(result))) {
- throw new NumberFormatException("JSON forbids NaN and infinities: " + result);
- }
- popStack();
- 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);
- }
- long result = ((JsonPrimitive) peekStack()).getAsLong();
- popStack();
- 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);
- }
- int result = ((JsonPrimitive) peekStack()).getAsInt();
- popStack();
- return result;
- }
-
- @Override public void close() throws IOException {
- stack.clear();
- stack.add(SENTINEL_CLOSED);
- }
-
- @Override public void skipValue() throws IOException {
- if (peek() == JsonToken.NAME) {
- nextName();
- } else {
- popStack();
- }
- }
-
- @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();
- stack.add(entry.getValue());
- stack.add(new JsonPrimitive((String)entry.getKey()));
- }
-}
+++ /dev/null
-/*
- * 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(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);
- }
-}
+++ /dev/null
-/*
- * 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;
- private final boolean complexMapKeySerialization;
-
- public MapTypeAdapterFactory(ConstructorConstructor constructorConstructor,
- boolean complexMapKeySerialization) {
- this.constructorConstructor = constructorConstructor;
- this.complexMapKeySerialization = complexMapKeySerialization;
- }
-
- 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;
- }
-
- 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;
- }
-
- 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; i < keys.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; i < keys.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();
- }
- }
- }
-}
+++ /dev/null
-/*
- * 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")
- 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;
-
- private 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);
- }
-}
+++ /dev/null
-/*
- * 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.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.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.LinkedHashMap;
-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;
-
- public ReflectiveTypeAdapterFactory(ConstructorConstructor constructorConstructor,
- FieldNamingStrategy fieldNamingPolicy, Excluder excluder) {
- this.constructorConstructor = constructorConstructor;
- this.fieldNamingPolicy = fieldNamingPolicy;
- this.excluder = excluder;
- }
-
- public boolean excludeField(Field f, boolean serialize) {
- return !excluder.excludeClass(f.getType(), serialize) && !excluder.excludeField(f, serialize);
- }
-
- private String getFieldName(Field f) {
- SerializedName serializedName = f.getAnnotation(SerializedName.class);
- return serializedName == null ? fieldNamingPolicy.translateName(f) : serializedName.value();
- }
-
- 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...
- return new ReflectiveTypeAdapterFactory.BoundField(name, serialize, deserialize) {
- final TypeAdapter<?> typeAdapter = context.getAdapter(fieldType);
- @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 =
- new TypeAdapterRuntimeTypeWrapper(context, this.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);
- }
- }
- };
- }
-
- 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;
- }
- field.setAccessible(true);
- Type fieldType = $Gson$Types.resolve(type.getType(), raw, field.getGenericType());
- BoundField boundField = createBoundField(context, field, getFieldName(field),
- TypeToken.get(fieldType), serialize, deserialize);
- BoundField previous = result.put(boundField.name, boundField);
- 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 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;
-
- private 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.serialized) {
- out.name(boundField.name);
- boundField.write(out, value);
- }
- }
- } catch (IllegalAccessException e) {
- throw new AssertionError();
- }
- out.endObject();
- }
- }
-}
+++ /dev/null
-/*
- * 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
- 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));
- }
-}
+++ /dev/null
-/*
- * 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
- 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));
- }
-}
+++ /dev/null
-/*\r
- * Copyright (C) 2011 Google Inc.\r
- *\r
- * Licensed under the Apache License, Version 2.0 (the "License");\r
- * you may not use this file except in compliance with the License.\r
- * You may obtain a copy of the License at\r
- *\r
- * http://www.apache.org/licenses/LICENSE-2.0\r
- *\r
- * Unless required by applicable law or agreed to in writing, software\r
- * distributed under the License is distributed on an "AS IS" BASIS,\r
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- * See the License for the specific language governing permissions and\r
- * limitations under the License.\r
- */\r
-package com.google.gson.internal.bind;\r
-\r
-import com.google.gson.Gson;\r
-import com.google.gson.TypeAdapter;\r
-import com.google.gson.reflect.TypeToken;\r
-import com.google.gson.stream.JsonReader;\r
-import com.google.gson.stream.JsonWriter;\r
-import java.io.IOException;\r
-import java.lang.reflect.Type;\r
-import java.lang.reflect.TypeVariable;\r
-\r
-final class TypeAdapterRuntimeTypeWrapper<T> extends TypeAdapter<T> {\r
- private final Gson context;\r
- private final TypeAdapter<T> delegate;\r
- private final Type type;\r
-\r
- TypeAdapterRuntimeTypeWrapper(Gson context, TypeAdapter<T> delegate, Type type) {\r
- this.context = context;\r
- this.delegate = delegate;\r
- this.type = type;\r
- }\r
-\r
- @Override\r
- public T read(JsonReader in) throws IOException {\r
- return delegate.read(in);\r
- }\r
-\r
- @SuppressWarnings({"rawtypes", "unchecked"})\r
- @Override\r
- public void write(JsonWriter out, T value) throws IOException {\r
- // Order of preference for choosing type adapters\r
- // First preference: a type adapter registered for the runtime type\r
- // Second preference: a type adapter registered for the declared type\r
- // Third preference: reflective type adapter for the runtime type (if it is a sub class of the declared type)\r
- // Fourth preference: reflective type adapter for the declared type\r
-\r
- TypeAdapter chosen = delegate;\r
- Type runtimeType = getRuntimeTypeIfMoreSpecific(type, value);\r
- if (runtimeType != type) {\r
- TypeAdapter runtimeTypeAdapter = context.getAdapter(TypeToken.get(runtimeType));\r
- if (!(runtimeTypeAdapter instanceof ReflectiveTypeAdapterFactory.Adapter)) {\r
- // The user registered a type adapter for the runtime type, so we will use that\r
- chosen = runtimeTypeAdapter;\r
- } else if (!(delegate instanceof ReflectiveTypeAdapterFactory.Adapter)) {\r
- // The user registered a type adapter for Base class, so we prefer it over the\r
- // reflective type adapter for the runtime type\r
- chosen = delegate;\r
- } else {\r
- // Use the type adapter for runtime type\r
- chosen = runtimeTypeAdapter;\r
- }\r
- }\r
- chosen.write(out, value);\r
- }\r
-\r
- /**\r
- * Finds a compatible runtime type if it is more specific\r
- */\r
- private Type getRuntimeTypeIfMoreSpecific(Type type, Object value) {\r
- if (value != null\r
- && (type == Object.class || type instanceof TypeVariable<?> || type instanceof Class<?>)) {\r
- type = value.getClass();\r
- }\r
- return type;\r
- }\r
-}\r
+++ /dev/null
-/*
- * 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.TypeAdapterFactory;
-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.BitSet;
-import java.util.Calendar;
-import java.util.Date;
-import java.util.GregorianCalendar;
-import java.util.HashMap;
-import java.util.Locale;
-import java.util.Map;
-import java.util.StringTokenizer;
-import java.util.UUID;
-
-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.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() {}
-
- @SuppressWarnings("rawtypes")
- public static final TypeAdapter<Class> CLASS = new TypeAdapter<Class>() {
- @Override
- public void write(JsonWriter out, Class value) throws IOException {
- if (value == null) {
- out.nullValue();
- } else {
- 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 {
- if (in.peek() == JsonToken.NULL) {
- in.nextNull();
- return null;
- } else {
- throw new UnsupportedOperationException(
- "Attempted to deserialize a java.lang.Class. Forgot to register a type adapter?");
- }
- }
- };
- public static final TypeAdapterFactory CLASS_FACTORY = newFactory(Class.class, CLASS);
-
- public static final TypeAdapter<BitSet> BIT_SET = new TypeAdapter<BitSet>() {
- public BitSet read(JsonReader in) throws IOException {
- if (in.peek() == JsonToken.NULL) {
- in.nextNull();
- return null;
- }
-
- 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;
- }
-
- public void write(JsonWriter out, BitSet src) throws IOException {
- if (src == null) {
- out.nullValue();
- return;
- }
-
- out.beginArray();
- for (int i = 0; i < src.length(); i++) {
- int value = (src.get(i)) ? 1 : 0;
- out.value(value);
- }
- out.endArray();
- }
- };
-
- 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 {
- if (in.peek() == JsonToken.NULL) {
- in.nextNull();
- return null;
- } else if (in.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 {
- if (value == null) {
- out.nullValue();
- return;
- }
- 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<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:
- 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 TypeAdapterFactory TIMESTAMP_FACTORY = new TypeAdapterFactory() {
- @SuppressWarnings("unchecked") // we use a runtime check to make sure the 'T's equal
- 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();
- }
- nameToConstant.put(name, constant);
- constantToName.put(constant, name);
- }
- } catch (NoSuchFieldException e) {
- throw new AssertionError();
- }
- }
- public T read(JsonReader in) throws IOException {
- if (in.peek() == JsonToken.NULL) {
- in.nextNull();
- return null;
- }
- return nameToConstant.get(in.nextString());
- }
-
- public void write(JsonWriter out, T value) throws IOException {
- out.value(value == null ? null : constantToName.get(value));
- }
- }
-
- public static final TypeAdapterFactory ENUM_FACTORY = newEnumTypeHierarchyFactory();
-
- public static TypeAdapterFactory newEnumTypeHierarchyFactory() {
- return new TypeAdapterFactory() {
- @SuppressWarnings({"rawtypes", "unchecked"})
- 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
- 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
- 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
- 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
- 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 + "]";
- }
- };
- }
-
- public static <TT> TypeAdapterFactory newTypeHierarchyFactory(
- final Class<TT> clazz, final TypeAdapter<TT> typeAdapter) {
- return new TypeAdapterFactory() {
- @SuppressWarnings("unchecked")
- public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) {
- return clazz.isAssignableFrom(typeToken.getRawType()) ? (TypeAdapter<T>) typeAdapter : null;
- }
- @Override public String toString() {
- return "Factory[typeHierarchy=" + clazz.getName() + ",adapter=" + typeAdapter + "]";
- }
- };
- }
-}
+++ /dev/null
-/**
- * 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
+++ /dev/null
-/**
- * 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
+++ /dev/null
-/*
- * 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);
- }
-}
+++ /dev/null
-/**
- * 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
+++ /dev/null
-/*
- * 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;
-
-/**
- * Reads a JSON (<a href="http://www.ietf.org/rfc/rfc4627.txt">RFC 4627</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"/><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;
-
- private 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;
- }
-
- /**
- * 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 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);
- peeked = PEEKED_NONE;
- } else {
- throw new IllegalStateException("Expected BEGIN_ARRAY but was " + peek()
- + " at line " + getLineNumber() + " column " + getColumnNumber());
- }
- }
-
- /**
- * 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--;
- peeked = PEEKED_NONE;
- } else {
- throw new IllegalStateException("Expected END_ARRAY but was " + peek()
- + " at line " + getLineNumber() + " column " + getColumnNumber());
- }
- }
-
- /**
- * 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()
- + " at line " + getLineNumber() + " column " + getColumnNumber());
- }
- }
-
- /**
- * 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--;
- peeked = PEEKED_NONE;
- } else {
- throw new IllegalStateException("Expected END_OBJECT but was " + peek()
- + " at line " + getLineNumber() + " column " + getColumnNumber());
- }
- }
-
- /**
- * 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();
- }
- }
-
- private 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 '"':
- if (stackSize == 1) {
- checkLenient();
- }
- 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.
- }
-
- if (stackSize == 1) {
- checkLenient(); // Top-level value isn't an array or an object.
- }
-
- 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)) {
- 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()
- + " at line " + getLineNumber() + " column " + getColumnNumber());
- }
- peeked = PEEKED_NONE;
- 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()
- + " at line " + getLineNumber() + " column " + getColumnNumber());
- }
- peeked = PEEKED_NONE;
- 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;
- return true;
- } else if (p == PEEKED_FALSE) {
- peeked = PEEKED_NONE;
- return false;
- }
- throw new IllegalStateException("Expected a boolean but was " + peek()
- + " at line " + getLineNumber() + " column " + getColumnNumber());
- }
-
- /**
- * 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;
- } else {
- throw new IllegalStateException("Expected null but was " + peek()
- + " at line " + getLineNumber() + " column " + getColumnNumber());
- }
- }
-
- /**
- * 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;
- 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()
- + " at line " + getLineNumber() + " column " + getColumnNumber());
- }
-
- 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
- + " at line " + getLineNumber() + " column " + getColumnNumber());
- }
- peekedString = null;
- peeked = PEEKED_NONE;
- 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;
- return 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 ? '\'' : '"');
- try {
- long result = Long.parseLong(peekedString);
- peeked = PEEKED_NONE;
- return result;
- } catch (NumberFormatException ignored) {
- // Fall back to parse as a double below.
- }
- } else {
- throw new IllegalStateException("Expected a long but was " + peek()
- + " at line " + getLineNumber() + " column " + getColumnNumber());
- }
-
- 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
- + " at line " + getLineNumber() + " column " + getColumnNumber());
- }
- peekedString = null;
- peeked = PEEKED_NONE;
- 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 = new StringBuilder();
- 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;
- builder.append(buffer, start, p - start - 1);
- return builder.toString();
- } else if (c == '\\') {
- pos = p;
- builder.append(buffer, start, p - start - 1);
- builder.append(readEscapeCharacter());
- p = pos;
- l = limit;
- start = p;
- } else if (c == '\n') {
- lineNumber++;
- lineStart = p;
- }
- }
-
- 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();
- }
- builder.append(buffer, pos, i);
- pos += i;
- i = 0;
- if (!fillBuffer(1)) {
- break;
- }
- }
-
- String result;
- if (builder == null) {
- result = new String(buffer, pos, i);
- } else {
- builder.append(buffer, pos, i);
- result = builder.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
- + " at line " + getLineNumber() + " column " + getColumnNumber());
- }
- peeked = PEEKED_NONE;
- return result;
- }
-
- 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 ? '\'' : '"');
- try {
- result = Integer.parseInt(peekedString);
- peeked = PEEKED_NONE;
- return result;
- } catch (NumberFormatException ignored) {
- // Fall back to parse as a double below.
- }
- } else {
- throw new IllegalStateException("Expected an int but was " + peek()
- + " at line " + getLineNumber() + " column " + getColumnNumber());
- }
-
- 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
- + " at line " + getLineNumber() + " column " + getColumnNumber());
- }
- peekedString = null;
- peeked = PEEKED_NONE;
- 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);
- }
-
- private void push(int newTop) {
- if (stackSize == stack.length) {
- int[] newStack = new int[stackSize * 2];
- System.arraycopy(stack, 0, newStack, 0, stackSize);
- stack = newStack;
- }
- 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;
- }
-
- private int getLineNumber() {
- return lineNumber + 1;
- }
-
- private int getColumnNumber() {
- return pos - lineStart + 1;
- }
-
- /**
- * 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"
- + " at line " + getLineNumber() + " column " + getColumnNumber());
- } 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 {
- outer:
- for (; pos + toFind.length() <= limit || fillBuffer(toFind.length()); pos++) {
- if (buffer[pos] == '\n') {
- lineNumber++;
- lineStart = pos + 1;
- continue;
- }
- for (int c = 0; c < toFind.length(); c++) {
- if (buffer[pos + c] != toFind.charAt(c)) {
- continue outer;
- }
- }
- return true;
- }
- return false;
- }
-
- @Override public String toString() {
- return getClass().getSimpleName()
- + " at line " + getLineNumber() + " column " + getColumnNumber();
- }
-
- /**
- * 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 '\\':
- default:
- return escaped;
- }
- }
-
- /**
- * 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
- + " at line " + getLineNumber() + " column " + getColumnNumber());
- }
-
- /**
- * 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() + " "
- + " at line " + reader.getLineNumber() + " column " + reader.getColumnNumber());
- }
- }
- };
- }
-}
+++ /dev/null
-/*
- * 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;
-}
+++ /dev/null
-/*
- * 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
-}
+++ /dev/null
-/*
- * 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 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/rfc4627.txt">RFC 4627</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.setIndentSpaces(4);
- * 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 4627, "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/rfc4627.txt">RFC 4627</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, String openBracket) throws IOException {
- beforeValue(true);
- 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, String 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) {
- int[] newStack = new int[stackSize * 2];
- System.arraycopy(stack, 0, newStack, 0, stackSize);
- stack = newStack;
- }
- 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(false);
- string(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(false);
- out.write("null");
- return this;
- }
-
- /**
- * Encodes {@code value}.
- *
- * @return this writer.
- */
- public JsonWriter value(boolean value) throws IOException {
- writeDeferredName();
- beforeValue(false);
- 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 {
- if (Double.isNaN(value) || Double.isInfinite(value)) {
- throw new IllegalArgumentException("Numeric values must be finite, but was " + value);
- }
- writeDeferredName();
- beforeValue(false);
- out.append(Double.toString(value));
- return this;
- }
-
- /**
- * Encodes {@code value}.
- *
- * @return this writer.
- */
- public JsonWriter value(long value) throws IOException {
- writeDeferredName();
- beforeValue(false);
- 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(false);
- 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.
- *
- * @param root true if the value is a new array or object, the two values
- * permitted as top-level elements.
- */
- @SuppressWarnings("fallthrough")
- private void beforeValue(boolean root) 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
- if (!lenient && !root) {
- throw new IllegalStateException(
- "JSON must start with an array or an object.");
- }
- 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.");
- }
- }
-}
+++ /dev/null
-/*
- * 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);
- }
-}