diff --git a/gson/src/main/java/com/google/gson/Gson.java b/gson/src/main/java/com/google/gson/Gson.java index a04a5da6..225b9085 100644 --- a/gson/src/main/java/com/google/gson/Gson.java +++ b/gson/src/main/java/com/google/gson/Gson.java @@ -29,12 +29,10 @@ import com.google.gson.internal.bind.ExcludedTypeAdapterFactory; import com.google.gson.internal.bind.JsonElementReader; import com.google.gson.internal.bind.JsonElementWriter; import com.google.gson.internal.bind.MapTypeAdapterFactory; -import com.google.gson.internal.bind.MiniGson; 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.TypeAdapter; import com.google.gson.internal.bind.TypeAdapters; import com.google.gson.reflect.TypeToken; import com.google.gson.stream.JsonReader; @@ -52,7 +50,9 @@ import java.lang.reflect.Modifier; 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.LinkedList; import java.util.List; import java.util.Map; @@ -120,6 +120,21 @@ public final class Gson { 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, FutureTypeAdapter>> calls + = new ThreadLocal, FutureTypeAdapter>>() { + @Override protected Map, FutureTypeAdapter> initialValue() { + return new HashMap, FutureTypeAdapter>(); + } + }; + + private final List factories; private final ExclusionStrategy deserializationExclusionStrategy; private final ExclusionStrategy serializationExclusionStrategy; private final ConstructorConstructor constructorConstructor; @@ -135,8 +150,6 @@ public final class Gson { private final boolean generateNonExecutableJson; private final boolean prettyPrinting; - private final MiniGson miniGson; - /** * Constructs a Gson object with default configuration. The default configuration has the * following settings: @@ -227,54 +240,53 @@ public final class Gson { } }; - MiniGson.Builder builder = new MiniGson.Builder() - .withoutDefaultFactories() - .factory(TypeAdapters.STRING_FACTORY) - .factory(TypeAdapters.INTEGER_FACTORY) - .factory(TypeAdapters.BOOLEAN_FACTORY) - .factory(TypeAdapters.BYTE_FACTORY) - .factory(TypeAdapters.SHORT_FACTORY) - .factory(TypeAdapters.newFactory(long.class, Long.class, - longAdapter(longSerializationPolicy))) - .factory(TypeAdapters.newFactory(double.class, Double.class, - doubleAdapter(serializeSpecialFloatingPointValues))) - .factory(TypeAdapters.newFactory(float.class, Float.class, - floatAdapter(serializeSpecialFloatingPointValues))) - .factory(new ExcludedTypeAdapterFactory( - serializationExclusionStrategy, deserializationExclusionStrategy)) - .factory(TypeAdapters.NUMBER_FACTORY) - .factory(TypeAdapters.CHARACTER_FACTORY) - .factory(TypeAdapters.STRING_BUILDER_FACTORY) - .factory(TypeAdapters.STRING_BUFFER_FACTORY) - .typeAdapter(BigDecimal.class, new BigDecimalTypeAdapter()) - .typeAdapter(BigInteger.class, new BigIntegerTypeAdapter()) - .factory(TypeAdapters.JSON_ELEMENT_FACTORY) - .factory(ObjectTypeAdapter.FACTORY); + ConstructorConstructor constructorConstructor = new ConstructorConstructor(); + List factories = new ArrayList(); + 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(new ExcludedTypeAdapterFactory( + serializationExclusionStrategy, deserializationExclusionStrategy)); + 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, new BigDecimalTypeAdapter())); + factories.add(TypeAdapters.newFactory(BigInteger.class, new BigIntegerTypeAdapter())); + factories.add(TypeAdapters.JSON_ELEMENT_FACTORY); + factories.add(ObjectTypeAdapter.FACTORY); for (TypeAdapter.Factory factory : typeAdapterFactories) { - builder.factory(factory); + factories.add(factory); } - builder - .factory(new GsonToMiniGsonTypeAdapterFactory(this, serializers, deserializers)) - .factory(new CollectionTypeAdapterFactory(constructorConstructor)) - .factory(TypeAdapters.URL_FACTORY) - .factory(TypeAdapters.URI_FACTORY) - .factory(TypeAdapters.UUID_FACTORY) - .factory(TypeAdapters.LOCALE_FACTORY) - .factory(TypeAdapters.INET_ADDRESS_FACTORY) - .factory(TypeAdapters.BIT_SET_FACTORY) - .factory(DateTypeAdapter.FACTORY) - .factory(TypeAdapters.CALENDAR_FACTORY) - .factory(TimeTypeAdapter.FACTORY) - .factory(SqlDateTypeAdapter.FACTORY) - .factory(TypeAdapters.TIMESTAMP_FACTORY) - .factory(new MapTypeAdapterFactory(constructorConstructor, complexMapKeySerialization)) - .factory(ArrayTypeAdapter.FACTORY) - .factory(TypeAdapters.ENUM_FACTORY) - .factory(reflectiveTypeAdapterFactory); + factories.add(new GsonToMiniGsonTypeAdapterFactory(this, serializers, deserializers)); + factories.add(new CollectionTypeAdapterFactory(constructorConstructor)); + 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(new MapTypeAdapterFactory(constructorConstructor, complexMapKeySerialization)); + factories.add(ArrayTypeAdapter.FACTORY); + factories.add(TypeAdapters.ENUM_FACTORY); + factories.add(reflectiveTypeAdapterFactory); - this.miniGson = builder.build(); + this.factories = Collections.unmodifiableList(factories); } private TypeAdapter doubleAdapter(boolean serializeSpecialFloatingPointValues) { @@ -363,6 +375,75 @@ public final class Gson { return new DisjunctionExclusionStrategy(strategies); } + /** + * Returns the type adapter for {@code} type. + * + * @throws IllegalArgumentException if this GSON cannot serialize and + * deserialize {@code type}. + */ + public TypeAdapter getAdapter(TypeToken type) { + // TODO: cache? + + Map, FutureTypeAdapter> threadCalls = calls.get(); + @SuppressWarnings("unchecked") // the key and value type parameters always agree + FutureTypeAdapter ongoingCall = (FutureTypeAdapter) threadCalls.get(type); + if (ongoingCall != null) { + return ongoingCall; + } + + FutureTypeAdapter call = new FutureTypeAdapter(); + threadCalls.put(type, call); + try { + for (TypeAdapter.Factory factory : factories) { + TypeAdapter candidate = factory.create(this, type); + if (candidate != null) { + call.setDelegate(candidate); + return candidate; + } + } + throw new IllegalArgumentException("GSON cannot handle " + type); + } finally { + threadCalls.remove(type); + } + } + + /** + * Returns a type adapter for {@code} type that isn't {@code skipPast}. This + * can be used for type adapters to compose other, simpler type adapters. + * + * @throws IllegalArgumentException if this GSON cannot serialize and + * deserialize {@code type}. + */ + public TypeAdapter getNextAdapter(TypeAdapter.Factory skipPast, TypeToken type) { + boolean skipPastFound = false; + + for (TypeAdapter.Factory factory : factories) { + if (!skipPastFound) { + if (factory == skipPast) { + skipPastFound = true; + } + continue; + } + + TypeAdapter 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 TypeAdapter getAdapter(Class 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 @@ -501,7 +582,7 @@ public final class Gson { */ @SuppressWarnings("unchecked") public void toJson(Object src, Type typeOfSrc, JsonWriter writer) throws JsonIOException { - TypeAdapter adapter = miniGson.getAdapter(TypeToken.get(typeOfSrc)); + TypeAdapter adapter = getAdapter(TypeToken.get(typeOfSrc)); boolean oldLenient = writer.isLenient(); writer.setLenient(true); boolean oldHtmlSafe = writer.isHtmlSafe(); @@ -716,7 +797,7 @@ public final class Gson { try { reader.peek(); isEmpty = false; - TypeAdapter typeAdapter = (TypeAdapter) miniGson.getAdapter(TypeToken.get(typeOfT)); + TypeAdapter typeAdapter = (TypeAdapter) getAdapter(TypeToken.get(typeOfT)); return typeAdapter.read(reader); } catch (EOFException e) { /* @@ -783,6 +864,31 @@ public final class Gson { return (T) fromJson(new JsonElementReader(json), typeOfT); } + static class FutureTypeAdapter extends TypeAdapter { + private TypeAdapter delegate; + + public void setDelegate(TypeAdapter typeAdapter) { + if (delegate != null) { + throw new AssertionError(); + } + delegate = typeAdapter; + } + + @Override public T read(JsonReader reader) throws IOException { + if (delegate == null) { + throw new IllegalStateException(); + } + return delegate.read(reader); + } + + @Override public void write(JsonWriter writer, T value) throws IOException { + if (delegate == null) { + throw new IllegalStateException(); + } + delegate.write(writer, value); + } + } + @Override public String toString() { StringBuilder sb = new StringBuilder("{") @@ -797,5 +903,4 @@ public final class Gson { .append("}"); return sb.toString(); } - } diff --git a/gson/src/main/java/com/google/gson/GsonBuilder.java b/gson/src/main/java/com/google/gson/GsonBuilder.java index 096cfc2a..06d3fe82 100644 --- a/gson/src/main/java/com/google/gson/GsonBuilder.java +++ b/gson/src/main/java/com/google/gson/GsonBuilder.java @@ -20,7 +20,8 @@ import com.google.gson.DefaultTypeAdapters.DefaultDateTypeAdapter; import com.google.gson.internal.$Gson$Preconditions; import com.google.gson.internal.ParameterizedTypeHandlerMap; import com.google.gson.internal.Primitives; -import com.google.gson.internal.bind.TypeAdapter; +import com.google.gson.internal.bind.TypeAdapters; +import com.google.gson.reflect.TypeToken; import java.lang.reflect.Type; import java.sql.Timestamp; import java.text.DateFormat; @@ -133,6 +134,31 @@ public final class GsonBuilder { generateNonExecutableJson = false; } + // TODO: nice documentation + public GsonBuilder factory(TypeAdapter.Factory factory) { + typeAdapterFactories.add(factory); + return this; + } + + // TODO: nice documentation + public GsonBuilder typeAdapter(final Class type, final TypeAdapter typeAdapter) { + typeAdapterFactories.add(TypeAdapters.newFactory(type, typeAdapter)); + return this; + } + + // TODO: nice documentation + // TODO: accept a Type instead of a TypeToken? It's less typesafe but more Gson-like + public GsonBuilder typeAdapter(TypeToken type, TypeAdapter typeAdapter) { + typeAdapterFactories.add(TypeAdapters.newFactory(type, typeAdapter)); + return this; + } + + // TODO: nice documentation + public GsonBuilder typeHierarchyAdapter(Class type, TypeAdapter typeAdapter) { + typeAdapterFactories.add(TypeAdapters.newTypeHierarchyFactory(type, typeAdapter)); + return this; + } + /** * Configures Gson to enable versioning support. * diff --git a/gson/src/main/java/com/google/gson/GsonToMiniGsonTypeAdapterFactory.java b/gson/src/main/java/com/google/gson/GsonToMiniGsonTypeAdapterFactory.java index 0d872965..f5bfe349 100644 --- a/gson/src/main/java/com/google/gson/GsonToMiniGsonTypeAdapterFactory.java +++ b/gson/src/main/java/com/google/gson/GsonToMiniGsonTypeAdapterFactory.java @@ -17,8 +17,6 @@ package com.google.gson; import com.google.gson.internal.ParameterizedTypeHandlerMap; import com.google.gson.internal.Streams; -import com.google.gson.internal.bind.MiniGson; -import com.google.gson.internal.bind.TypeAdapter; import com.google.gson.reflect.TypeToken; import com.google.gson.stream.JsonReader; import com.google.gson.stream.JsonWriter; @@ -53,7 +51,7 @@ final class GsonToMiniGsonTypeAdapterFactory implements TypeAdapter.Factory { }; } - public TypeAdapter create(final MiniGson context, final TypeToken typeToken) { + public TypeAdapter create(final Gson context, final TypeToken typeToken) { final Type type = typeToken.getType(); @SuppressWarnings("unchecked") // guaranteed to match typeOfT diff --git a/gson/src/main/java/com/google/gson/internal/bind/TypeAdapter.java b/gson/src/main/java/com/google/gson/TypeAdapter.java similarity index 90% rename from gson/src/main/java/com/google/gson/internal/bind/TypeAdapter.java rename to gson/src/main/java/com/google/gson/TypeAdapter.java index bedb778b..6e4bf7b5 100644 --- a/gson/src/main/java/com/google/gson/internal/bind/TypeAdapter.java +++ b/gson/src/main/java/com/google/gson/TypeAdapter.java @@ -14,10 +14,10 @@ * limitations under the License. */ -package com.google.gson.internal.bind; +package com.google.gson; -import com.google.gson.JsonElement; -import com.google.gson.JsonIOException; +import com.google.gson.internal.bind.JsonElementReader; +import com.google.gson.internal.bind.JsonElementWriter; import com.google.gson.reflect.TypeToken; import com.google.gson.stream.JsonReader; import com.google.gson.stream.JsonWriter; @@ -27,6 +27,8 @@ import java.io.StringReader; import java.io.StringWriter; import java.io.Writer; +// TODO: nice documentation + public abstract class TypeAdapter { public abstract T read(JsonReader reader) throws IOException; public abstract void write(JsonWriter writer, T value) throws IOException; @@ -74,6 +76,6 @@ public abstract class TypeAdapter { } public interface Factory { - TypeAdapter create(MiniGson context, TypeToken type); + TypeAdapter create(Gson context, TypeToken type); } } diff --git a/gson/src/main/java/com/google/gson/internal/bind/ArrayTypeAdapter.java b/gson/src/main/java/com/google/gson/internal/bind/ArrayTypeAdapter.java index 3a254b5a..eff8f720 100644 --- a/gson/src/main/java/com/google/gson/internal/bind/ArrayTypeAdapter.java +++ b/gson/src/main/java/com/google/gson/internal/bind/ArrayTypeAdapter.java @@ -16,6 +16,8 @@ package com.google.gson.internal.bind; +import com.google.gson.Gson; +import com.google.gson.TypeAdapter; import java.io.IOException; import java.lang.reflect.Array; import java.lang.reflect.GenericArrayType; @@ -35,7 +37,7 @@ import com.google.gson.stream.JsonWriter; public final class ArrayTypeAdapter extends TypeAdapter { public static final Factory FACTORY = new Factory() { @SuppressWarnings({"unchecked", "rawtypes"}) - public TypeAdapter create(MiniGson context, TypeToken typeToken) { + public TypeAdapter create(Gson context, TypeToken typeToken) { Type type = typeToken.getType(); if (!(type instanceof GenericArrayType || type instanceof Class && ((Class) type).isArray())) { return null; @@ -53,7 +55,7 @@ public final class ArrayTypeAdapter extends TypeAdapter { private final Class componentType; private final TypeAdapter componentTypeAdapter; - public ArrayTypeAdapter(MiniGson context, TypeAdapter componentTypeAdapter, Class componentType) { + public ArrayTypeAdapter(Gson context, TypeAdapter componentTypeAdapter, Class componentType) { this.componentTypeAdapter = new TypeAdapterRuntimeTypeWrapper(context, componentTypeAdapter, componentType); this.componentType = componentType; diff --git a/gson/src/main/java/com/google/gson/internal/bind/BigDecimalTypeAdapter.java b/gson/src/main/java/com/google/gson/internal/bind/BigDecimalTypeAdapter.java index 73b718a5..a728a63a 100644 --- a/gson/src/main/java/com/google/gson/internal/bind/BigDecimalTypeAdapter.java +++ b/gson/src/main/java/com/google/gson/internal/bind/BigDecimalTypeAdapter.java @@ -17,6 +17,7 @@ package com.google.gson.internal.bind; import com.google.gson.JsonSyntaxException; +import com.google.gson.TypeAdapter; import com.google.gson.stream.JsonReader; import com.google.gson.stream.JsonToken; import com.google.gson.stream.JsonWriter; diff --git a/gson/src/main/java/com/google/gson/internal/bind/BigIntegerTypeAdapter.java b/gson/src/main/java/com/google/gson/internal/bind/BigIntegerTypeAdapter.java index 19543527..e419b4c8 100644 --- a/gson/src/main/java/com/google/gson/internal/bind/BigIntegerTypeAdapter.java +++ b/gson/src/main/java/com/google/gson/internal/bind/BigIntegerTypeAdapter.java @@ -17,6 +17,7 @@ package com.google.gson.internal.bind; import com.google.gson.JsonSyntaxException; +import com.google.gson.TypeAdapter; import com.google.gson.stream.JsonReader; import com.google.gson.stream.JsonToken; import com.google.gson.stream.JsonWriter; diff --git a/gson/src/main/java/com/google/gson/internal/bind/CollectionTypeAdapterFactory.java b/gson/src/main/java/com/google/gson/internal/bind/CollectionTypeAdapterFactory.java index 2082495f..669a8d4d 100644 --- a/gson/src/main/java/com/google/gson/internal/bind/CollectionTypeAdapterFactory.java +++ b/gson/src/main/java/com/google/gson/internal/bind/CollectionTypeAdapterFactory.java @@ -16,6 +16,8 @@ package com.google.gson.internal.bind; +import com.google.gson.Gson; +import com.google.gson.TypeAdapter; import com.google.gson.internal.$Gson$Types; import com.google.gson.internal.ConstructorConstructor; import com.google.gson.internal.ObjectConstructor; @@ -37,7 +39,7 @@ public final class CollectionTypeAdapterFactory implements TypeAdapter.Factory { this.constructorConstructor = constructorConstructor; } - public TypeAdapter create(MiniGson context, TypeToken typeToken) { + public TypeAdapter create(Gson context, TypeToken typeToken) { Type type = typeToken.getType(); Class rawType = typeToken.getRawType(); @@ -58,7 +60,7 @@ public final class CollectionTypeAdapterFactory implements TypeAdapter.Factory { private final TypeAdapter elementTypeAdapter; private final ObjectConstructor> constructor; - public Adapter(MiniGson context, Type elementType, + public Adapter(Gson context, Type elementType, TypeAdapter elementTypeAdapter, ObjectConstructor> constructor) { this.elementTypeAdapter = diff --git a/gson/src/main/java/com/google/gson/internal/bind/DateTypeAdapter.java b/gson/src/main/java/com/google/gson/internal/bind/DateTypeAdapter.java index 51f40c01..ef130745 100644 --- a/gson/src/main/java/com/google/gson/internal/bind/DateTypeAdapter.java +++ b/gson/src/main/java/com/google/gson/internal/bind/DateTypeAdapter.java @@ -16,7 +16,9 @@ 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.reflect.TypeToken; import com.google.gson.stream.JsonReader; import com.google.gson.stream.JsonToken; @@ -38,7 +40,7 @@ import java.util.TimeZone; public final class DateTypeAdapter extends TypeAdapter { public static final Factory FACTORY = new Factory() { @SuppressWarnings("unchecked") // we use a runtime check to make sure the 'T's equal - public TypeAdapter create(MiniGson context, TypeToken typeToken) { + public TypeAdapter create(Gson context, TypeToken typeToken) { return typeToken.getRawType() == Date.class ? (TypeAdapter) new DateTypeAdapter() : null; } }; diff --git a/gson/src/main/java/com/google/gson/internal/bind/ExcludedTypeAdapterFactory.java b/gson/src/main/java/com/google/gson/internal/bind/ExcludedTypeAdapterFactory.java index 1e253556..a5350901 100644 --- a/gson/src/main/java/com/google/gson/internal/bind/ExcludedTypeAdapterFactory.java +++ b/gson/src/main/java/com/google/gson/internal/bind/ExcludedTypeAdapterFactory.java @@ -17,6 +17,8 @@ package com.google.gson.internal.bind; import com.google.gson.ExclusionStrategy; +import com.google.gson.Gson; +import com.google.gson.TypeAdapter; import com.google.gson.reflect.TypeToken; import com.google.gson.stream.JsonReader; import com.google.gson.stream.JsonWriter; @@ -36,7 +38,7 @@ public final class ExcludedTypeAdapterFactory implements TypeAdapter.Factory { this.deserializationExclusionStrategy = deserializationExclusionStrategy; } - public TypeAdapter create(final MiniGson context, final TypeToken type) { + public TypeAdapter create(final Gson context, final TypeToken type) { Class rawType = type.getRawType(); final boolean skipSerialize = serializationExclusionStrategy.shouldSkipClass(rawType); final boolean skipDeserialize = deserializationExclusionStrategy.shouldSkipClass(rawType); diff --git a/gson/src/main/java/com/google/gson/internal/bind/MapTypeAdapterFactory.java b/gson/src/main/java/com/google/gson/internal/bind/MapTypeAdapterFactory.java index b44ccae5..032dbd76 100644 --- a/gson/src/main/java/com/google/gson/internal/bind/MapTypeAdapterFactory.java +++ b/gson/src/main/java/com/google/gson/internal/bind/MapTypeAdapterFactory.java @@ -16,9 +16,11 @@ 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.internal.$Gson$Types; import com.google.gson.internal.ConstructorConstructor; import com.google.gson.internal.ObjectConstructor; @@ -108,7 +110,7 @@ public final class MapTypeAdapterFactory implements TypeAdapter.Factory { this.complexMapKeySerialization = complexMapKeySerialization; } - public TypeAdapter create(MiniGson context, TypeToken typeToken) { + public TypeAdapter create(Gson context, TypeToken typeToken) { Type type = typeToken.getType(); Class rawType = typeToken.getRawType(); @@ -132,7 +134,7 @@ public final class MapTypeAdapterFactory implements TypeAdapter.Factory { /** * Returns a type adapter that writes the value as a string. */ - private TypeAdapter getKeyAdapter(MiniGson context, Type keyType) { + private TypeAdapter getKeyAdapter(Gson context, Type keyType) { return (keyType == boolean.class || keyType == Boolean.class) ? TypeAdapters.BOOLEAN_AS_STRING : context.getAdapter(TypeToken.get(keyType)); @@ -143,7 +145,7 @@ public final class MapTypeAdapterFactory implements TypeAdapter.Factory { private final TypeAdapter valueTypeAdapter; private final ObjectConstructor> constructor; - public Adapter(MiniGson context, Type keyType, TypeAdapter keyTypeAdapter, + public Adapter(Gson context, Type keyType, TypeAdapter keyTypeAdapter, Type valueType, TypeAdapter valueTypeAdapter, ObjectConstructor> constructor) { this.keyTypeAdapter = diff --git a/gson/src/main/java/com/google/gson/internal/bind/MiniGson.java b/gson/src/main/java/com/google/gson/internal/bind/MiniGson.java deleted file mode 100644 index 64919623..00000000 --- a/gson/src/main/java/com/google/gson/internal/bind/MiniGson.java +++ /dev/null @@ -1,206 +0,0 @@ -/* - * 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.internal.ConstructorConstructor; -import com.google.gson.reflect.TypeToken; -import com.google.gson.stream.JsonReader; -import com.google.gson.stream.JsonWriter; -import java.io.IOException; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -/** - * A basic binding between JSON and Java objects. - */ -public final class MiniGson { - /** - * 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, FutureTypeAdapter>> calls - = new ThreadLocal, FutureTypeAdapter>>() { - @Override protected Map, FutureTypeAdapter> initialValue() { - return new HashMap, FutureTypeAdapter>(); - } - }; - - private final List factories; - - private MiniGson(Builder builder) { - ConstructorConstructor constructorConstructor = new ConstructorConstructor(); - List factories = new ArrayList(); - if (builder.addDefaultFactories) { - factories.add(TypeAdapters.BOOLEAN_FACTORY); - factories.add(TypeAdapters.INTEGER_FACTORY); - factories.add(TypeAdapters.DOUBLE_FACTORY); - factories.add(TypeAdapters.FLOAT_FACTORY); - factories.add(TypeAdapters.LONG_FACTORY); - factories.add(TypeAdapters.STRING_FACTORY); - } - factories.addAll(builder.factories); - if (builder.addDefaultFactories) { - factories.add(new CollectionTypeAdapterFactory(constructorConstructor)); - factories.add(new StringToValueMapTypeAdapterFactory(constructorConstructor)); - factories.add(ArrayTypeAdapter.FACTORY); - factories.add(ObjectTypeAdapter.FACTORY); - factories.add(new ReflectiveTypeAdapterFactory(constructorConstructor)); - } - this.factories = Collections.unmodifiableList(factories); - } - - /** - * Returns the type adapter for {@code} type. - * - * @throws IllegalArgumentException if this GSON cannot serialize and - * deserialize {@code type}. - */ - public TypeAdapter getAdapter(TypeToken type) { - // TODO: create a cache! - - Map, FutureTypeAdapter> threadCalls = calls.get(); - @SuppressWarnings("unchecked") // the key and value type parameters always agree - FutureTypeAdapter ongoingCall = (FutureTypeAdapter) threadCalls.get(type); - if (ongoingCall != null) { - return ongoingCall; - } - - FutureTypeAdapter call = new FutureTypeAdapter(); - threadCalls.put(type, call); - try { - for (TypeAdapter.Factory factory : factories) { - TypeAdapter candidate = factory.create(this, type); - if (candidate != null) { - call.setDelegate(candidate); - return candidate; - } - } - throw new IllegalArgumentException("This MiniGSON cannot handle " + type); - } finally { - threadCalls.remove(type); - } - } - - /** - * Returns a type adapter for {@code} type that isn't {@code skipPast}. This - * can be used for type adapters to compose other, simpler type adapters. - * - * @throws IllegalArgumentException if this GSON cannot serialize and - * deserialize {@code type}. - */ - public TypeAdapter getNextAdapter(TypeAdapter.Factory skipPast, TypeToken type) { - boolean skipPastFound = false; - - for (TypeAdapter.Factory factory : factories) { - if (!skipPastFound) { - if (factory == skipPast) { - skipPastFound = true; - } - continue; - } - - TypeAdapter candidate = factory.create(this, type); - if (candidate != null) { - return candidate; - } - } - - throw new IllegalArgumentException("This MiniGSON cannot serialize " + type); - } - - static class FutureTypeAdapter extends TypeAdapter { - private TypeAdapter delegate; - - public void setDelegate(TypeAdapter typeAdapter) { - if (delegate != null) { - throw new AssertionError(); - } - delegate = typeAdapter; - } - - @Override public T read(JsonReader reader) throws IOException { - if (delegate == null) { - throw new IllegalStateException(); - } - return delegate.read(reader); - } - - @Override public void write(JsonWriter writer, T value) throws IOException { - if (delegate == null) { - throw new IllegalStateException(); - } - delegate.write(writer, value); - } - } - - /** - * Returns the type adapter for {@code} type. - * - * @throws IllegalArgumentException if this GSON cannot serialize and - * deserialize {@code type}. - */ - public TypeAdapter getAdapter(Class type) { - return getAdapter(TypeToken.get(type)); - } - - /** - * Returns the type adapters of this context in order of precedence. - */ - public List getFactories() { - return factories; - } - - public static final class Builder { - private final List factories = new ArrayList(); - boolean addDefaultFactories = true; - - public Builder factory(TypeAdapter.Factory factory) { - factories.add(factory); - return this; - } - - public Builder withoutDefaultFactories() { - this.addDefaultFactories = false; - return this; - } - - public Builder typeAdapter(final Class type, final TypeAdapter typeAdapter) { - factories.add(TypeAdapters.newFactory(type, typeAdapter)); - return this; - } - - public Builder typeAdapter(TypeToken type, TypeAdapter typeAdapter) { - factories.add(TypeAdapters.newFactory(type, typeAdapter)); - return this; - } - - public Builder typeHierarchyAdapter(Class type, TypeAdapter typeAdapter) { - factories.add(TypeAdapters.newTypeHierarchyFactory(type, typeAdapter)); - return this; - } - - public MiniGson build() { - return new MiniGson(this); - } - } -} diff --git a/gson/src/main/java/com/google/gson/internal/bind/ObjectTypeAdapter.java b/gson/src/main/java/com/google/gson/internal/bind/ObjectTypeAdapter.java index 3e395cdb..6b1619f5 100644 --- a/gson/src/main/java/com/google/gson/internal/bind/ObjectTypeAdapter.java +++ b/gson/src/main/java/com/google/gson/internal/bind/ObjectTypeAdapter.java @@ -16,6 +16,8 @@ package com.google.gson.internal.bind; +import com.google.gson.Gson; +import com.google.gson.TypeAdapter; import com.google.gson.reflect.TypeToken; import com.google.gson.stream.JsonReader; import com.google.gson.stream.JsonToken; @@ -33,7 +35,7 @@ import java.util.Map; public final class ObjectTypeAdapter extends TypeAdapter { public static final Factory FACTORY = new Factory() { @SuppressWarnings("unchecked") - public TypeAdapter create(MiniGson context, TypeToken type) { + public TypeAdapter create(Gson context, TypeToken type) { if (type.getRawType() == Object.class) { return (TypeAdapter) new ObjectTypeAdapter(context); } @@ -41,10 +43,10 @@ public final class ObjectTypeAdapter extends TypeAdapter { } }; - private final MiniGson miniGson; + private final Gson gson; - private ObjectTypeAdapter(MiniGson miniGson) { - this.miniGson = miniGson; + private ObjectTypeAdapter(Gson gson) { + this.gson = gson; } @Override public Object read(JsonReader reader) throws IOException { @@ -92,7 +94,7 @@ public final class ObjectTypeAdapter extends TypeAdapter { return; } - TypeAdapter typeAdapter = (TypeAdapter) miniGson.getAdapter(value.getClass()); + TypeAdapter typeAdapter = (TypeAdapter) gson.getAdapter(value.getClass()); if (typeAdapter instanceof ObjectTypeAdapter) { writer.beginObject(); writer.endObject(); diff --git a/gson/src/main/java/com/google/gson/internal/bind/ReflectiveTypeAdapterFactory.java b/gson/src/main/java/com/google/gson/internal/bind/ReflectiveTypeAdapterFactory.java index 272df92e..ebbb920c 100644 --- a/gson/src/main/java/com/google/gson/internal/bind/ReflectiveTypeAdapterFactory.java +++ b/gson/src/main/java/com/google/gson/internal/bind/ReflectiveTypeAdapterFactory.java @@ -16,7 +16,9 @@ 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.internal.$Gson$Types; import com.google.gson.internal.ConstructorConstructor; import com.google.gson.internal.ObjectConstructor; @@ -54,7 +56,7 @@ public class ReflectiveTypeAdapterFactory implements TypeAdapter.Factory { return f.getName(); } - public TypeAdapter create(MiniGson context, final TypeToken type) { + public TypeAdapter create(Gson context, final TypeToken type) { Class raw = type.getRawType(); if (!Object.class.isAssignableFrom(raw)) { @@ -66,7 +68,7 @@ public class ReflectiveTypeAdapterFactory implements TypeAdapter.Factory { } private ReflectiveTypeAdapterFactory.BoundField createBoundField( - final MiniGson context, final Field field, final String name, + final Gson context, final Field field, final String name, final TypeToken fieldType, boolean serialize, boolean deserialize) { final boolean isPrimitive = Primitives.isPrimitive(fieldType.getRawType()); @@ -92,7 +94,7 @@ public class ReflectiveTypeAdapterFactory implements TypeAdapter.Factory { } private Map getBoundFields( - MiniGson context, TypeToken type, Class raw) { + Gson context, TypeToken type, Class raw) { Map result = new LinkedHashMap(); if (raw.isInterface()) { return result; diff --git a/gson/src/main/java/com/google/gson/internal/bind/SqlDateTypeAdapter.java b/gson/src/main/java/com/google/gson/internal/bind/SqlDateTypeAdapter.java index 1fc7850c..83508783 100644 --- a/gson/src/main/java/com/google/gson/internal/bind/SqlDateTypeAdapter.java +++ b/gson/src/main/java/com/google/gson/internal/bind/SqlDateTypeAdapter.java @@ -16,7 +16,9 @@ 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.reflect.TypeToken; import com.google.gson.stream.JsonReader; import com.google.gson.stream.JsonToken; @@ -35,7 +37,7 @@ import java.text.SimpleDateFormat; public final class SqlDateTypeAdapter extends TypeAdapter { public static final Factory FACTORY = new Factory() { @SuppressWarnings("unchecked") // we use a runtime check to make sure the 'T's equal - public TypeAdapter create(MiniGson context, TypeToken typeToken) { + public TypeAdapter create(Gson context, TypeToken typeToken) { return typeToken.getRawType() == java.sql.Date.class ? (TypeAdapter) new SqlDateTypeAdapter() : null; } diff --git a/gson/src/main/java/com/google/gson/internal/bind/StringToValueMapTypeAdapterFactory.java b/gson/src/main/java/com/google/gson/internal/bind/StringToValueMapTypeAdapterFactory.java index 9040973f..47f94443 100644 --- a/gson/src/main/java/com/google/gson/internal/bind/StringToValueMapTypeAdapterFactory.java +++ b/gson/src/main/java/com/google/gson/internal/bind/StringToValueMapTypeAdapterFactory.java @@ -16,6 +16,8 @@ package com.google.gson.internal.bind; +import com.google.gson.Gson; +import com.google.gson.TypeAdapter; import com.google.gson.internal.$Gson$Types; import com.google.gson.internal.ConstructorConstructor; import com.google.gson.internal.ObjectConstructor; @@ -38,7 +40,7 @@ public final class StringToValueMapTypeAdapterFactory implements TypeAdapter.Fac this.constructorConstructor = constructorConstructor; } - public TypeAdapter create(MiniGson context, TypeToken typeToken) { + public TypeAdapter create(Gson context, TypeToken typeToken) { Type type = typeToken.getType(); if (!(type instanceof ParameterizedType)) { return null; diff --git a/gson/src/main/java/com/google/gson/internal/bind/TimeTypeAdapter.java b/gson/src/main/java/com/google/gson/internal/bind/TimeTypeAdapter.java index 9b40d308..c91f4a3a 100644 --- a/gson/src/main/java/com/google/gson/internal/bind/TimeTypeAdapter.java +++ b/gson/src/main/java/com/google/gson/internal/bind/TimeTypeAdapter.java @@ -16,7 +16,9 @@ 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.reflect.TypeToken; import com.google.gson.stream.JsonReader; import com.google.gson.stream.JsonToken; @@ -37,7 +39,7 @@ import java.util.Date; public final class TimeTypeAdapter extends TypeAdapter