diff --git a/gson/src/main/java/com/google/gson/Gson.java b/gson/src/main/java/com/google/gson/Gson.java index ab66f2cd2a..a06f290747 100644 --- a/gson/src/main/java/com/google/gson/Gson.java +++ b/gson/src/main/java/com/google/gson/Gson.java @@ -16,8 +16,7 @@ package com.google.gson; -import static com.google.gson.internal.bind.TypeAdapters.atomicLongAdapter; -import static com.google.gson.internal.bind.TypeAdapters.atomicLongArrayAdapter; +import static com.google.gson.GsonBuilder.newImmutableList; import com.google.gson.annotations.JsonAdapter; import com.google.gson.internal.ConstructorConstructor; @@ -25,19 +24,10 @@ import com.google.gson.internal.GsonBuildConfig; import com.google.gson.internal.Primitives; import com.google.gson.internal.Streams; -import com.google.gson.internal.bind.ArrayTypeAdapter; -import com.google.gson.internal.bind.CollectionTypeAdapterFactory; -import com.google.gson.internal.bind.DefaultDateTypeAdapter; import com.google.gson.internal.bind.JsonAdapterAnnotationTypeAdapterFactory; import com.google.gson.internal.bind.JsonTreeReader; import com.google.gson.internal.bind.JsonTreeWriter; -import com.google.gson.internal.bind.MapTypeAdapterFactory; -import com.google.gson.internal.bind.NumberTypeAdapter; -import com.google.gson.internal.bind.ObjectTypeAdapter; -import com.google.gson.internal.bind.ReflectiveTypeAdapterFactory; import com.google.gson.internal.bind.SerializationDelegatingTypeAdapter; -import com.google.gson.internal.bind.TypeAdapters; -import com.google.gson.internal.sql.SqlTypesSupport; import com.google.gson.reflect.TypeToken; import com.google.gson.stream.JsonReader; import com.google.gson.stream.JsonToken; @@ -49,17 +39,12 @@ import java.io.StringReader; import java.io.Writer; import java.lang.reflect.Type; -import java.text.DateFormat; -import java.util.ArrayList; -import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; -import java.util.concurrent.atomic.AtomicLong; -import java.util.concurrent.atomic.AtomicLongArray; /** * This is the main class for using Gson. Gson is typically used by first constructing a Gson @@ -149,21 +134,6 @@ */ public final class Gson { - static final boolean DEFAULT_JSON_NON_EXECUTABLE = false; - // Strictness of `null` is the legacy mode where some Gson APIs are always lenient - static final Strictness DEFAULT_STRICTNESS = null; - static final FormattingStyle DEFAULT_FORMATTING_STYLE = FormattingStyle.COMPACT; - static final boolean DEFAULT_ESCAPE_HTML = true; - static final boolean DEFAULT_SERIALIZE_NULLS = false; - static final boolean DEFAULT_COMPLEX_MAP_KEYS = false; - static final boolean DEFAULT_SPECIALIZE_FLOAT_VALUES = false; - static final boolean DEFAULT_USE_JDK_UNSAFE = true; - static final String DEFAULT_DATE_PATTERN = null; - static final FieldNamingStrategy DEFAULT_FIELD_NAMING_STRATEGY = FieldNamingPolicy.IDENTITY; - static final ToNumberStrategy DEFAULT_OBJECT_TO_NUMBER_STRATEGY = ToNumberPolicy.DOUBLE; - static final ToNumberStrategy DEFAULT_NUMBER_TO_NUMBER_STRATEGY = - ToNumberPolicy.LAZILY_PARSED_NUMBER; - private static final String JSON_NON_EXECUTABLE_PREFIX = ")]}'\n"; /** @@ -250,144 +220,40 @@ public final class Gson { * */ public Gson() { - this( - Excluder.DEFAULT, - DEFAULT_FIELD_NAMING_STRATEGY, - Collections.emptyMap(), - DEFAULT_SERIALIZE_NULLS, - DEFAULT_COMPLEX_MAP_KEYS, - DEFAULT_JSON_NON_EXECUTABLE, - DEFAULT_ESCAPE_HTML, - DEFAULT_FORMATTING_STYLE, - DEFAULT_STRICTNESS, - DEFAULT_SPECIALIZE_FLOAT_VALUES, - DEFAULT_USE_JDK_UNSAFE, - LongSerializationPolicy.DEFAULT, - DEFAULT_DATE_PATTERN, - DateFormat.DEFAULT, - DateFormat.DEFAULT, - Collections.emptyList(), - Collections.emptyList(), - Collections.emptyList(), - DEFAULT_OBJECT_TO_NUMBER_STRATEGY, - DEFAULT_NUMBER_TO_NUMBER_STRATEGY, - Collections.emptyList()); + this(GsonBuilder.DEFAULT); } - Gson( - Excluder excluder, - FieldNamingStrategy fieldNamingStrategy, - Map> instanceCreators, - boolean serializeNulls, - boolean complexMapKeySerialization, - boolean generateNonExecutableGson, - boolean htmlSafe, - FormattingStyle formattingStyle, - Strictness strictness, - boolean serializeSpecialFloatingPointValues, - boolean useJdkUnsafe, - LongSerializationPolicy longSerializationPolicy, - String datePattern, - int dateStyle, - int timeStyle, - List builderFactories, - List builderHierarchyFactories, - List factoriesToBeAdded, - ToNumberStrategy objectToNumberStrategy, - ToNumberStrategy numberToNumberStrategy, - List reflectionFilters) { - this.excluder = excluder; - this.fieldNamingStrategy = fieldNamingStrategy; - this.instanceCreators = instanceCreators; - this.constructorConstructor = - new ConstructorConstructor(instanceCreators, useJdkUnsafe, reflectionFilters); - this.serializeNulls = serializeNulls; - this.complexMapKeySerialization = complexMapKeySerialization; - this.generateNonExecutableJson = generateNonExecutableGson; - this.htmlSafe = htmlSafe; - this.formattingStyle = formattingStyle; - this.strictness = strictness; - this.serializeSpecialFloatingPointValues = serializeSpecialFloatingPointValues; - this.useJdkUnsafe = useJdkUnsafe; - this.longSerializationPolicy = longSerializationPolicy; - this.datePattern = datePattern; - this.dateStyle = dateStyle; - this.timeStyle = timeStyle; - this.builderFactories = builderFactories; - this.builderHierarchyFactories = builderHierarchyFactories; - this.objectToNumberStrategy = objectToNumberStrategy; - this.numberToNumberStrategy = numberToNumberStrategy; - this.reflectionFilters = reflectionFilters; - - List factories = new ArrayList<>(); - - // built-in type adapters that cannot be overridden - factories.add(TypeAdapters.JSON_ELEMENT_FACTORY); - factories.add(ObjectTypeAdapter.getFactory(objectToNumberStrategy)); - - // the excluder must precede all adapters that handle user-defined types - factories.add(excluder); - - // users' type adapters - factories.addAll(factoriesToBeAdded); - - // type adapters for basic platform types - 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); - TypeAdapter longAdapter = longSerializationPolicy.typeAdapter(); - factories.add(TypeAdapters.newFactory(long.class, Long.class, longAdapter)); - factories.add(TypeAdapters.newFactory(double.class, Double.class, doubleAdapter())); - factories.add(TypeAdapters.newFactory(float.class, Float.class, floatAdapter())); - factories.add(NumberTypeAdapter.getFactory(numberToNumberStrategy)); - factories.add(TypeAdapters.ATOMIC_INTEGER_FACTORY); - factories.add(TypeAdapters.ATOMIC_BOOLEAN_FACTORY); - factories.add(TypeAdapters.newFactory(AtomicLong.class, atomicLongAdapter(longAdapter))); - factories.add( - TypeAdapters.newFactory(AtomicLongArray.class, atomicLongArrayAdapter(longAdapter))); - factories.add(TypeAdapters.ATOMIC_INTEGER_ARRAY_FACTORY); - factories.add(TypeAdapters.CHARACTER_FACTORY); - factories.add(TypeAdapters.STRING_BUILDER_FACTORY); - factories.add(TypeAdapters.STRING_BUFFER_FACTORY); - factories.add(TypeAdapters.BIG_DECIMAL_FACTORY); - factories.add(TypeAdapters.BIG_INTEGER_FACTORY); - // Add adapter for LazilyParsedNumber because user can obtain it from Gson and then try to - // serialize it again - factories.add(TypeAdapters.LAZILY_PARSED_NUMBER_FACTORY); - factories.add(TypeAdapters.URL_FACTORY); - factories.add(TypeAdapters.URI_FACTORY); - factories.add(TypeAdapters.UUID_FACTORY); - factories.add(TypeAdapters.CURRENCY_FACTORY); - factories.add(TypeAdapters.LOCALE_FACTORY); - factories.add(TypeAdapters.INET_ADDRESS_FACTORY); - factories.add(TypeAdapters.BIT_SET_FACTORY); - factories.add(DefaultDateTypeAdapter.DEFAULT_STYLE_FACTORY); - factories.add(TypeAdapters.CALENDAR_FACTORY); - factories.addAll(SqlTypesSupport.SQL_TYPE_FACTORIES); - TypeAdapterFactory javaTimeFactory = TypeAdapters.javaTimeTypeAdapterFactory(); - if (javaTimeFactory != null) { - factories.add(javaTimeFactory); + Gson(GsonBuilder builder) { + this.excluder = builder.excluder; + this.fieldNamingStrategy = builder.fieldNamingPolicy; + this.instanceCreators = new HashMap<>(builder.instanceCreators); + this.serializeNulls = builder.serializeNulls; + this.complexMapKeySerialization = builder.complexMapKeySerialization; + this.generateNonExecutableJson = builder.generateNonExecutableJson; + this.htmlSafe = builder.escapeHtmlChars; + this.formattingStyle = builder.formattingStyle; + this.strictness = builder.strictness; + this.serializeSpecialFloatingPointValues = builder.serializeSpecialFloatingPointValues; + this.useJdkUnsafe = builder.useJdkUnsafe; + this.longSerializationPolicy = builder.longSerializationPolicy; + this.datePattern = builder.datePattern; + this.dateStyle = builder.dateStyle; + this.timeStyle = builder.timeStyle; + this.builderFactories = newImmutableList(builder.factories); + this.builderHierarchyFactories = newImmutableList(builder.hierarchyFactories); + this.objectToNumberStrategy = builder.objectToNumberStrategy; + this.numberToNumberStrategy = builder.numberToNumberStrategy; + this.reflectionFilters = newImmutableList(builder.reflectionFilters); + if (builder == GsonBuilder.DEFAULT) { + this.constructorConstructor = GsonBuilder.DEFAULT_CONSTRUCTOR_CONSTRUCTOR; + this.jsonAdapterFactory = GsonBuilder.DEFAULT_JSON_ADAPTER_ANNOTATION_TYPE_ADAPTER_FACTORY; + this.factories = GsonBuilder.DEFAULT_TYPE_ADAPTER_FACTORIES; + } else { + this.constructorConstructor = + new ConstructorConstructor(instanceCreators, useJdkUnsafe, reflectionFilters); + this.jsonAdapterFactory = new JsonAdapterAnnotationTypeAdapterFactory(constructorConstructor); + this.factories = builder.createFactories(constructorConstructor, jsonAdapterFactory); } - factories.add(ArrayTypeAdapter.FACTORY); - factories.add(TypeAdapters.CLASS_FACTORY); - - // type adapters for composite and user-defined types - factories.add(new CollectionTypeAdapterFactory(constructorConstructor)); - factories.add(new MapTypeAdapterFactory(constructorConstructor, complexMapKeySerialization)); - this.jsonAdapterFactory = new JsonAdapterAnnotationTypeAdapterFactory(constructorConstructor); - factories.add(jsonAdapterFactory); - factories.add(TypeAdapters.ENUM_FACTORY); - factories.add( - new ReflectiveTypeAdapterFactory( - constructorConstructor, - fieldNamingStrategy, - excluder, - jsonAdapterFactory, - reflectionFilters)); - - this.factories = Collections.unmodifiableList(factories); } /** @@ -439,14 +305,6 @@ public boolean htmlSafe() { return htmlSafe; } - private TypeAdapter floatAdapter() { - return serializeSpecialFloatingPointValues ? TypeAdapters.FLOAT : TypeAdapters.FLOAT_STRICT; - } - - private TypeAdapter doubleAdapter() { - return serializeSpecialFloatingPointValues ? TypeAdapters.DOUBLE : TypeAdapters.DOUBLE_STRICT; - } - /** * Returns the type adapter for {@code type}. * diff --git a/gson/src/main/java/com/google/gson/GsonBuilder.java b/gson/src/main/java/com/google/gson/GsonBuilder.java index 5879973223..1cd29f9fff 100644 --- a/gson/src/main/java/com/google/gson/GsonBuilder.java +++ b/gson/src/main/java/com/google/gson/GsonBuilder.java @@ -16,24 +16,20 @@ package com.google.gson; -import static com.google.gson.Gson.DEFAULT_COMPLEX_MAP_KEYS; -import static com.google.gson.Gson.DEFAULT_DATE_PATTERN; -import static com.google.gson.Gson.DEFAULT_ESCAPE_HTML; -import static com.google.gson.Gson.DEFAULT_FORMATTING_STYLE; -import static com.google.gson.Gson.DEFAULT_JSON_NON_EXECUTABLE; -import static com.google.gson.Gson.DEFAULT_NUMBER_TO_NUMBER_STRATEGY; -import static com.google.gson.Gson.DEFAULT_OBJECT_TO_NUMBER_STRATEGY; -import static com.google.gson.Gson.DEFAULT_SERIALIZE_NULLS; -import static com.google.gson.Gson.DEFAULT_SPECIALIZE_FLOAT_VALUES; -import static com.google.gson.Gson.DEFAULT_STRICTNESS; -import static com.google.gson.Gson.DEFAULT_USE_JDK_UNSAFE; - import com.google.errorprone.annotations.CanIgnoreReturnValue; import com.google.errorprone.annotations.InlineMe; import com.google.gson.annotations.Since; import com.google.gson.annotations.Until; +import com.google.gson.internal.ConstructorConstructor; import com.google.gson.internal.Excluder; +import com.google.gson.internal.bind.ArrayTypeAdapter; +import com.google.gson.internal.bind.CollectionTypeAdapterFactory; import com.google.gson.internal.bind.DefaultDateTypeAdapter; +import com.google.gson.internal.bind.JsonAdapterAnnotationTypeAdapterFactory; +import com.google.gson.internal.bind.MapTypeAdapterFactory; +import com.google.gson.internal.bind.NumberTypeAdapter; +import com.google.gson.internal.bind.ObjectTypeAdapter; +import com.google.gson.internal.bind.ReflectiveTypeAdapterFactory; import com.google.gson.internal.bind.TreeTypeAdapter; import com.google.gson.internal.bind.TypeAdapters; import com.google.gson.internal.sql.SqlTypesSupport; @@ -45,12 +41,16 @@ import java.text.SimpleDateFormat; import java.util.ArrayDeque; import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.concurrent.atomic.AtomicLong; +import java.util.concurrent.atomic.AtomicLongArray; /** * Use this builder to construct a {@link Gson} instance when you need to set configuration options @@ -90,29 +90,64 @@ * @author Jesse Wilson */ public final class GsonBuilder { - private Excluder excluder = Excluder.DEFAULT; - private LongSerializationPolicy longSerializationPolicy = LongSerializationPolicy.DEFAULT; - private FieldNamingStrategy fieldNamingPolicy = FieldNamingPolicy.IDENTITY; - private final Map> instanceCreators = new HashMap<>(); - private final List factories = new ArrayList<>(); + private static final boolean DEFAULT_JSON_NON_EXECUTABLE = false; + // Strictness of `null` is the legacy mode where some Gson APIs are always lenient + private static final Strictness DEFAULT_STRICTNESS = null; + private static final FormattingStyle DEFAULT_FORMATTING_STYLE = FormattingStyle.COMPACT; + private static final boolean DEFAULT_ESCAPE_HTML = true; + private static final boolean DEFAULT_SERIALIZE_NULLS = false; + private static final boolean DEFAULT_COMPLEX_MAP_KEYS = false; + private static final boolean DEFAULT_SPECIALIZE_FLOAT_VALUES = false; + private static final boolean DEFAULT_USE_JDK_UNSAFE = true; + private static final String DEFAULT_DATE_PATTERN = null; + private static final FieldNamingStrategy DEFAULT_FIELD_NAMING_STRATEGY = + FieldNamingPolicy.IDENTITY; + private static final ToNumberStrategy DEFAULT_OBJECT_TO_NUMBER_STRATEGY = ToNumberPolicy.DOUBLE; + private static final ToNumberStrategy DEFAULT_NUMBER_TO_NUMBER_STRATEGY = + ToNumberPolicy.LAZILY_PARSED_NUMBER; + + static final ConstructorConstructor DEFAULT_CONSTRUCTOR_CONSTRUCTOR = + new ConstructorConstructor( + Collections.emptyMap(), DEFAULT_USE_JDK_UNSAFE, Collections.emptyList()); + + static final JsonAdapterAnnotationTypeAdapterFactory + DEFAULT_JSON_ADAPTER_ANNOTATION_TYPE_ADAPTER_FACTORY = + new JsonAdapterAnnotationTypeAdapterFactory(DEFAULT_CONSTRUCTOR_CONSTRUCTOR); + + /** + * Default instance of the builder, to be used only by the default {@link Gson#Gson()} + * constructor. Must not be used for anything else and must not be leaked to user code, since that + * could lead to accidental modification of this default builder. + */ + static final GsonBuilder DEFAULT = new GsonBuilder(); + + static final List DEFAULT_TYPE_ADAPTER_FACTORIES = + GsonBuilder.DEFAULT.createFactories( + DEFAULT_CONSTRUCTOR_CONSTRUCTOR, DEFAULT_JSON_ADAPTER_ANNOTATION_TYPE_ADAPTER_FACTORY); + + Excluder excluder = Excluder.DEFAULT; + LongSerializationPolicy longSerializationPolicy = LongSerializationPolicy.DEFAULT; + FieldNamingStrategy fieldNamingPolicy = DEFAULT_FIELD_NAMING_STRATEGY; + final Map> instanceCreators = new HashMap<>(); + final List factories = new ArrayList<>(); /** tree-style hierarchy factories. These come after factories for backwards compatibility. */ - private final List hierarchyFactories = new ArrayList<>(); - - private boolean serializeNulls = DEFAULT_SERIALIZE_NULLS; - private String datePattern = DEFAULT_DATE_PATTERN; - private int dateStyle = DateFormat.DEFAULT; - private int timeStyle = DateFormat.DEFAULT; - private boolean complexMapKeySerialization = DEFAULT_COMPLEX_MAP_KEYS; - private boolean serializeSpecialFloatingPointValues = DEFAULT_SPECIALIZE_FLOAT_VALUES; - private boolean escapeHtmlChars = DEFAULT_ESCAPE_HTML; - private FormattingStyle formattingStyle = DEFAULT_FORMATTING_STYLE; - private boolean generateNonExecutableJson = DEFAULT_JSON_NON_EXECUTABLE; - private Strictness strictness = DEFAULT_STRICTNESS; - private boolean useJdkUnsafe = DEFAULT_USE_JDK_UNSAFE; - private ToNumberStrategy objectToNumberStrategy = DEFAULT_OBJECT_TO_NUMBER_STRATEGY; - private ToNumberStrategy numberToNumberStrategy = DEFAULT_NUMBER_TO_NUMBER_STRATEGY; - private final ArrayDeque reflectionFilters = new ArrayDeque<>(); + final List hierarchyFactories = new ArrayList<>(); + + boolean serializeNulls = DEFAULT_SERIALIZE_NULLS; + String datePattern = DEFAULT_DATE_PATTERN; + int dateStyle = DateFormat.DEFAULT; + int timeStyle = DateFormat.DEFAULT; + boolean complexMapKeySerialization = DEFAULT_COMPLEX_MAP_KEYS; + boolean serializeSpecialFloatingPointValues = DEFAULT_SPECIALIZE_FLOAT_VALUES; + boolean escapeHtmlChars = DEFAULT_ESCAPE_HTML; + FormattingStyle formattingStyle = DEFAULT_FORMATTING_STYLE; + boolean generateNonExecutableJson = DEFAULT_JSON_NON_EXECUTABLE; + Strictness strictness = DEFAULT_STRICTNESS; + boolean useJdkUnsafe = DEFAULT_USE_JDK_UNSAFE; + ToNumberStrategy objectToNumberStrategy = DEFAULT_OBJECT_TO_NUMBER_STRATEGY; + ToNumberStrategy numberToNumberStrategy = DEFAULT_NUMBER_TO_NUMBER_STRATEGY; + final ArrayDeque reflectionFilters = new ArrayDeque<>(); /** * Creates a GsonBuilder instance that can be used to build Gson with various configuration @@ -884,43 +919,127 @@ public GsonBuilder addReflectionAccessFilter(ReflectionAccessFilter filter) { * @return an instance of Gson configured with the options currently set in this builder */ public Gson create() { - List factories = - new ArrayList<>(this.factories.size() + this.hierarchyFactories.size() + 3); - factories.addAll(this.factories); - Collections.reverse(factories); - - List hierarchyFactories = new ArrayList<>(this.hierarchyFactories); - Collections.reverse(hierarchyFactories); - factories.addAll(hierarchyFactories); - - addTypeAdaptersForDate(datePattern, dateStyle, timeStyle, factories); - - return new Gson( - excluder, - fieldNamingPolicy, - new HashMap<>(instanceCreators), - serializeNulls, - complexMapKeySerialization, - generateNonExecutableJson, - escapeHtmlChars, - formattingStyle, - strictness, - serializeSpecialFloatingPointValues, - useJdkUnsafe, - longSerializationPolicy, - datePattern, - dateStyle, - timeStyle, - new ArrayList<>(this.factories), - new ArrayList<>(this.hierarchyFactories), - factories, - objectToNumberStrategy, - numberToNumberStrategy, - new ArrayList<>(reflectionFilters)); - } - - private static void addTypeAdaptersForDate( - String datePattern, int dateStyle, int timeStyle, List factories) { + return new Gson(this); + } + + List createFactories( + ConstructorConstructor constructorConstructor, + JsonAdapterAnnotationTypeAdapterFactory jsonAdapterFactory) { + ArrayList factories = new ArrayList<>(); + + // built-in type adapters that cannot be overridden + factories.add(TypeAdapters.JSON_ELEMENT_FACTORY); + factories.add(ObjectTypeAdapter.getFactory(objectToNumberStrategy)); + + // the excluder must precede all adapters that handle user-defined types + factories.add(excluder); + + // users' type adapters + addUserDefinedAdapters(factories); + + // custom Date adapters + addDateTypeAdapters(factories); + + // type adapters for basic platform types + 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); + TypeAdapter longAdapter = longSerializationPolicy.typeAdapter(); + factories.add(TypeAdapters.newFactory(long.class, Long.class, longAdapter)); + factories.add(TypeAdapters.newFactory(double.class, Double.class, doubleAdapter())); + factories.add(TypeAdapters.newFactory(float.class, Float.class, floatAdapter())); + factories.add(NumberTypeAdapter.getFactory(numberToNumberStrategy)); + factories.add(TypeAdapters.ATOMIC_INTEGER_FACTORY); + factories.add(TypeAdapters.ATOMIC_BOOLEAN_FACTORY); + factories.add( + TypeAdapters.newFactory(AtomicLong.class, TypeAdapters.atomicLongAdapter(longAdapter))); + factories.add( + TypeAdapters.newFactory( + AtomicLongArray.class, TypeAdapters.atomicLongArrayAdapter(longAdapter))); + factories.add(TypeAdapters.ATOMIC_INTEGER_ARRAY_FACTORY); + factories.add(TypeAdapters.CHARACTER_FACTORY); + factories.add(TypeAdapters.STRING_BUILDER_FACTORY); + factories.add(TypeAdapters.STRING_BUFFER_FACTORY); + factories.add(TypeAdapters.BIG_DECIMAL_FACTORY); + factories.add(TypeAdapters.BIG_INTEGER_FACTORY); + // Add adapter for LazilyParsedNumber because user can obtain it from Gson and then try to + // serialize it again + factories.add(TypeAdapters.LAZILY_PARSED_NUMBER_FACTORY); + factories.add(TypeAdapters.URL_FACTORY); + factories.add(TypeAdapters.URI_FACTORY); + factories.add(TypeAdapters.UUID_FACTORY); + factories.add(TypeAdapters.CURRENCY_FACTORY); + factories.add(TypeAdapters.LOCALE_FACTORY); + factories.add(TypeAdapters.INET_ADDRESS_FACTORY); + factories.add(TypeAdapters.BIT_SET_FACTORY); + factories.add(DefaultDateTypeAdapter.DEFAULT_STYLE_FACTORY); + factories.add(TypeAdapters.CALENDAR_FACTORY); + TypeAdapterFactory javaTimeFactory = TypeAdapters.javaTimeTypeAdapterFactory(); + if (javaTimeFactory != null) { + factories.add(javaTimeFactory); + } + factories.addAll(SqlTypesSupport.SQL_TYPE_FACTORIES); + factories.add(ArrayTypeAdapter.FACTORY); + factories.add(TypeAdapters.CLASS_FACTORY); + + // type adapters for composite and user-defined types + factories.add(new CollectionTypeAdapterFactory(constructorConstructor)); + factories.add(new MapTypeAdapterFactory(constructorConstructor, complexMapKeySerialization)); + factories.add(jsonAdapterFactory); + factories.add(TypeAdapters.ENUM_FACTORY); + factories.add( + new ReflectiveTypeAdapterFactory( + constructorConstructor, + fieldNamingPolicy, + excluder, + jsonAdapterFactory, + newImmutableList(reflectionFilters))); + + factories.trimToSize(); + return Collections.unmodifiableList(factories); + } + + static List newImmutableList(Collection collection) { + if (collection.isEmpty()) { + return Collections.emptyList(); + } + if (collection.size() == 1) { + return Collections.singletonList( + collection instanceof List + ? ((List) collection).get(0) + : collection.iterator().next()); + } + @SuppressWarnings("unchecked") + List list = (List) Collections.unmodifiableList(Arrays.asList(collection.toArray())); + return list; + } + + private TypeAdapter doubleAdapter() { + return serializeSpecialFloatingPointValues ? TypeAdapters.DOUBLE : TypeAdapters.DOUBLE_STRICT; + } + + private TypeAdapter floatAdapter() { + return serializeSpecialFloatingPointValues ? TypeAdapters.FLOAT : TypeAdapters.FLOAT_STRICT; + } + + private void addUserDefinedAdapters(List all) { + if (!this.factories.isEmpty()) { + List reversedFactories = new ArrayList<>(this.factories); + Collections.reverse(reversedFactories); + all.addAll(reversedFactories); + } + + if (!this.hierarchyFactories.isEmpty()) { + List reversedHierarchyFactories = + new ArrayList<>(this.hierarchyFactories); + Collections.reverse(reversedHierarchyFactories); + all.addAll(reversedHierarchyFactories); + } + } + + private void addDateTypeAdapters(List factories) { TypeAdapterFactory dateAdapterFactory; boolean sqlTypesSupported = SqlTypesSupport.SUPPORTS_SQL_TYPES; TypeAdapterFactory sqlTimestampAdapterFactory = null; diff --git a/gson/src/test/java/com/google/gson/GsonTest.java b/gson/src/test/java/com/google/gson/GsonTest.java index 4798168984..2d00dd4d35 100644 --- a/gson/src/test/java/com/google/gson/GsonTest.java +++ b/gson/src/test/java/com/google/gson/GsonTest.java @@ -28,10 +28,6 @@ import java.io.IOException; import java.io.StringReader; import java.io.StringWriter; -import java.text.DateFormat; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; import java.util.concurrent.CountDownLatch; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; @@ -53,6 +49,17 @@ public final class GsonTest { private static final ToNumberStrategy CUSTOM_NUMBER_TO_NUMBER_STRATEGY = ToNumberPolicy.LAZILY_PARSED_NUMBER; + private static Gson createGson() { + GsonBuilder gsonBuilder = new GsonBuilder(); + gsonBuilder.excluder = CUSTOM_EXCLUDER; + gsonBuilder.fieldNamingPolicy = CUSTOM_FIELD_NAMING_STRATEGY; + gsonBuilder.serializeNulls = true; + gsonBuilder.disableHtmlEscaping(); + gsonBuilder.objectToNumberStrategy = CUSTOM_OBJECT_TO_NUMBER_STRATEGY; + gsonBuilder.numberToNumberStrategy = CUSTOM_NUMBER_TO_NUMBER_STRATEGY; + return gsonBuilder.create(); + } + @Test public void testStrictnessDefault() { assertThat(new Gson().strictness).isNull(); @@ -60,29 +67,7 @@ public void testStrictnessDefault() { @Test public void testOverridesDefaultExcluder() { - Gson gson = - new Gson( - CUSTOM_EXCLUDER, - CUSTOM_FIELD_NAMING_STRATEGY, - new HashMap<>(), - true, - false, - true, - false, - FormattingStyle.PRETTY, - Strictness.LENIENT, - false, - true, - LongSerializationPolicy.DEFAULT, - null, - DateFormat.DEFAULT, - DateFormat.DEFAULT, - new ArrayList<>(), - new ArrayList<>(), - new ArrayList<>(), - CUSTOM_OBJECT_TO_NUMBER_STRATEGY, - CUSTOM_NUMBER_TO_NUMBER_STRATEGY, - Collections.emptyList()); + Gson gson = createGson(); assertThat(gson.excluder).isEqualTo(CUSTOM_EXCLUDER); assertThat(gson.fieldNamingStrategy()).isEqualTo(CUSTOM_FIELD_NAMING_STRATEGY); @@ -92,29 +77,7 @@ public void testOverridesDefaultExcluder() { @Test public void testClonedTypeAdapterFactoryListsAreIndependent() { - Gson original = - new Gson( - CUSTOM_EXCLUDER, - CUSTOM_FIELD_NAMING_STRATEGY, - new HashMap<>(), - true, - false, - true, - false, - FormattingStyle.PRETTY, - Strictness.LENIENT, - false, - true, - LongSerializationPolicy.DEFAULT, - null, - DateFormat.DEFAULT, - DateFormat.DEFAULT, - new ArrayList<>(), - new ArrayList<>(), - new ArrayList<>(), - CUSTOM_OBJECT_TO_NUMBER_STRATEGY, - CUSTOM_NUMBER_TO_NUMBER_STRATEGY, - Collections.emptyList()); + Gson original = createGson(); Gson clone = original.newBuilder().registerTypeAdapter(int.class, new TestTypeAdapter()).create();