From 2df65502edc90a04ad0cf12319aee0788972d5cb Mon Sep 17 00:00:00 2001 From: jwilson Date: Thu, 2 Jun 2016 00:18:03 -0400 Subject: [PATCH] Don't use ThreadLocals for @JsonAdapter factories and getDelegateAdapter(). --- gson/src/main/java/com/google/gson/Gson.java | 20 +++-- ...onAdapterAnnotationTypeAdapterFactory.java | 87 ++++--------------- .../bind/ReflectiveTypeAdapterFactory.java | 3 +- ...JsonAdapterSerializerDeserializerTest.java | 2 +- 4 files changed, 30 insertions(+), 82 deletions(-) diff --git a/gson/src/main/java/com/google/gson/Gson.java b/gson/src/main/java/com/google/gson/Gson.java index c34c896d..67ae06e0 100644 --- a/gson/src/main/java/com/google/gson/Gson.java +++ b/gson/src/main/java/com/google/gson/Gson.java @@ -488,26 +488,28 @@ public final class Gson { * @since 2.2 */ public TypeAdapter getDelegateAdapter(TypeAdapterFactory skipPast, TypeToken type) { - // If the specified skipPast factory is not registered, ignore it. - boolean skipPastFound = skipPast == null - || (!factories.contains(skipPast) && jsonAdapterFactory.getDelegateAdapterFactory(type) == null); + boolean skipPastFound = false; + + // Hack. If the skipPast factory isn't registered, assume the factory is being requested via + // our @JsonAdapter annotation. + if (!factories.contains(skipPast)) { + skipPast = jsonAdapterFactory; + } for (TypeAdapterFactory factory : factories) { if (!skipPastFound) { - skipPastFound = factory == skipPast; - if (!skipPastFound && factory instanceof JsonAdapterAnnotationTypeAdapterFactory) { - // Also check if there is a registered JsonAdapter for it - factory = ((JsonAdapterAnnotationTypeAdapterFactory)factory).getDelegateAdapterFactory(type); - skipPastFound = factory == skipPast; + if (factory == skipPast) { + skipPastFound = true; } continue; } + TypeAdapter candidate = factory.create(this, type); if (candidate != null) { return candidate; } } - throw new IllegalArgumentException("GSON cannot serialize or deserialize " + type); + throw new IllegalArgumentException("GSON cannot serialize " + type); } /** diff --git a/gson/src/main/java/com/google/gson/internal/bind/JsonAdapterAnnotationTypeAdapterFactory.java b/gson/src/main/java/com/google/gson/internal/bind/JsonAdapterAnnotationTypeAdapterFactory.java index 1eaacaa4..c079b64d 100644 --- a/gson/src/main/java/com/google/gson/internal/bind/JsonAdapterAnnotationTypeAdapterFactory.java +++ b/gson/src/main/java/com/google/gson/internal/bind/JsonAdapterAnnotationTypeAdapterFactory.java @@ -16,9 +16,6 @@ package com.google.gson.internal.bind; -import java.util.HashMap; -import java.util.Map; - import com.google.gson.Gson; import com.google.gson.JsonDeserializer; import com.google.gson.JsonSerializer; @@ -35,15 +32,6 @@ import com.google.gson.reflect.TypeToken; * @since 2.3 */ public final class JsonAdapterAnnotationTypeAdapterFactory implements TypeAdapterFactory { - - @SuppressWarnings("rawtypes") - private final ThreadLocal> activeJsonAdapterFactories = new ThreadLocal>() { - @Override protected Map initialValue() { - // No need for a thread-safe map since we are using it in a single thread - return new HashMap(); - } - }; - private final ConstructorConstructor constructorConstructor; public JsonAdapterAnnotationTypeAdapterFactory(ConstructorConstructor constructorConstructor) { @@ -61,75 +49,34 @@ public final class JsonAdapterAnnotationTypeAdapterFactory implements TypeAdapte return (TypeAdapter) getTypeAdapter(constructorConstructor, gson, targetType, annotation); } - public TypeAdapter getDelegateAdapter(Gson gson, TypeAdapterFactory skipPast, TypeToken targetType) { - TypeAdapterFactory factory = getDelegateAdapterFactory(targetType); - if (factory == skipPast) factory = null; - return factory == null ? null: factory.create(gson, targetType); - } - - public TypeAdapterFactory getDelegateAdapterFactory(TypeToken targetType) { - Class annotatedClass = targetType.getRawType(); - JsonAdapter annotation = annotatedClass.getAnnotation(JsonAdapter.class); - if (annotation == null) { - return null; - } - return getTypeAdapterFactory(annotation, constructorConstructor); - } - @SuppressWarnings({ "unchecked", "rawtypes" }) // Casts guarded by conditionals. TypeAdapter getTypeAdapter(ConstructorConstructor constructorConstructor, Gson gson, TypeToken type, JsonAdapter annotation) { - Class value = annotation.value(); - boolean isTypeAdapter = TypeAdapter.class.isAssignableFrom(value); - boolean isJsonSerializer = JsonSerializer.class.isAssignableFrom(value); - boolean isJsonDeserializer = JsonDeserializer.class.isAssignableFrom(value); + Object instance = constructorConstructor.get(TypeToken.get(annotation.value())).construct(); TypeAdapter typeAdapter; - if (isTypeAdapter || isJsonSerializer || isJsonDeserializer) { - if (isTypeAdapter) { - Class> typeAdapterClass = (Class>) value; - typeAdapter = constructorConstructor.get(TypeToken.get(typeAdapterClass)).construct(); - } else if (isJsonSerializer || isJsonDeserializer) { - JsonSerializer serializer = null; - if (isJsonSerializer) { - Class> serializerClass = (Class>) value; - serializer = constructorConstructor.get(TypeToken.get(serializerClass)).construct(); - } - JsonDeserializer deserializer = null; - if (isJsonDeserializer) { - Class> deserializerClass = (Class>) value; - deserializer = constructorConstructor.get(TypeToken.get(deserializerClass)).construct(); - } - typeAdapter = new TreeTypeAdapter(serializer, deserializer, gson, type, null); - } else { - typeAdapter = null; - } - } else if (TypeAdapterFactory.class.isAssignableFrom(value)) { - TypeAdapterFactory factory = getTypeAdapterFactory(annotation, constructorConstructor); - typeAdapter = factory == null ? null : factory.create(gson, type); + if (instance instanceof TypeAdapter) { + typeAdapter = (TypeAdapter) instance; + } else if (instance instanceof TypeAdapterFactory) { + typeAdapter = ((TypeAdapterFactory) instance).create(gson, type); + } else if (instance instanceof JsonSerializer || instance instanceof JsonDeserializer) { + JsonSerializer serializer = instance instanceof JsonSerializer + ? (JsonSerializer) instance + : null; + JsonDeserializer deserializer = instance instanceof JsonDeserializer + ? (JsonDeserializer) instance + : null; + typeAdapter = new TreeTypeAdapter(serializer, deserializer, gson, type, null); } else { throw new IllegalArgumentException( - "@JsonAdapter value must be TypeAdapter, TypeAdapterFactory, JsonSerializer or JsonDeserializer reference."); + "@JsonAdapter value must be TypeAdapter, TypeAdapterFactory, " + + "JsonSerializer or JsonDeserializer reference."); } + if (typeAdapter != null) { typeAdapter = typeAdapter.nullSafe(); } + return typeAdapter; } - - - @SuppressWarnings({ "unchecked", "rawtypes" }) // Casts guarded by conditionals. - TypeAdapterFactory getTypeAdapterFactory(JsonAdapter annotation, ConstructorConstructor constructorConstructor) { - Class value = annotation.value(); - if (!TypeAdapterFactory.class.isAssignableFrom(value)) return null; - Map adapterFactories = activeJsonAdapterFactories.get(); - TypeAdapterFactory factory = adapterFactories.get(value); - if (factory == null) { - Class typeAdapterFactoryClass = (Class) value; - factory = constructorConstructor.get(TypeToken.get(typeAdapterFactoryClass)) - .construct(); - adapterFactories.put(value, factory); - } - return factory; - } } 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 45d11224..34e97664 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 @@ -32,7 +32,6 @@ import com.google.gson.reflect.TypeToken; import com.google.gson.stream.JsonReader; import com.google.gson.stream.JsonToken; import com.google.gson.stream.JsonWriter; - import java.io.IOException; import java.lang.reflect.Field; import java.lang.reflect.Type; @@ -110,7 +109,7 @@ public final class ReflectiveTypeAdapterFactory implements TypeAdapterFactory { TypeAdapter mapped = null; if (annotation != null) { mapped = jsonAdapterFactory.getTypeAdapter( - constructorConstructor, context, fieldType, annotation); + constructorConstructor, context, fieldType, annotation); } final boolean jsonAdapterPresent = mapped != null; if (mapped == null) mapped = context.getAdapter(fieldType); diff --git a/gson/src/test/java/com/google/gson/functional/JsonAdapterSerializerDeserializerTest.java b/gson/src/test/java/com/google/gson/functional/JsonAdapterSerializerDeserializerTest.java index de9ad294..8ab4e128 100644 --- a/gson/src/test/java/com/google/gson/functional/JsonAdapterSerializerDeserializerTest.java +++ b/gson/src/test/java/com/google/gson/functional/JsonAdapterSerializerDeserializerTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008 Google Inc. + * Copyright (C) 2016 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License.