From 7e760143fdc8b689daeebdee33ad6ea7d0a62a56 Mon Sep 17 00:00:00 2001 From: Jesse Wilson Date: Sun, 20 Nov 2011 15:23:08 +0000 Subject: [PATCH] Smash together MiniGson and Gson. This changes Gson to be the MiniGson rather than delegating to the MiniGson. It means that the MiniGson objects passed into streaming type adapters are now fully-capable 'Gson' objects. The most notable impact of this change is that it adds several new public APIs: - The TypeAdapter abstract class - The TypeAdapter.Factory interface - Four new methods on GsonBuilder to register streaming TypeAdapters (via Factory, via Class, via TypeToken, and as a type hierarchy) - Three new methods on Gson to lookup streaming TypeAdapters (by type, by class, and to get the next type adapter) Still outstanding: - Write beautiful prose to document the new APIs above - Change GsonBuilder's precedence so that both old and new-style type adapters are registered in one lot --- gson/src/main/java/com/google/gson/Gson.java | 205 ++++++++++++----- .../java/com/google/gson/GsonBuilder.java | 28 ++- .../GsonToMiniGsonTypeAdapterFactory.java | 4 +- .../gson/{internal/bind => }/TypeAdapter.java | 10 +- .../gson/internal/bind/ArrayTypeAdapter.java | 6 +- .../internal/bind/BigDecimalTypeAdapter.java | 1 + .../internal/bind/BigIntegerTypeAdapter.java | 1 + .../bind/CollectionTypeAdapterFactory.java | 6 +- .../gson/internal/bind/DateTypeAdapter.java | 4 +- .../bind/ExcludedTypeAdapterFactory.java | 4 +- .../internal/bind/MapTypeAdapterFactory.java | 8 +- .../google/gson/internal/bind/MiniGson.java | 206 ------------------ .../gson/internal/bind/ObjectTypeAdapter.java | 12 +- .../bind/ReflectiveTypeAdapterFactory.java | 8 +- .../internal/bind/SqlDateTypeAdapter.java | 4 +- .../StringToValueMapTypeAdapterFactory.java | 4 +- .../gson/internal/bind/TimeTypeAdapter.java | 4 +- .../bind/TypeAdapterRuntimeTypeWrapper.java | 6 +- .../gson/internal/bind/TypeAdapters.java | 16 +- .../FunctionWithInternalDependenciesTest.java | 1 - .../google/gson/ObjectTypeAdapterTest.java | 14 +- .../StreamingTypeAdaptersTest.java} | 13 +- 22 files changed, 258 insertions(+), 307 deletions(-) rename gson/src/main/java/com/google/gson/{internal/bind => }/TypeAdapter.java (90%) delete mode 100644 gson/src/main/java/com/google/gson/internal/bind/MiniGson.java rename gson/src/test/java/com/google/gson/{internal/bind/MiniGsonTest.java => functional/StreamingTypeAdaptersTest.java} (95%) 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