From 08bbb226f11a1f7f76835f953e700d905e1fab4d Mon Sep 17 00:00:00 2001 From: Warren Smith Date: Wed, 20 Sep 2017 18:53:10 -0700 Subject: [PATCH] Add newBuilder() API (#1142) * Add Gson.newBuilder API. * Remove redundant test. * Address Codacy comments. * Reduce visibility of GsonBuilder constructor. --- gson/src/main/java/com/google/gson/Gson.java | 64 ++++++++++++++----- .../java/com/google/gson/GsonBuilder.java | 29 ++++++++- .../test/java/com/google/gson/GsonTest.java | 30 ++++++++- 3 files changed, 105 insertions(+), 18 deletions(-) diff --git a/gson/src/main/java/com/google/gson/Gson.java b/gson/src/main/java/com/google/gson/Gson.java index fc8ba2ee..5b1c6c83 100644 --- a/gson/src/main/java/com/google/gson/Gson.java +++ b/gson/src/main/java/com/google/gson/Gson.java @@ -25,6 +25,7 @@ import java.io.Writer; import java.lang.reflect.Type; import java.math.BigDecimal; import java.math.BigInteger; +import java.text.DateFormat; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; @@ -124,18 +125,28 @@ public final class Gson { private final Map, TypeAdapter> typeTokenCache = new ConcurrentHashMap, TypeAdapter>(); - private final List factories; private final ConstructorConstructor constructorConstructor; - - private final Excluder excluder; - private final FieldNamingStrategy fieldNamingStrategy; - private final boolean serializeNulls; - private final boolean htmlSafe; - private final boolean generateNonExecutableJson; - private final boolean prettyPrinting; - private final boolean lenient; private final JsonAdapterAnnotationTypeAdapterFactory jsonAdapterFactory; + final List factories; + + final Excluder excluder; + final FieldNamingStrategy fieldNamingStrategy; + final Map> instanceCreators; + final boolean serializeNulls; + final boolean complexMapKeySerialization; + final boolean generateNonExecutableJson; + final boolean htmlSafe; + final boolean prettyPrinting; + final boolean lenient; + final boolean serializeSpecialFloatingPointValues; + final String datePattern; + final int dateStyle; + final int timeStyle; + final LongSerializationPolicy longSerializationPolicy; + final List builderFactories; + final List builderHierarchyFactories; + /** * Constructs a Gson object with default configuration. The default configuration has the * following settings: @@ -175,23 +186,36 @@ public final class Gson { Collections.>emptyMap(), DEFAULT_SERIALIZE_NULLS, DEFAULT_COMPLEX_MAP_KEYS, DEFAULT_JSON_NON_EXECUTABLE, DEFAULT_ESCAPE_HTML, DEFAULT_PRETTY_PRINT, DEFAULT_LENIENT, DEFAULT_SPECIALIZE_FLOAT_VALUES, - LongSerializationPolicy.DEFAULT, Collections.emptyList()); + LongSerializationPolicy.DEFAULT, null, DateFormat.DEFAULT, DateFormat.DEFAULT, + Collections.emptyList(), Collections.emptyList(), + Collections.emptyList()); } Gson(final Excluder excluder, final FieldNamingStrategy fieldNamingStrategy, final Map> instanceCreators, boolean serializeNulls, boolean complexMapKeySerialization, boolean generateNonExecutableGson, boolean htmlSafe, boolean prettyPrinting, boolean lenient, boolean serializeSpecialFloatingPointValues, - LongSerializationPolicy longSerializationPolicy, - List typeAdapterFactories) { - this.constructorConstructor = new ConstructorConstructor(instanceCreators); + LongSerializationPolicy longSerializationPolicy, String datePattern, int dateStyle, + int timeStyle, List builderFactories, + List builderHierarchyFactories, + List factoriesToBeAdded) { this.excluder = excluder; this.fieldNamingStrategy = fieldNamingStrategy; + this.instanceCreators = instanceCreators; + this.constructorConstructor = new ConstructorConstructor(instanceCreators); this.serializeNulls = serializeNulls; + this.complexMapKeySerialization = complexMapKeySerialization; this.generateNonExecutableJson = generateNonExecutableGson; this.htmlSafe = htmlSafe; this.prettyPrinting = prettyPrinting; this.lenient = lenient; + this.serializeSpecialFloatingPointValues = serializeSpecialFloatingPointValues; + this.longSerializationPolicy = longSerializationPolicy; + this.datePattern = datePattern; + this.dateStyle = dateStyle; + this.timeStyle = timeStyle; + this.builderFactories = builderFactories; + this.builderHierarchyFactories = builderHierarchyFactories; List factories = new ArrayList(); @@ -202,8 +226,8 @@ public final class Gson { // the excluder must precede all adapters that handle user-defined types factories.add(excluder); - // user's type adapters - factories.addAll(typeAdapterFactories); + // users' type adapters + factories.addAll(factoriesToBeAdded); // type adapters for basic platform types factories.add(TypeAdapters.STRING_FACTORY); @@ -255,6 +279,16 @@ public final class Gson { this.factories = Collections.unmodifiableList(factories); } + /** + * Returns a new GsonBuilder containing all custom factories and configuration used by the current + * instance. + * + * @return a GsonBuilder instance. + */ + public GsonBuilder newBuilder() { + return new GsonBuilder(this); + } + public Excluder excluder() { return excluder; } diff --git a/gson/src/main/java/com/google/gson/GsonBuilder.java b/gson/src/main/java/com/google/gson/GsonBuilder.java index bd4b87dd..b97be452 100644 --- a/gson/src/main/java/com/google/gson/GsonBuilder.java +++ b/gson/src/main/java/com/google/gson/GsonBuilder.java @@ -104,6 +104,31 @@ public final class GsonBuilder { public GsonBuilder() { } + /** + * Constructs a GsonBuilder instance from a Gson instance. The newly constructed GsonBuilder + * has the same configuration as the previously built Gson instance. + * + * @param gson the gson instance whose configuration should by applied to a new GsonBuilder. + */ + GsonBuilder(Gson gson) { + this.excluder = gson.excluder; + this.fieldNamingPolicy = gson.fieldNamingStrategy; + this.instanceCreators.putAll(gson.instanceCreators); + this.serializeNulls = gson.serializeNulls; + this.complexMapKeySerialization = gson.complexMapKeySerialization; + this.generateNonExecutableJson = gson.generateNonExecutableJson; + this.escapeHtmlChars = gson.htmlSafe; + this.prettyPrinting = gson.prettyPrinting; + this.lenient = gson.lenient; + this.serializeSpecialFloatingPointValues = gson.serializeSpecialFloatingPointValues; + this.longSerializationPolicy = gson.longSerializationPolicy; + this.datePattern = gson.datePattern; + this.dateStyle = gson.dateStyle; + this.timeStyle = gson.timeStyle; + this.factories.addAll(gson.builderFactories); + this.hierarchyFactories.addAll(gson.builderHierarchyFactories); + } + /** * Configures Gson to enable versioning support. * @@ -572,7 +597,9 @@ public final class GsonBuilder { return new Gson(excluder, fieldNamingPolicy, instanceCreators, serializeNulls, complexMapKeySerialization, generateNonExecutableJson, escapeHtmlChars, prettyPrinting, lenient, - serializeSpecialFloatingPointValues, longSerializationPolicy, factories); + serializeSpecialFloatingPointValues, longSerializationPolicy, + datePattern, dateStyle, timeStyle, + this.factories, this.hierarchyFactories, factories); } @SuppressWarnings("unchecked") diff --git a/gson/src/test/java/com/google/gson/GsonTest.java b/gson/src/test/java/com/google/gson/GsonTest.java index fb0c0032..eec2ec91 100644 --- a/gson/src/test/java/com/google/gson/GsonTest.java +++ b/gson/src/test/java/com/google/gson/GsonTest.java @@ -17,8 +17,12 @@ package com.google.gson; import com.google.gson.internal.Excluder; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonWriter; +import java.io.IOException; import java.lang.reflect.Field; import java.lang.reflect.Type; +import java.text.DateFormat; import java.util.ArrayList; import java.util.HashMap; import junit.framework.TestCase; @@ -43,12 +47,34 @@ public final class GsonTest extends TestCase { public void testOverridesDefaultExcluder() { Gson gson = new Gson(CUSTOM_EXCLUDER, CUSTOM_FIELD_NAMING_STRATEGY, new HashMap>(), true, false, true, false, - true, true, false, LongSerializationPolicy.DEFAULT, - new ArrayList()); + true, true, false, LongSerializationPolicy.DEFAULT, null, DateFormat.DEFAULT, + DateFormat.DEFAULT, new ArrayList(), + new ArrayList(), new ArrayList()); assertEquals(CUSTOM_EXCLUDER, gson.excluder()); assertEquals(CUSTOM_FIELD_NAMING_STRATEGY, gson.fieldNamingStrategy()); assertEquals(true, gson.serializeNulls()); assertEquals(false, gson.htmlSafe()); } + + public void testClonedTypeAdapterFactoryListsAreIndependent() { + Gson original = new Gson(CUSTOM_EXCLUDER, CUSTOM_FIELD_NAMING_STRATEGY, + new HashMap>(), true, false, true, false, + true, true, false, LongSerializationPolicy.DEFAULT, null, DateFormat.DEFAULT, + DateFormat.DEFAULT, new ArrayList(), + new ArrayList(), new ArrayList()); + + Gson clone = original.newBuilder() + .registerTypeAdapter(Object.class, new TestTypeAdapter()) + .create(); + + assertEquals(original.factories.size() + 1, clone.factories.size()); + } + + private static final class TestTypeAdapter extends TypeAdapter { + @Override public void write(JsonWriter out, Object value) throws IOException { + // Test stub. + } + @Override public Object read(JsonReader in) throws IOException { return null; } + } }