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
This commit is contained in:
Jesse Wilson 2011-11-20 15:23:08 +00:00
parent df31d2db16
commit 7e760143fd
22 changed files with 258 additions and 307 deletions

View File

@ -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<Map<TypeToken<?>, FutureTypeAdapter<?>>> calls
= new ThreadLocal<Map<TypeToken<?>, FutureTypeAdapter<?>>>() {
@Override protected Map<TypeToken<?>, FutureTypeAdapter<?>> initialValue() {
return new HashMap<TypeToken<?>, FutureTypeAdapter<?>>();
}
};
private final List<TypeAdapter.Factory> 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<TypeAdapter.Factory> factories = new ArrayList<TypeAdapter.Factory>();
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<Number> 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 <T> TypeAdapter<T> getAdapter(TypeToken<T> type) {
// TODO: cache?
Map<TypeToken<?>, FutureTypeAdapter<?>> threadCalls = calls.get();
@SuppressWarnings("unchecked") // the key and value type parameters always agree
FutureTypeAdapter<T> ongoingCall = (FutureTypeAdapter<T>) threadCalls.get(type);
if (ongoingCall != null) {
return ongoingCall;
}
FutureTypeAdapter<T> call = new FutureTypeAdapter<T>();
threadCalls.put(type, call);
try {
for (TypeAdapter.Factory factory : factories) {
TypeAdapter<T> 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 <T> TypeAdapter<T> getNextAdapter(TypeAdapter.Factory skipPast, TypeToken<T> type) {
boolean skipPastFound = false;
for (TypeAdapter.Factory 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
@ -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<T> typeAdapter = (TypeAdapter<T>) miniGson.getAdapter(TypeToken.get(typeOfT));
TypeAdapter<T> typeAdapter = (TypeAdapter<T>) 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<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 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();
}
}

View File

@ -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 <T> GsonBuilder typeAdapter(final Class<T> type, final TypeAdapter<T> 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 <T> GsonBuilder typeAdapter(TypeToken<T> type, TypeAdapter<T> typeAdapter) {
typeAdapterFactories.add(TypeAdapters.newFactory(type, typeAdapter));
return this;
}
// TODO: nice documentation
public <T> GsonBuilder typeHierarchyAdapter(Class<T> type, TypeAdapter<T> typeAdapter) {
typeAdapterFactories.add(TypeAdapters.newTypeHierarchyFactory(type, typeAdapter));
return this;
}
/**
* Configures Gson to enable versioning support.
*

View File

@ -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 <T> TypeAdapter<T> create(final MiniGson context, final TypeToken<T> typeToken) {
public <T> TypeAdapter<T> create(final Gson context, final TypeToken<T> typeToken) {
final Type type = typeToken.getType();
@SuppressWarnings("unchecked") // guaranteed to match typeOfT

View File

@ -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<T> {
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<T> {
}
public interface Factory {
<T> TypeAdapter<T> create(MiniGson context, TypeToken<T> type);
<T> TypeAdapter<T> create(Gson context, TypeToken<T> type);
}
}

View File

@ -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<E> extends TypeAdapter<Object> {
public static final Factory FACTORY = new Factory() {
@SuppressWarnings({"unchecked", "rawtypes"})
public <T> TypeAdapter<T> create(MiniGson context, TypeToken<T> typeToken) {
public <T> TypeAdapter<T> create(Gson context, TypeToken<T> 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<E> extends TypeAdapter<Object> {
private final Class<E> componentType;
private final TypeAdapter<E> componentTypeAdapter;
public ArrayTypeAdapter(MiniGson context, TypeAdapter<E> componentTypeAdapter, Class<E> componentType) {
public ArrayTypeAdapter(Gson context, TypeAdapter<E> componentTypeAdapter, Class<E> componentType) {
this.componentTypeAdapter =
new TypeAdapterRuntimeTypeWrapper<E>(context, componentTypeAdapter, componentType);
this.componentType = componentType;

View File

@ -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;

View File

@ -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;

View File

@ -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 <T> TypeAdapter<T> create(MiniGson context, TypeToken<T> typeToken) {
public <T> TypeAdapter<T> create(Gson context, TypeToken<T> typeToken) {
Type type = typeToken.getType();
Class<? super T> rawType = typeToken.getRawType();
@ -58,7 +60,7 @@ public final class CollectionTypeAdapterFactory implements TypeAdapter.Factory {
private final TypeAdapter<E> elementTypeAdapter;
private final ObjectConstructor<? extends Collection<E>> constructor;
public Adapter(MiniGson context, Type elementType,
public Adapter(Gson context, Type elementType,
TypeAdapter<E> elementTypeAdapter,
ObjectConstructor<? extends Collection<E>> constructor) {
this.elementTypeAdapter =

View File

@ -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<Date> {
public static final Factory FACTORY = new Factory() {
@SuppressWarnings("unchecked") // we use a runtime check to make sure the 'T's equal
public <T> TypeAdapter<T> create(MiniGson context, TypeToken<T> typeToken) {
public <T> TypeAdapter<T> create(Gson context, TypeToken<T> typeToken) {
return typeToken.getRawType() == Date.class ? (TypeAdapter<T>) new DateTypeAdapter() : null;
}
};

View File

@ -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 <T> TypeAdapter<T> create(final MiniGson context, final TypeToken<T> type) {
public <T> TypeAdapter<T> create(final Gson context, final TypeToken<T> type) {
Class<?> rawType = type.getRawType();
final boolean skipSerialize = serializationExclusionStrategy.shouldSkipClass(rawType);
final boolean skipDeserialize = deserializationExclusionStrategy.shouldSkipClass(rawType);

View File

@ -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 <T> TypeAdapter<T> create(MiniGson context, TypeToken<T> typeToken) {
public <T> TypeAdapter<T> create(Gson context, TypeToken<T> typeToken) {
Type type = typeToken.getType();
Class<? super T> 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<V> valueTypeAdapter;
private final ObjectConstructor<? extends Map<K, V>> constructor;
public Adapter(MiniGson context, Type keyType, TypeAdapter<K> keyTypeAdapter,
public Adapter(Gson context, Type keyType, TypeAdapter<K> keyTypeAdapter,
Type valueType, TypeAdapter<V> valueTypeAdapter,
ObjectConstructor<? extends Map<K, V>> constructor) {
this.keyTypeAdapter =

View File

@ -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<Map<TypeToken<?>, FutureTypeAdapter<?>>> calls
= new ThreadLocal<Map<TypeToken<?>, FutureTypeAdapter<?>>>() {
@Override protected Map<TypeToken<?>, FutureTypeAdapter<?>> initialValue() {
return new HashMap<TypeToken<?>, FutureTypeAdapter<?>>();
}
};
private final List<TypeAdapter.Factory> factories;
private MiniGson(Builder builder) {
ConstructorConstructor constructorConstructor = new ConstructorConstructor();
List<TypeAdapter.Factory> factories = new ArrayList<TypeAdapter.Factory>();
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 <T> TypeAdapter<T> getAdapter(TypeToken<T> type) {
// TODO: create a cache!
Map<TypeToken<?>, FutureTypeAdapter<?>> threadCalls = calls.get();
@SuppressWarnings("unchecked") // the key and value type parameters always agree
FutureTypeAdapter<T> ongoingCall = (FutureTypeAdapter<T>) threadCalls.get(type);
if (ongoingCall != null) {
return ongoingCall;
}
FutureTypeAdapter<T> call = new FutureTypeAdapter<T>();
threadCalls.put(type, call);
try {
for (TypeAdapter.Factory factory : factories) {
TypeAdapter<T> 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 <T> TypeAdapter<T> getNextAdapter(TypeAdapter.Factory skipPast, TypeToken<T> type) {
boolean skipPastFound = false;
for (TypeAdapter.Factory 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("This MiniGSON cannot serialize " + type);
}
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 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 <T> TypeAdapter<T> getAdapter(Class<T> type) {
return getAdapter(TypeToken.get(type));
}
/**
* Returns the type adapters of this context in order of precedence.
*/
public List<TypeAdapter.Factory> getFactories() {
return factories;
}
public static final class Builder {
private final List<TypeAdapter.Factory> factories = new ArrayList<TypeAdapter.Factory>();
boolean addDefaultFactories = true;
public Builder factory(TypeAdapter.Factory factory) {
factories.add(factory);
return this;
}
public Builder withoutDefaultFactories() {
this.addDefaultFactories = false;
return this;
}
public <T> Builder typeAdapter(final Class<T> type, final TypeAdapter<T> typeAdapter) {
factories.add(TypeAdapters.newFactory(type, typeAdapter));
return this;
}
public <T> Builder typeAdapter(TypeToken<T> type, TypeAdapter<T> typeAdapter) {
factories.add(TypeAdapters.newFactory(type, typeAdapter));
return this;
}
public <T> Builder typeHierarchyAdapter(Class<T> type, TypeAdapter<T> typeAdapter) {
factories.add(TypeAdapters.newTypeHierarchyFactory(type, typeAdapter));
return this;
}
public MiniGson build() {
return new MiniGson(this);
}
}
}

View File

@ -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<Object> {
public static final Factory FACTORY = new Factory() {
@SuppressWarnings("unchecked")
public <T> TypeAdapter<T> create(MiniGson context, TypeToken<T> type) {
public <T> TypeAdapter<T> create(Gson context, TypeToken<T> type) {
if (type.getRawType() == Object.class) {
return (TypeAdapter<T>) new ObjectTypeAdapter(context);
}
@ -41,10 +43,10 @@ public final class ObjectTypeAdapter extends TypeAdapter<Object> {
}
};
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<Object> {
return;
}
TypeAdapter<Object> typeAdapter = (TypeAdapter<Object>) miniGson.getAdapter(value.getClass());
TypeAdapter<Object> typeAdapter = (TypeAdapter<Object>) gson.getAdapter(value.getClass());
if (typeAdapter instanceof ObjectTypeAdapter) {
writer.beginObject();
writer.endObject();

View File

@ -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 <T> TypeAdapter<T> create(MiniGson context, final TypeToken<T> type) {
public <T> TypeAdapter<T> create(Gson context, final TypeToken<T> type) {
Class<? super T> 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<String, BoundField> getBoundFields(
MiniGson context, TypeToken<?> type, Class<?> raw) {
Gson context, TypeToken<?> type, Class<?> raw) {
Map<String, BoundField> result = new LinkedHashMap<String, BoundField>();
if (raw.isInterface()) {
return result;

View File

@ -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<java.sql.Date> {
public static final Factory FACTORY = new Factory() {
@SuppressWarnings("unchecked") // we use a runtime check to make sure the 'T's equal
public <T> TypeAdapter<T> create(MiniGson context, TypeToken<T> typeToken) {
public <T> TypeAdapter<T> create(Gson context, TypeToken<T> typeToken) {
return typeToken.getRawType() == java.sql.Date.class
? (TypeAdapter<T>) new SqlDateTypeAdapter() : null;
}

View File

@ -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 <T> TypeAdapter<T> create(MiniGson context, TypeToken<T> typeToken) {
public <T> TypeAdapter<T> create(Gson context, TypeToken<T> typeToken) {
Type type = typeToken.getType();
if (!(type instanceof ParameterizedType)) {
return null;

View File

@ -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<Time> {
public static final Factory FACTORY = new Factory() {
@SuppressWarnings("unchecked") // we use a runtime check to make sure the 'T's equal
public <T> TypeAdapter<T> create(MiniGson context, TypeToken<T> typeToken) {
public <T> TypeAdapter<T> create(Gson context, TypeToken<T> typeToken) {
return typeToken.getRawType() == Time.class ? (TypeAdapter<T>) new TimeTypeAdapter() : null;
}
};

View File

@ -15,6 +15,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.JsonWriter;
@ -23,11 +25,11 @@ import java.lang.reflect.Type;
final class TypeAdapterRuntimeTypeWrapper<T> extends TypeAdapter<T> {
private final MiniGson context;
private final Gson context;
private final TypeAdapter<T> delegate;
private final Type type;
TypeAdapterRuntimeTypeWrapper(MiniGson context, TypeAdapter<T> delegate, Type type) {
TypeAdapterRuntimeTypeWrapper(Gson context, TypeAdapter<T> delegate, Type type) {
this.context = context;
this.delegate = delegate;
this.type = type;

View File

@ -16,6 +16,7 @@
package com.google.gson.internal.bind;
import com.google.gson.Gson;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonIOException;
@ -23,6 +24,7 @@ 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.internal.LazilyParsedNumber;
import com.google.gson.reflect.TypeToken;
import com.google.gson.stream.JsonReader;
@ -451,7 +453,7 @@ public final class TypeAdapters {
public static final TypeAdapter.Factory TIMESTAMP_FACTORY = new TypeAdapter.Factory() {
@SuppressWarnings("unchecked") // we use a runtime check to make sure the 'T's equal
public <T> TypeAdapter<T> create(MiniGson context, TypeToken<T> typeToken) {
public <T> TypeAdapter<T> create(Gson context, TypeToken<T> typeToken) {
if (typeToken.getRawType() != Timestamp.class) {
return null;
}
@ -674,7 +676,7 @@ public final class TypeAdapters {
public static <TT> TypeAdapter.Factory newEnumTypeHierarchyFactory(final Class<TT> clazz) {
return new TypeAdapter.Factory() {
@SuppressWarnings({"rawtypes", "unchecked"})
public <T> TypeAdapter<T> create(MiniGson context, TypeToken<T> typeToken) {
public <T> TypeAdapter<T> create(Gson context, TypeToken<T> typeToken) {
Class<? super T> rawType = typeToken.getRawType();
return clazz.isAssignableFrom(rawType)
? (TypeAdapter<T>) new EnumTypeAdapter(rawType) : null;
@ -686,7 +688,7 @@ public final class TypeAdapters {
final TypeToken<TT> type, final TypeAdapter<TT> typeAdapter) {
return new TypeAdapter.Factory() {
@SuppressWarnings("unchecked") // we use a runtime check to make sure the 'T's equal
public <T> TypeAdapter<T> create(MiniGson context, TypeToken<T> typeToken) {
public <T> TypeAdapter<T> create(Gson context, TypeToken<T> typeToken) {
return typeToken.equals(type) ? (TypeAdapter<T>) typeAdapter : null;
}
};
@ -696,7 +698,7 @@ public final class TypeAdapters {
final Class<TT> type, final TypeAdapter<TT> typeAdapter) {
return new TypeAdapter.Factory() {
@SuppressWarnings("unchecked") // we use a runtime check to make sure the 'T's equal
public <T> TypeAdapter<T> create(MiniGson context, TypeToken<T> typeToken) {
public <T> TypeAdapter<T> create(Gson context, TypeToken<T> typeToken) {
return typeToken.getRawType() == type ? (TypeAdapter<T>) typeAdapter : null;
}
@Override public String toString() {
@ -709,7 +711,7 @@ public final class TypeAdapters {
final Class<TT> unboxed, final Class<TT> boxed, final TypeAdapter<? super TT> typeAdapter) {
return new TypeAdapter.Factory() {
@SuppressWarnings("unchecked") // we use a runtime check to make sure the 'T's equal
public <T> TypeAdapter<T> create(MiniGson context, TypeToken<T> typeToken) {
public <T> TypeAdapter<T> create(Gson context, TypeToken<T> typeToken) {
Class<? super T> rawType = typeToken.getRawType();
return (rawType == unboxed || rawType == boxed) ? (TypeAdapter<T>) typeAdapter : null;
}
@ -724,7 +726,7 @@ public final class TypeAdapters {
final Class<TT> base, final Class<? extends TT> sub, final TypeAdapter<? super TT> typeAdapter) {
return new TypeAdapter.Factory() {
@SuppressWarnings("unchecked") // we use a runtime check to make sure the 'T's equal
public <T> TypeAdapter<T> create(MiniGson context, TypeToken<T> typeToken) {
public <T> TypeAdapter<T> create(Gson context, TypeToken<T> typeToken) {
Class<? super T> rawType = typeToken.getRawType();
return (rawType == base || rawType == sub) ? (TypeAdapter<T>) typeAdapter : null;
}
@ -739,7 +741,7 @@ public final class TypeAdapters {
final Class<TT> clazz, final TypeAdapter<TT> typeAdapter) {
return new TypeAdapter.Factory() {
@SuppressWarnings("unchecked")
public <T> TypeAdapter<T> create(MiniGson context, TypeToken<T> typeToken) {
public <T> TypeAdapter<T> create(Gson context, TypeToken<T> typeToken) {
return clazz.isAssignableFrom(typeToken.getRawType()) ? (TypeAdapter<T>) typeAdapter : null;
}
@Override public String toString() {

View File

@ -17,7 +17,6 @@
package com.google.gson;
import com.google.gson.common.TestTypes.ClassWithNoFields;
import com.google.gson.internal.bind.TypeAdapter;
import java.lang.reflect.Modifier;
import java.util.Collections;
import java.util.LinkedList;

View File

@ -16,15 +16,13 @@
package com.google.gson;
import com.google.gson.internal.bind.MiniGson;
import com.google.gson.internal.bind.TypeAdapter;
import java.util.Arrays;
import java.util.Collections;
import java.util.Map;
import junit.framework.TestCase;
public final class ObjectTypeAdapterTest extends TestCase {
private final MiniGson gson = new MiniGson.Builder().build();
private final Gson gson = new GsonBuilder().create();
private final TypeAdapter<Object> adapter = gson.getAdapter(Object.class);
public void testDeserialize() throws Exception {
@ -37,14 +35,16 @@ public final class ObjectTypeAdapterTest extends TestCase {
@SuppressWarnings("unused")
public void testSerialize() throws Exception {
Object object = new Object() {
Object a = 5;
Object b = Arrays.asList(1, 2, null);
};
Object object = new RuntimeType();
assertEquals("{'a':5,'b':[1,2,null]}", adapter.toJson(object).replace("\"", "'"));
}
public void testSerializeObject() throws Exception {
assertEquals("{}", adapter.toJson(new Object()));
}
private class RuntimeType {
Object a = 5;
Object b = Arrays.asList(1, 2, null);
}
}

View File

@ -14,10 +14,11 @@
* limitations under the License.
*/
package com.google.gson.internal.bind;
package com.google.gson.functional;
import com.google.gson.internal.bind.MiniGson;
import com.google.gson.internal.bind.TypeAdapter;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.TypeAdapter;
import com.google.gson.reflect.TypeToken;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonWriter;
@ -29,8 +30,8 @@ import java.util.List;
import java.util.Map;
import junit.framework.TestCase;
public final class MiniGsonTest extends TestCase {
private MiniGson miniGson = new MiniGson.Builder().build();
public final class StreamingTypeAdaptersTest extends TestCase {
private Gson miniGson = new GsonBuilder().create();
private TypeAdapter<Truck> truckAdapter = miniGson.getAdapter(Truck.class);
private TypeAdapter<Map<String, Double>> mapAdapter
= miniGson.getAdapter(new TypeToken<Map<String, Double>>() {});
@ -101,7 +102,7 @@ public final class MiniGsonTest extends TestCase {
writer.value(value.name);
}
};
miniGson = new MiniGson.Builder().typeAdapter(Person.class, personNameAdapter).build();
miniGson = new GsonBuilder().typeAdapter(Person.class, personNameAdapter).create();
truckAdapter = miniGson.getAdapter(Truck.class);
}