Register Gson 1.x tree-style adapters in the TypeAdapter.Factory list rather than in the ParameterizedTypeHandlerMap.
The motivation for this change is to give tree-style adapters precedence order in registration. This fixes the test I committed earlier today, where registration order was not honored. This renamed ParameterizedTypeHandlerMap to the shorter 'TypeMap'. For type adapters, this is now only used for type hierarchy. We still need non-hierarchy support in TypeMap for instance creators; I'll be looking for workarounds to see if further simplification is possible here.
This commit is contained in:
parent
777e17c723
commit
d391584d48
@ -17,9 +17,9 @@
|
||||
package com.google.gson;
|
||||
|
||||
import com.google.gson.internal.ConstructorConstructor;
|
||||
import com.google.gson.internal.ParameterizedTypeHandlerMap;
|
||||
import com.google.gson.internal.Primitives;
|
||||
import com.google.gson.internal.Streams;
|
||||
import com.google.gson.internal.TypeMap;
|
||||
import com.google.gson.internal.bind.ArrayTypeAdapter;
|
||||
import com.google.gson.internal.bind.BigDecimalTypeAdapter;
|
||||
import com.google.gson.internal.bind.BigIntegerTypeAdapter;
|
||||
@ -101,8 +101,7 @@ import java.util.Map;
|
||||
*/
|
||||
public final class Gson {
|
||||
@SuppressWarnings("rawtypes")
|
||||
static final ParameterizedTypeHandlerMap EMPTY_MAP =
|
||||
new ParameterizedTypeHandlerMap().makeUnmodifiable();
|
||||
static final TypeMap EMPTY_MAP = new TypeMap().makeUnmodifiable();
|
||||
|
||||
static final boolean DEFAULT_JSON_NON_EXECUTABLE = false;
|
||||
|
||||
@ -140,16 +139,31 @@ public final class Gson {
|
||||
private final ConstructorConstructor constructorConstructor;
|
||||
|
||||
/** Map containing Type or Class objects as keys */
|
||||
private final ParameterizedTypeHandlerMap<JsonSerializer<?>> serializers;
|
||||
private final TypeMap<JsonSerializer<?>> serializers;
|
||||
|
||||
/** Map containing Type or Class objects as keys */
|
||||
private final ParameterizedTypeHandlerMap<JsonDeserializer<?>> deserializers;
|
||||
private final TypeMap<JsonDeserializer<?>> deserializers;
|
||||
|
||||
private final boolean serializeNulls;
|
||||
private final boolean htmlSafe;
|
||||
private final boolean generateNonExecutableJson;
|
||||
private final boolean prettyPrinting;
|
||||
|
||||
final JsonDeserializationContext deserializationContext = new JsonDeserializationContext() {
|
||||
public <T> T deserialize(JsonElement json, Type typeOfT) throws JsonParseException {
|
||||
return (T) fromJson(json, typeOfT);
|
||||
}
|
||||
};
|
||||
|
||||
final JsonSerializationContext serializationContext = new JsonSerializationContext() {
|
||||
public JsonElement serialize(Object src) {
|
||||
return toJsonTree(src);
|
||||
}
|
||||
public JsonElement serialize(Object src, Type typeOfSrc) {
|
||||
return toJsonTree(src, typeOfSrc);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Constructs a Gson object with default configuration. The default configuration has the
|
||||
* following settings:
|
||||
@ -195,9 +209,9 @@ public final class Gson {
|
||||
Gson(final ExclusionStrategy deserializationExclusionStrategy,
|
||||
final ExclusionStrategy serializationExclusionStrategy,
|
||||
final FieldNamingStrategy2 fieldNamingPolicy,
|
||||
final ParameterizedTypeHandlerMap<InstanceCreator<?>> instanceCreators, boolean serializeNulls,
|
||||
final ParameterizedTypeHandlerMap<JsonSerializer<?>> serializers,
|
||||
final ParameterizedTypeHandlerMap<JsonDeserializer<?>> deserializers,
|
||||
final TypeMap<InstanceCreator<?>> instanceCreators, boolean serializeNulls,
|
||||
final TypeMap<JsonSerializer<?>> serializers,
|
||||
final TypeMap<JsonDeserializer<?>> deserializers,
|
||||
boolean complexMapKeySerialization, boolean generateNonExecutableGson, boolean htmlSafe,
|
||||
boolean prettyPrinting, boolean serializeSpecialFloatingPointValues,
|
||||
LongSerializationPolicy longSerializationPolicy,
|
||||
@ -268,7 +282,7 @@ public final class Gson {
|
||||
factories.add(factory);
|
||||
}
|
||||
|
||||
factories.add(new GsonToMiniGsonTypeAdapterFactory(this, serializers, deserializers));
|
||||
factories.add(new TreeTypeAdapter.TypeHierarchyFactory(serializers, deserializers));
|
||||
factories.add(new CollectionTypeAdapterFactory(constructorConstructor));
|
||||
factories.add(TypeAdapters.URL_FACTORY);
|
||||
factories.add(TypeAdapters.URI_FACTORY);
|
||||
|
@ -18,8 +18,8 @@ package com.google.gson;
|
||||
|
||||
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.TypeMap;
|
||||
import com.google.gson.internal.bind.TypeAdapters;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
import java.lang.reflect.Type;
|
||||
@ -87,9 +87,9 @@ public final class GsonBuilder {
|
||||
private boolean excludeFieldsWithoutExposeAnnotation;
|
||||
private LongSerializationPolicy longSerializationPolicy;
|
||||
private FieldNamingStrategy2 fieldNamingPolicy;
|
||||
private final ParameterizedTypeHandlerMap<InstanceCreator<?>> instanceCreators;
|
||||
private final ParameterizedTypeHandlerMap<JsonSerializer<?>> serializers;
|
||||
private final ParameterizedTypeHandlerMap<JsonDeserializer<?>> deserializers;
|
||||
private final TypeMap<InstanceCreator<?>> instanceCreators;
|
||||
private final TypeMap<JsonSerializer<?>> serializers;
|
||||
private final TypeMap<JsonDeserializer<?>> deserializers;
|
||||
private final List<TypeAdapter.Factory> typeAdapterFactories
|
||||
= new ArrayList<TypeAdapter.Factory>();
|
||||
private boolean serializeNulls;
|
||||
@ -124,9 +124,9 @@ public final class GsonBuilder {
|
||||
excludeFieldsWithoutExposeAnnotation = false;
|
||||
longSerializationPolicy = LongSerializationPolicy.DEFAULT;
|
||||
fieldNamingPolicy = Gson.DEFAULT_NAMING_POLICY;
|
||||
instanceCreators = new ParameterizedTypeHandlerMap<InstanceCreator<?>>();
|
||||
serializers = new ParameterizedTypeHandlerMap<JsonSerializer<?>>();
|
||||
deserializers = new ParameterizedTypeHandlerMap<JsonDeserializer<?>>();
|
||||
instanceCreators = new TypeMap<InstanceCreator<?>>();
|
||||
serializers = new TypeMap<JsonSerializer<?>>();
|
||||
deserializers = new TypeMap<JsonDeserializer<?>>();
|
||||
serializeNulls = false;
|
||||
dateStyle = DateFormat.DEFAULT;
|
||||
timeStyle = DateFormat.DEFAULT;
|
||||
@ -529,11 +529,9 @@ public final class GsonBuilder {
|
||||
if (typeAdapter instanceof InstanceCreator<?>) {
|
||||
registerInstanceCreator(type, (InstanceCreator<?>) typeAdapter);
|
||||
}
|
||||
if (typeAdapter instanceof JsonSerializer<?>) {
|
||||
registerSerializer(type, (JsonSerializer<?>) typeAdapter);
|
||||
}
|
||||
if (typeAdapter instanceof JsonDeserializer<?>) {
|
||||
registerDeserializer(type, (JsonDeserializer<?>) typeAdapter);
|
||||
if (typeAdapter instanceof JsonSerializer<?> || typeAdapter instanceof JsonDeserializer<?>) {
|
||||
TypeToken<?> typeToken = TypeToken.get(type);
|
||||
typeAdapterFactories.add(new TreeTypeAdapter.SingleTypeFactory(typeToken, typeAdapter));
|
||||
}
|
||||
if (typeAdapter instanceof TypeAdapter.Factory) {
|
||||
typeAdapterFactories.add((TypeAdapter.Factory) typeAdapter);
|
||||
@ -558,36 +556,6 @@ public final class GsonBuilder {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures Gson to use a custom JSON serializer for the specified type. You should use this
|
||||
* method if you want to register different serializers for different generic types corresponding
|
||||
* to a raw type.
|
||||
*
|
||||
*
|
||||
* @param typeOfT The type definition for T
|
||||
* @param serializer the custom serializer
|
||||
* @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern
|
||||
*/
|
||||
private <T> GsonBuilder registerSerializer(Type typeOfT, JsonSerializer<T> serializer) {
|
||||
serializers.register(typeOfT, serializer);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures Gson to use a custom JSON deserializer for the specified type. You should use this
|
||||
* method if you want to register different deserializers for different generic types
|
||||
* corresponding to a raw type.
|
||||
*
|
||||
*
|
||||
* @param typeOfT The type definition for T
|
||||
* @param deserializer the custom deserializer
|
||||
* @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern
|
||||
*/
|
||||
private <T> GsonBuilder registerDeserializer(Type typeOfT, JsonDeserializer<T> deserializer) {
|
||||
deserializers.register(typeOfT, new JsonDeserializerExceptionWrapper<T>(deserializer));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures Gson for custom serialization or deserialization for an inheritance type hierarchy.
|
||||
* This method combines the registration of an {@link InstanceCreator}, {@link JsonSerializer},
|
||||
@ -703,8 +671,7 @@ public final class GsonBuilder {
|
||||
}
|
||||
|
||||
private static void addTypeAdaptersForDate(String datePattern, int dateStyle, int timeStyle,
|
||||
ParameterizedTypeHandlerMap<JsonSerializer<?>> serializers,
|
||||
ParameterizedTypeHandlerMap<JsonDeserializer<?>> deserializers) {
|
||||
TypeMap<JsonSerializer<?>> serializers, TypeMap<JsonDeserializer<?>> deserializers) {
|
||||
DefaultDateTypeAdapter dateTypeAdapter = null;
|
||||
if (datePattern != null && !"".equals(datePattern.trim())) {
|
||||
dateTypeAdapter = new DefaultDateTypeAdapter(datePattern);
|
||||
@ -722,8 +689,7 @@ public final class GsonBuilder {
|
||||
}
|
||||
}
|
||||
|
||||
private static <T> void registerIfAbsent(Class<?> type,
|
||||
ParameterizedTypeHandlerMap<T> adapters, T adapter) {
|
||||
private static <T> void registerIfAbsent(Class<?> type, TypeMap<T> adapters, T adapter) {
|
||||
if (!adapters.hasSpecificHandlerFor(type)) {
|
||||
adapters.register(type, adapter);
|
||||
}
|
||||
|
@ -1,107 +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;
|
||||
|
||||
import com.google.gson.internal.ParameterizedTypeHandlerMap;
|
||||
import com.google.gson.internal.Streams;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
import com.google.gson.stream.JsonReader;
|
||||
import com.google.gson.stream.JsonWriter;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Type;
|
||||
|
||||
final class GsonToMiniGsonTypeAdapterFactory implements TypeAdapter.Factory {
|
||||
private final ParameterizedTypeHandlerMap<JsonSerializer<?>> serializers;
|
||||
private final ParameterizedTypeHandlerMap<JsonDeserializer<?>> deserializers;
|
||||
private final JsonDeserializationContext deserializationContext;
|
||||
private final JsonSerializationContext serializationContext;
|
||||
|
||||
public GsonToMiniGsonTypeAdapterFactory(final Gson gson,
|
||||
ParameterizedTypeHandlerMap<JsonSerializer<?>> serializers,
|
||||
ParameterizedTypeHandlerMap<JsonDeserializer<?>> deserializers) {
|
||||
this.serializers = serializers;
|
||||
this.deserializers = deserializers;
|
||||
|
||||
this.deserializationContext = new JsonDeserializationContext() {
|
||||
public <T> T deserialize(JsonElement json, Type typeOfT) throws JsonParseException {
|
||||
return (T) gson.fromJson(json, typeOfT);
|
||||
}
|
||||
};
|
||||
|
||||
this.serializationContext = new JsonSerializationContext() {
|
||||
public JsonElement serialize(Object src) {
|
||||
return gson.toJsonTree(src);
|
||||
}
|
||||
public JsonElement serialize(Object src, Type typeOfSrc) {
|
||||
return gson.toJsonTree(src, typeOfSrc);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public <T> TypeAdapter<T> create(final Gson context, final TypeToken<T> typeToken) {
|
||||
final Type type = typeToken.getType();
|
||||
|
||||
@SuppressWarnings("unchecked") // guaranteed to match typeOfT
|
||||
final JsonSerializer<T> serializer
|
||||
= (JsonSerializer<T>) serializers.getHandlerFor(type, false);
|
||||
@SuppressWarnings("unchecked") // guaranteed to match typeOfT
|
||||
final JsonDeserializer<T> deserializer
|
||||
= (JsonDeserializer<T>) deserializers.getHandlerFor(type, false);
|
||||
|
||||
if (serializer == null && deserializer == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new TypeAdapter<T>() {
|
||||
/**
|
||||
* The delegate is lazily created because it may not be needed, and
|
||||
* creating it may fail.
|
||||
*/
|
||||
private TypeAdapter<T> delegate;
|
||||
|
||||
@Override public T read(JsonReader reader) throws IOException {
|
||||
if (deserializer == null) {
|
||||
return delegate().read(reader);
|
||||
}
|
||||
JsonElement value = Streams.parse(reader);
|
||||
if (value.isJsonNull()) {
|
||||
return null;
|
||||
}
|
||||
return deserializer.deserialize(value, type, deserializationContext);
|
||||
}
|
||||
|
||||
@Override public void write(JsonWriter writer, T value) throws IOException {
|
||||
if (serializer == null) {
|
||||
delegate().write(writer, value);
|
||||
return;
|
||||
}
|
||||
if (value == null) {
|
||||
writer.nullValue();
|
||||
return;
|
||||
}
|
||||
JsonElement element = serializer.serialize(value, type, serializationContext);
|
||||
Streams.write(element, writer);
|
||||
}
|
||||
|
||||
private TypeAdapter<T> delegate() {
|
||||
TypeAdapter<T> d = delegate;
|
||||
return d != null
|
||||
? d
|
||||
: (delegate = context.getNextAdapter(GsonToMiniGsonTypeAdapterFactory.this, typeToken));
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
128
gson/src/main/java/com/google/gson/TreeTypeAdapter.java
Normal file
128
gson/src/main/java/com/google/gson/TreeTypeAdapter.java
Normal file
@ -0,0 +1,128 @@
|
||||
/*
|
||||
* Copyright (C) 2011 Google Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.gson;
|
||||
|
||||
import com.google.gson.internal.$Gson$Preconditions;
|
||||
import com.google.gson.internal.Streams;
|
||||
import com.google.gson.internal.TypeMap;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
import com.google.gson.stream.JsonReader;
|
||||
import com.google.gson.stream.JsonWriter;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Type;
|
||||
|
||||
/**
|
||||
* Adapts a Gson 1.x tree-style adapter as a streaming TypeAdapter. Since the
|
||||
* tree adapter may be serialization-only or deserialization-only, this class
|
||||
* has a facility to lookup a delegate type adapter on demand.
|
||||
*/
|
||||
public final class TreeTypeAdapter<T> extends TypeAdapter<T> {
|
||||
private final JsonSerializer<T> serializer;
|
||||
private final JsonDeserializer<T> deserializer;
|
||||
private final Gson gson;
|
||||
private final TypeToken<T> typeToken;
|
||||
private final Factory skipPast;
|
||||
|
||||
/** The delegate is lazily created because it may not be needed, and creating it may fail. */
|
||||
private TypeAdapter<T> delegate;
|
||||
|
||||
private TreeTypeAdapter(JsonSerializer<T> serializer, JsonDeserializer<T> deserializer,
|
||||
Gson gson, TypeToken<T> typeToken, Factory skipPast) {
|
||||
this.serializer = serializer;
|
||||
this.deserializer = deserializer;
|
||||
this.gson = gson;
|
||||
this.typeToken = typeToken;
|
||||
this.skipPast = skipPast;
|
||||
}
|
||||
|
||||
@Override public T read(JsonReader reader) throws IOException {
|
||||
if (deserializer == null) {
|
||||
return delegate().read(reader);
|
||||
}
|
||||
JsonElement value = Streams.parse(reader);
|
||||
if (value.isJsonNull()) {
|
||||
return null;
|
||||
}
|
||||
return deserializer.deserialize(value, typeToken.getType(), gson.deserializationContext);
|
||||
}
|
||||
|
||||
@Override public void write(JsonWriter writer, T value) throws IOException {
|
||||
if (serializer == null) {
|
||||
delegate().write(writer, value);
|
||||
return;
|
||||
}
|
||||
if (value == null) {
|
||||
writer.nullValue();
|
||||
return;
|
||||
}
|
||||
JsonElement tree = serializer.serialize(value, typeToken.getType(), gson.serializationContext);
|
||||
Streams.write(tree,writer);
|
||||
}
|
||||
|
||||
private TypeAdapter<T> delegate() {
|
||||
TypeAdapter<T> d = delegate;
|
||||
return d != null
|
||||
? d
|
||||
: (delegate = gson.getNextAdapter(skipPast, typeToken));
|
||||
}
|
||||
|
||||
public static class SingleTypeFactory implements TypeAdapter.Factory {
|
||||
private final TypeToken<?> typeToken;
|
||||
private final JsonSerializer<?> serializer;
|
||||
private final JsonDeserializer<?> deserializer;
|
||||
|
||||
public SingleTypeFactory(TypeToken<?> typeToken, Object typeAdapter) {
|
||||
this.typeToken = typeToken;
|
||||
serializer = typeAdapter instanceof JsonSerializer
|
||||
? (JsonSerializer) typeAdapter
|
||||
: null;
|
||||
deserializer = typeAdapter instanceof JsonDeserializer
|
||||
? (JsonDeserializer) typeAdapter
|
||||
: null;
|
||||
$Gson$Preconditions.checkArgument(serializer != null || deserializer != null);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked") // guarded by typeToken.equals() call
|
||||
public <T> TypeAdapter<T> create(Gson context, TypeToken<T> type) {
|
||||
return typeToken.equals(type)
|
||||
? new TreeTypeAdapter<T>((JsonSerializer<T>) serializer,
|
||||
(JsonDeserializer<T>) deserializer, context, type, this)
|
||||
: null;
|
||||
}
|
||||
}
|
||||
|
||||
public static class TypeHierarchyFactory implements TypeAdapter.Factory {
|
||||
private final TypeMap<JsonSerializer<?>> serializers;
|
||||
private final TypeMap<JsonDeserializer<?>> deserializers;
|
||||
|
||||
public TypeHierarchyFactory(TypeMap<JsonSerializer<?>> serializers,
|
||||
TypeMap<JsonDeserializer<?>> deserializers) {
|
||||
this.serializers = serializers;
|
||||
this.deserializers = deserializers;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked") // guaranteed by serializers lookup matching type
|
||||
public <T> TypeAdapter<T> create(Gson context, TypeToken<T> typeToken) {
|
||||
Type type = typeToken.getType();
|
||||
JsonSerializer<T> serializer = (JsonSerializer<T>) serializers.getHandlerFor(type);
|
||||
JsonDeserializer<T> deserializer = (JsonDeserializer<T>) deserializers.getHandlerFor(type);
|
||||
return (serializer != null || deserializer != null)
|
||||
? new TreeTypeAdapter<T>(serializer, deserializer, context, typeToken, this)
|
||||
: null;
|
||||
}
|
||||
}
|
||||
}
|
@ -36,14 +36,14 @@ import java.util.TreeSet;
|
||||
* Returns a function that can construct an instance of a requested type.
|
||||
*/
|
||||
public final class ConstructorConstructor {
|
||||
private final ParameterizedTypeHandlerMap<InstanceCreator<?>> instanceCreators;
|
||||
private final TypeMap<InstanceCreator<?>> instanceCreators;
|
||||
|
||||
public ConstructorConstructor(ParameterizedTypeHandlerMap<InstanceCreator<?>> instanceCreators) {
|
||||
public ConstructorConstructor(TypeMap<InstanceCreator<?>> instanceCreators) {
|
||||
this.instanceCreators = instanceCreators;
|
||||
}
|
||||
|
||||
public ConstructorConstructor() {
|
||||
this(new ParameterizedTypeHandlerMap<InstanceCreator<?>>());
|
||||
this(new TypeMap<InstanceCreator<?>>());
|
||||
}
|
||||
|
||||
public <T> ObjectConstructor<T> getConstructor(TypeToken<T> typeToken) {
|
||||
@ -54,7 +54,7 @@ public final class ConstructorConstructor {
|
||||
|
||||
@SuppressWarnings("unchecked") // types must agree
|
||||
final InstanceCreator<T> creator
|
||||
= (InstanceCreator<T>) instanceCreators.getHandlerFor(type, false);
|
||||
= (InstanceCreator<T>) instanceCreators.getHandlerFor(type);
|
||||
if (creator != null) {
|
||||
return new ObjectConstructor<T>() {
|
||||
public T construct() {
|
||||
|
@ -33,16 +33,14 @@ import java.util.logging.Logger;
|
||||
*
|
||||
* @param <T> The handler that will be looked up by type
|
||||
*/
|
||||
public final class ParameterizedTypeHandlerMap<T> {
|
||||
|
||||
private static final Logger logger =
|
||||
Logger.getLogger(ParameterizedTypeHandlerMap.class.getName());
|
||||
public final class TypeMap<T> {
|
||||
private static final Logger logger = Logger.getLogger(TypeMap.class.getName());
|
||||
|
||||
/** Map that is meant for storing default type adapters */
|
||||
private final Map<Type, T> userMap = new HashMap<Type, T>();
|
||||
private final Map<Type, T> typeMap = new HashMap<Type, T>();
|
||||
|
||||
/** List of default type hierarchy adapters */
|
||||
private final List<Pair<Class<?>, T>> userTypeHierarchyList = new ArrayList<Pair<Class<?>, T>>();
|
||||
private final List<Pair<Class<?>, T>> typeHierarchyList = new ArrayList<Pair<Class<?>, T>>();
|
||||
private boolean modifiable = true;
|
||||
|
||||
public synchronized void registerForTypeHierarchy(Class<?> typeOfT, T value) {
|
||||
@ -54,14 +52,13 @@ public final class ParameterizedTypeHandlerMap<T> {
|
||||
if (!modifiable) {
|
||||
throw new IllegalStateException("Attempted to modify an unmodifiable map.");
|
||||
}
|
||||
List<Pair<Class<?>, T>> typeHierarchyList = userTypeHierarchyList;
|
||||
int index = getIndexOfSpecificHandlerForTypeHierarchy(pair.first, typeHierarchyList);
|
||||
if (index >= 0) {
|
||||
if (index != -1) {
|
||||
logger.log(Level.WARNING, "Overriding the existing type handler for {0}", pair.first);
|
||||
typeHierarchyList.remove(index);
|
||||
}
|
||||
index = getIndexOfAnOverriddenHandler(pair.first, typeHierarchyList);
|
||||
if (index >= 0) {
|
||||
if (index != -1) {
|
||||
throw new IllegalArgumentException("The specified type handler for type " + pair.first
|
||||
+ " hides the previously registered type hierarchy handler for "
|
||||
+ typeHierarchyList.get(index).first + ". Gson does not allow this.");
|
||||
@ -85,71 +82,41 @@ public final class ParameterizedTypeHandlerMap<T> {
|
||||
if (!modifiable) {
|
||||
throw new IllegalStateException("Attempted to modify an unmodifiable map.");
|
||||
}
|
||||
if (hasSpecificHandlerFor(typeOfT)) {
|
||||
logger.log(Level.WARNING, "Overriding the existing type handler for {0}", typeOfT);
|
||||
}
|
||||
Map<Type, T> map = userMap;
|
||||
map.put(typeOfT, value);
|
||||
typeMap.put(typeOfT, value);
|
||||
}
|
||||
|
||||
public synchronized void registerIfAbsent(ParameterizedTypeHandlerMap<T> other) {
|
||||
if (!modifiable) {
|
||||
throw new IllegalStateException("Attempted to modify an unmodifiable map.");
|
||||
}
|
||||
for (Map.Entry<Type, T> entry : other.userMap.entrySet()) {
|
||||
if (!userMap.containsKey(entry.getKey())) {
|
||||
register(entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
// Quite important to traverse the typeHierarchyList from stack bottom first since
|
||||
// we want to register the handlers in the same order to preserve priority order
|
||||
for (int i = other.userTypeHierarchyList.size()-1; i >= 0; --i) {
|
||||
Pair<Class<?>, T> entry = other.userTypeHierarchyList.get(i);
|
||||
int index = getIndexOfSpecificHandlerForTypeHierarchy(entry.first, userTypeHierarchyList);
|
||||
if (index < 0) {
|
||||
registerForTypeHierarchy(entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized ParameterizedTypeHandlerMap<T> makeUnmodifiable() {
|
||||
public synchronized TypeMap<T> makeUnmodifiable() {
|
||||
modifiable = false;
|
||||
return this;
|
||||
}
|
||||
|
||||
public synchronized T getHandlerFor(Type type, boolean systemOnly) {
|
||||
T handler;
|
||||
if (!systemOnly) {
|
||||
handler = userMap.get(type);
|
||||
if (handler != null) {
|
||||
return handler;
|
||||
}
|
||||
public synchronized T getHandlerFor(Type type) {
|
||||
T handler = typeMap.get(type);
|
||||
if (handler != null) {
|
||||
return handler;
|
||||
}
|
||||
Class<?> rawClass = $Gson$Types.getRawType(type);
|
||||
if (rawClass != type) {
|
||||
handler = getHandlerFor(rawClass, systemOnly);
|
||||
handler = getHandlerFor(rawClass);
|
||||
if (handler != null) {
|
||||
return handler;
|
||||
}
|
||||
}
|
||||
// check if something registered for type hierarchy
|
||||
handler = getHandlerForTypeHierarchy(rawClass, systemOnly);
|
||||
return handler;
|
||||
return getHandlerForTypeHierarchy(rawClass);
|
||||
}
|
||||
|
||||
private T getHandlerForTypeHierarchy(Class<?> type, boolean systemOnly) {
|
||||
if (!systemOnly) {
|
||||
for (Pair<Class<?>, T> entry : userTypeHierarchyList) {
|
||||
if (entry.first.isAssignableFrom(type)) {
|
||||
return entry.second;
|
||||
}
|
||||
private T getHandlerForTypeHierarchy(Class<?> type) {
|
||||
for (Pair<Class<?>, T> entry : typeHierarchyList) {
|
||||
if (entry.first.isAssignableFrom(type)) {
|
||||
return entry.second;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public synchronized boolean hasSpecificHandlerFor(Type type) {
|
||||
return userMap.containsKey(type);
|
||||
return typeMap.containsKey(type);
|
||||
}
|
||||
|
||||
private static <T> int getIndexOfSpecificHandlerForTypeHierarchy(
|
||||
@ -162,24 +129,21 @@ public final class ParameterizedTypeHandlerMap<T> {
|
||||
return -1;
|
||||
}
|
||||
|
||||
public synchronized ParameterizedTypeHandlerMap<T> copyOf() {
|
||||
ParameterizedTypeHandlerMap<T> copy = new ParameterizedTypeHandlerMap<T>();
|
||||
public synchronized TypeMap<T> copyOf() {
|
||||
TypeMap<T> copy = new TypeMap<T>();
|
||||
// Instead of individually registering entries in the map, make an efficient copy
|
||||
// of the list and map
|
||||
|
||||
// TODO (inder): Performance optimization. We can probably just share the
|
||||
// systemMap and systemTypeHierarchyList instead of making copies
|
||||
copy.userMap.putAll(userMap);
|
||||
copy.userTypeHierarchyList.addAll(userTypeHierarchyList);
|
||||
copy.typeMap.putAll(typeMap);
|
||||
copy.typeHierarchyList.addAll(typeHierarchyList);
|
||||
return copy;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder("{userTypeHierarchyList:{");
|
||||
appendList(sb, userTypeHierarchyList);
|
||||
sb.append("},userMap:{");
|
||||
appendMap(sb, userMap);
|
||||
StringBuilder sb = new StringBuilder("{typeHierarchyList:{");
|
||||
appendList(sb, typeHierarchyList);
|
||||
sb.append("},typeMap:{");
|
||||
appendMap(sb, typeMap);
|
||||
sb.append("}");
|
||||
return sb.toString();
|
||||
}
|
@ -24,23 +24,23 @@ import java.util.List;
|
||||
import junit.framework.TestCase;
|
||||
|
||||
/**
|
||||
* Unit tests for the {@link com.google.gson.internal.ParameterizedTypeHandlerMap} class.
|
||||
* Unit tests for the {@link TypeMap} class.
|
||||
*
|
||||
* @author Joel Leitch
|
||||
*/
|
||||
public class ParameterizedTypeHandlerMapTest extends TestCase {
|
||||
private ParameterizedTypeHandlerMap<String> paramMap;
|
||||
public class TypeMapTest extends TestCase {
|
||||
private TypeMap<String> paramMap;
|
||||
|
||||
@Override
|
||||
protected void setUp() throws Exception {
|
||||
super.setUp();
|
||||
paramMap = new ParameterizedTypeHandlerMap<String>();
|
||||
paramMap = new TypeMap<String>();
|
||||
}
|
||||
|
||||
public void testNullMap() throws Exception {
|
||||
assertFalse(paramMap.hasSpecificHandlerFor(String.class));
|
||||
assertNull(paramMap.getHandlerFor(String.class, false));
|
||||
assertNull(paramMap.getHandlerFor(String.class, false));
|
||||
assertNull(paramMap.getHandlerFor(String.class));
|
||||
assertNull(paramMap.getHandlerFor(String.class));
|
||||
}
|
||||
|
||||
public void testHasGenericButNotSpecific() throws Exception {
|
||||
@ -50,9 +50,9 @@ public class ParameterizedTypeHandlerMapTest extends TestCase {
|
||||
|
||||
assertFalse(paramMap.hasSpecificHandlerFor(specificType));
|
||||
assertTrue(paramMap.hasSpecificHandlerFor(List.class));
|
||||
assertNotNull(paramMap.getHandlerFor(specificType, false));
|
||||
assertNotNull(paramMap.getHandlerFor(List.class, false));
|
||||
assertEquals(handler, paramMap.getHandlerFor(specificType, false));
|
||||
assertNotNull(paramMap.getHandlerFor(specificType));
|
||||
assertNotNull(paramMap.getHandlerFor(List.class));
|
||||
assertEquals(handler, paramMap.getHandlerFor(specificType));
|
||||
}
|
||||
|
||||
public void testHasSpecificType() throws Exception {
|
||||
@ -62,9 +62,9 @@ public class ParameterizedTypeHandlerMapTest extends TestCase {
|
||||
|
||||
assertTrue(paramMap.hasSpecificHandlerFor(specificType));
|
||||
assertFalse(paramMap.hasSpecificHandlerFor(List.class));
|
||||
assertNotNull(paramMap.getHandlerFor(specificType, false));
|
||||
assertNull(paramMap.getHandlerFor(List.class, false));
|
||||
assertEquals(handler, paramMap.getHandlerFor(specificType, false));
|
||||
assertNotNull(paramMap.getHandlerFor(specificType));
|
||||
assertNull(paramMap.getHandlerFor(List.class));
|
||||
assertEquals(handler, paramMap.getHandlerFor(specificType));
|
||||
}
|
||||
|
||||
public void testTypeOverridding() throws Exception {
|
||||
@ -74,7 +74,7 @@ public class ParameterizedTypeHandlerMapTest extends TestCase {
|
||||
paramMap.register(String.class, handler2);
|
||||
|
||||
assertTrue(paramMap.hasSpecificHandlerFor(String.class));
|
||||
assertEquals(handler2, paramMap.getHandlerFor(String.class, false));
|
||||
assertEquals(handler2, paramMap.getHandlerFor(String.class));
|
||||
}
|
||||
|
||||
public void testMakeUnmodifiable() throws Exception {
|
||||
@ -87,30 +87,21 @@ public class ParameterizedTypeHandlerMapTest extends TestCase {
|
||||
|
||||
public void testTypeHierarchy() {
|
||||
paramMap.registerForTypeHierarchy(Base.class, "baseHandler");
|
||||
String handler = paramMap.getHandlerFor(Sub.class, false);
|
||||
String handler = paramMap.getHandlerFor(Sub.class);
|
||||
assertEquals("baseHandler", handler);
|
||||
}
|
||||
|
||||
public void testTypeHierarchyMultipleHandlers() {
|
||||
paramMap.registerForTypeHierarchy(Base.class, "baseHandler");
|
||||
paramMap.registerForTypeHierarchy(Sub.class, "subHandler");
|
||||
String handler = paramMap.getHandlerFor(SubOfSub.class, false);
|
||||
String handler = paramMap.getHandlerFor(SubOfSub.class);
|
||||
assertEquals("subHandler", handler);
|
||||
}
|
||||
|
||||
public void testTypeHierarchyRegisterIfAbsent() {
|
||||
paramMap.registerForTypeHierarchy(Base.class, "baseHandler");
|
||||
ParameterizedTypeHandlerMap<String> otherMap = new ParameterizedTypeHandlerMap<String>();
|
||||
otherMap.registerForTypeHierarchy(Base.class, "baseHandler2");
|
||||
paramMap.registerIfAbsent(otherMap);
|
||||
String handler = paramMap.getHandlerFor(Base.class, false);
|
||||
assertEquals("baseHandler", handler);
|
||||
}
|
||||
|
||||
public void testReplaceExistingTypeHierarchyHandler() {
|
||||
paramMap.registerForTypeHierarchy(Base.class, "baseHandler");
|
||||
paramMap.registerForTypeHierarchy(Base.class, "base2Handler");
|
||||
String handler = paramMap.getHandlerFor(Base.class, false);
|
||||
String handler = paramMap.getHandlerFor(Base.class);
|
||||
assertEquals("base2Handler", handler);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user