diff --git a/examples/android-proguard-example/src/com/google/gson/examples/android/model/Cart.java b/examples/android-proguard-example/src/com/google/gson/examples/android/model/Cart.java index 7582036ebc..7d15a9d96f 100644 --- a/examples/android-proguard-example/src/com/google/gson/examples/android/model/Cart.java +++ b/examples/android-proguard-example/src/com/google/gson/examples/android/model/Cart.java @@ -41,6 +41,34 @@ public Cart(List lineItems, String buyerName, String creditCard) { this.creditCard = creditCard; } + @SuppressWarnings("unchecked") + public static String getSimpleTypeName(Type type) { + if (type == null) { + return "null"; + } + if (type instanceof Class) { + return ((Class)type).getSimpleName(); + } else if (type instanceof ParameterizedType) { + ParameterizedType pType = (ParameterizedType) type; + StringBuilder sb = new StringBuilder(getSimpleTypeName(pType.getRawType())); + sb.append('<'); + boolean first = true; + for (Type argumentType : pType.getActualTypeArguments()) { + if (first) { + first = false; + } else { + sb.append(','); + } + sb.append(getSimpleTypeName(argumentType)); + } + sb.append('>'); + return sb.toString(); + } else if (type instanceof WildcardType) { + return "?"; + } + return type.toString(); + } + public List getLineItems() { return lineItems; } @@ -77,32 +105,4 @@ public String toString() { + "LINE_ITEMS: " + itemsText.toString() + "]"; } - @SuppressWarnings("unchecked") - public static String getSimpleTypeName(Type type) { - if (type == null) { - return "null"; - } - if (type instanceof Class) { - return ((Class)type).getSimpleName(); - } else if (type instanceof ParameterizedType) { - ParameterizedType pType = (ParameterizedType) type; - StringBuilder sb = new StringBuilder(getSimpleTypeName(pType.getRawType())); - sb.append('<'); - boolean first = true; - for (Type argumentType : pType.getActualTypeArguments()) { - if (first) { - first = false; - } else { - sb.append(','); - } - sb.append(getSimpleTypeName(argumentType)); - } - sb.append('>'); - return sb.toString(); - } else if (type instanceof WildcardType) { - return "?"; - } - return type.toString(); - } - } diff --git a/extras/src/main/java/com/google/gson/extras/examples/rawcollections/RawCollectionsExample.java b/extras/src/main/java/com/google/gson/extras/examples/rawcollections/RawCollectionsExample.java index 9ea350cae8..d102a2c263 100644 --- a/extras/src/main/java/com/google/gson/extras/examples/rawcollections/RawCollectionsExample.java +++ b/extras/src/main/java/com/google/gson/extras/examples/rawcollections/RawCollectionsExample.java @@ -15,27 +15,13 @@ */ package com.google.gson.extras.examples.rawcollections; -import java.util.ArrayList; -import java.util.Collection; - import com.google.gson.Gson; import com.google.gson.JsonArray; import com.google.gson.JsonParser; +import java.util.ArrayList; +import java.util.Collection; public class RawCollectionsExample { - static class Event { - private String name; - private String source; - private Event(String name, String source) { - this.name = name; - this.source = source; - } - @Override - public String toString() { - return String.format("(name=%s, source=%s)", name, source); - } - } - @SuppressWarnings({ "unchecked", "rawtypes" }) public static void main(String[] args) { Gson gson = new Gson(); @@ -51,4 +37,17 @@ public static void main(String[] args) { Event event = gson.fromJson(array.get(2), Event.class); System.out.printf("Using Gson.fromJson() to get: %s, %d, %s", message, number, event); } + + static class Event { + private String name; + private String source; + private Event(String name, String source) { + this.name = name; + this.source = source; + } + @Override + public String toString() { + return String.format("(name=%s, source=%s)", name, source); + } + } } diff --git a/extras/src/main/java/com/google/gson/graph/GraphAdapterBuilder.java b/extras/src/main/java/com/google/gson/graph/GraphAdapterBuilder.java index b226b220f0..e5652214b5 100644 --- a/extras/src/main/java/com/google/gson/graph/GraphAdapterBuilder.java +++ b/extras/src/main/java/com/google/gson/graph/GraphAdapterBuilder.java @@ -269,23 +269,20 @@ static class Element { * This element's name in the top level graph object. */ private final String id; - + /** + * The element to deserialize. Unused in serialization. + */ + private final JsonElement element; /** * The value if known. During deserialization this is lazily populated. */ private T value; - /** * This element's type adapter if known. During deserialization this is * lazily populated. */ private TypeAdapter typeAdapter; - /** - * The element to deserialize. Unused in serialization. - */ - private final JsonElement element; - Element(T value, String id, TypeAdapter typeAdapter, JsonElement element) { this.value = value; this.id = id; diff --git a/extras/src/main/java/com/google/gson/typeadapters/PostConstructAdapterFactory.java b/extras/src/main/java/com/google/gson/typeadapters/PostConstructAdapterFactory.java index 450ebbab2d..1cce6d1e7e 100644 --- a/extras/src/main/java/com/google/gson/typeadapters/PostConstructAdapterFactory.java +++ b/extras/src/main/java/com/google/gson/typeadapters/PostConstructAdapterFactory.java @@ -16,18 +16,16 @@ package com.google.gson.typeadapters; -import java.io.IOException; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; - -import javax.annotation.PostConstruct; - import com.google.gson.Gson; import com.google.gson.TypeAdapter; import com.google.gson.TypeAdapterFactory; 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.InvocationTargetException; +import java.lang.reflect.Method; +import javax.annotation.PostConstruct; public class PostConstructAdapterFactory implements TypeAdapterFactory { // copied from https://gist.github.com/swankjesse/20df26adaf639ed7fd160f145a0b661a diff --git a/extras/src/main/java/com/google/gson/typeadapters/UtcDateTypeAdapter.java b/extras/src/main/java/com/google/gson/typeadapters/UtcDateTypeAdapter.java index 2278f842bf..1de7982c60 100644 --- a/extras/src/main/java/com/google/gson/typeadapters/UtcDateTypeAdapter.java +++ b/extras/src/main/java/com/google/gson/typeadapters/UtcDateTypeAdapter.java @@ -16,6 +16,10 @@ package com.google.gson.typeadapters; +import com.google.gson.JsonParseException; +import com.google.gson.TypeAdapter; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonWriter; import java.io.IOException; import java.text.ParseException; import java.text.ParsePosition; @@ -24,46 +28,12 @@ import java.util.GregorianCalendar; import java.util.Locale; import java.util.TimeZone; -import com.google.gson.JsonParseException; -import com.google.gson.TypeAdapter; -import com.google.gson.stream.JsonReader; -import com.google.gson.stream.JsonToken; -import com.google.gson.stream.JsonWriter; public final class UtcDateTypeAdapter extends TypeAdapter { - private final TimeZone UTC_TIME_ZONE = TimeZone.getTimeZone("UTC"); - - @Override - public void write(JsonWriter out, Date date) throws IOException { - if (date == null) { - out.nullValue(); - } else { - String value = format(date, true, UTC_TIME_ZONE); - out.value(value); - } - } - - @Override - public Date read(JsonReader in) throws IOException { - try { - switch (in.peek()) { - case NULL: - in.nextNull(); - return null; - default: - String date = in.nextString(); - // Instead of using iso8601Format.parse(value), we use Jackson's date parsing - // This is because Android doesn't support XXX because it is JDK 1.6 - return parse(date, new ParsePosition(0)); - } - } catch (ParseException e) { - throw new JsonParseException(e); - } - } - // Date parsing code from Jackson databind ISO8601Utils.java // https://github.com/FasterXML/jackson-databind/blob/master/src/main/java/com/fasterxml/jackson/databind/util/ISO8601Utils.java private static final String GMT_ID = "GMT"; + private final TimeZone UTC_TIME_ZONE = TimeZone.getTimeZone("UTC"); /** * Format date into yyyy-MM-ddThh:mm:ss[.sss][Z|[+-]hh:mm] @@ -113,6 +83,7 @@ private static String format(Date date, boolean millis, TimeZone tz) { return formatted.toString(); } + /** * Zero pad a number to a specified length * @@ -279,4 +250,32 @@ private static int parseInt(String value, int beginIndex, int endIndex) throws N } return -result; } + + @Override + public void write(JsonWriter out, Date date) throws IOException { + if (date == null) { + out.nullValue(); + } else { + String value = format(date, true, UTC_TIME_ZONE); + out.value(value); + } + } + + @Override + public Date read(JsonReader in) throws IOException { + try { + switch (in.peek()) { + case NULL: + in.nextNull(); + return null; + default: + String date = in.nextString(); + // Instead of using iso8601Format.parse(value), we use Jackson's date parsing + // This is because Android doesn't support XXX because it is JDK 1.6 + return parse(date, new ParsePosition(0)); + } + } catch (ParseException e) { + throw new JsonParseException(e); + } + } } diff --git a/extras/src/test/java/com/google/gson/graph/GraphAdapterBuilderTest.java b/extras/src/test/java/com/google/gson/graph/GraphAdapterBuilderTest.java index 3b2425ec9e..be8a9f40da 100644 --- a/extras/src/test/java/com/google/gson/graph/GraphAdapterBuilderTest.java +++ b/extras/src/test/java/com/google/gson/graph/GraphAdapterBuilderTest.java @@ -19,17 +19,15 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertSame; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.reflect.TypeToken; import java.lang.reflect.Type; import java.util.ArrayList; import java.util.Collections; import java.util.List; - import org.junit.Test; -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; -import com.google.gson.reflect.TypeToken; - public final class GraphAdapterBuilderTest { @Test public void testSerialization() { diff --git a/gson/src/main/java/com/google/gson/Gson.java b/gson/src/main/java/com/google/gson/Gson.java index 0339d56920..54d841b376 100644 --- a/gson/src/main/java/com/google/gson/Gson.java +++ b/gson/src/main/java/com/google/gson/Gson.java @@ -153,28 +153,7 @@ public final class Gson { static final ToNumberStrategy DEFAULT_NUMBER_TO_NUMBER_STRATEGY = ToNumberPolicy.LAZILY_PARSED_NUMBER; private static final String JSON_NON_EXECUTABLE_PREFIX = ")]}'\n"; - - /** - * This thread local guards against reentrant calls to {@link #getAdapter(TypeToken)}. - * 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, - * {@link FutureTypeAdapter}, which is wired up once the initial adapter has - * been created. - * - *

The map stores the type adapters for ongoing {@code getAdapter} calls, - * with the type token provided to {@code getAdapter} as key and either - * {@code FutureTypeAdapter} or a regular {@code TypeAdapter} as value. - */ - private final ThreadLocal, TypeAdapter>> threadLocalAdapterResults = new ThreadLocal<>(); - - private final ConcurrentMap, TypeAdapter> typeTokenCache = new ConcurrentHashMap<>(); - - private final ConstructorConstructor constructorConstructor; - private final JsonAdapterAnnotationTypeAdapterFactory jsonAdapterFactory; - final List factories; - final Excluder excluder; final FieldNamingStrategy fieldNamingStrategy; final Map> instanceCreators; @@ -195,6 +174,22 @@ public final class Gson { final ToNumberStrategy objectToNumberStrategy; final ToNumberStrategy numberToNumberStrategy; final List reflectionFilters; + /** + * This thread local guards against reentrant calls to {@link #getAdapter(TypeToken)}. + * 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, + * {@link FutureTypeAdapter}, which is wired up once the initial adapter has + * been created. + * + *

The map stores the type adapters for ongoing {@code getAdapter} calls, + * with the type token provided to {@code getAdapter} as key and either + * {@code FutureTypeAdapter} or a regular {@code TypeAdapter} as value. + */ + private final ThreadLocal, TypeAdapter>> threadLocalAdapterResults = new ThreadLocal<>(); + private final ConcurrentMap, TypeAdapter> typeTokenCache = new ConcurrentHashMap<>(); + private final ConstructorConstructor constructorConstructor; + private final JsonAdapterAnnotationTypeAdapterFactory jsonAdapterFactory; /** * Constructs a Gson object with default configuration. The default configuration has the @@ -343,6 +338,87 @@ public Gson() { this.factories = Collections.unmodifiableList(factories); } + static void checkValidFloatingPoint(double value) { + if (Double.isNaN(value) || Double.isInfinite(value)) { + throw new IllegalArgumentException(value + + " is not a valid double value as per JSON specification. To override this" + + " behavior, use GsonBuilder.serializeSpecialFloatingPointValues() method."); + } + } + + private static TypeAdapter longAdapter(LongSerializationPolicy longSerializationPolicy) { + if (longSerializationPolicy == LongSerializationPolicy.DEFAULT) { + return TypeAdapters.LONG; + } + return new TypeAdapter() { + @Override public Number read(JsonReader in) throws IOException { + if (in.peek() == JsonToken.NULL) { + in.nextNull(); + return null; + } + return in.nextLong(); + } + @Override public void write(JsonWriter out, Number value) throws IOException { + if (value == null) { + out.nullValue(); + return; + } + out.value(value.toString()); + } + }; + } + + private static TypeAdapter atomicLongAdapter(final TypeAdapter longAdapter) { + return new TypeAdapter() { + @Override public void write(JsonWriter out, AtomicLong value) throws IOException { + longAdapter.write(out, value.get()); + } + @Override public AtomicLong read(JsonReader in) throws IOException { + Number value = longAdapter.read(in); + return new AtomicLong(value.longValue()); + } + }.nullSafe(); + } + + private static TypeAdapter atomicLongArrayAdapter(final TypeAdapter longAdapter) { + return new TypeAdapter() { + @Override public void write(JsonWriter out, AtomicLongArray value) throws IOException { + out.beginArray(); + for (int i = 0, length = value.length(); i < length; i++) { + longAdapter.write(out, value.get(i)); + } + out.endArray(); + } + @Override public AtomicLongArray read(JsonReader in) throws IOException { + List list = new ArrayList<>(); + in.beginArray(); + while (in.hasNext()) { + long value = longAdapter.read(in).longValue(); + list.add(value); + } + in.endArray(); + int length = list.size(); + AtomicLongArray array = new AtomicLongArray(length); + for (int i = 0; i < length; ++i) { + array.set(i, list.get(i)); + } + return array; + } + }.nullSafe(); + } + + private static void assertFullConsumption(Object obj, JsonReader reader) { + try { + if (obj != null && reader.peek() != JsonToken.END_DOCUMENT) { + throw new JsonSyntaxException("JSON document was not fully consumed."); + } + } catch (MalformedJsonException e) { + throw new JsonSyntaxException(e); + } catch (IOException e) { + throw new JsonIOException(e); + } + } + /** * Returns a new GsonBuilder containing all custom factories and configuration used by the current * instance. @@ -443,75 +519,6 @@ private TypeAdapter floatAdapter(boolean serializeSpecialFloatingPointVa }; } - static void checkValidFloatingPoint(double value) { - if (Double.isNaN(value) || Double.isInfinite(value)) { - throw new IllegalArgumentException(value - + " is not a valid double value as per JSON specification. To override this" - + " behavior, use GsonBuilder.serializeSpecialFloatingPointValues() method."); - } - } - - private static TypeAdapter longAdapter(LongSerializationPolicy longSerializationPolicy) { - if (longSerializationPolicy == LongSerializationPolicy.DEFAULT) { - return TypeAdapters.LONG; - } - return new TypeAdapter() { - @Override public Number read(JsonReader in) throws IOException { - if (in.peek() == JsonToken.NULL) { - in.nextNull(); - return null; - } - return in.nextLong(); - } - @Override public void write(JsonWriter out, Number value) throws IOException { - if (value == null) { - out.nullValue(); - return; - } - out.value(value.toString()); - } - }; - } - - private static TypeAdapter atomicLongAdapter(final TypeAdapter longAdapter) { - return new TypeAdapter() { - @Override public void write(JsonWriter out, AtomicLong value) throws IOException { - longAdapter.write(out, value.get()); - } - @Override public AtomicLong read(JsonReader in) throws IOException { - Number value = longAdapter.read(in); - return new AtomicLong(value.longValue()); - } - }.nullSafe(); - } - - private static TypeAdapter atomicLongArrayAdapter(final TypeAdapter longAdapter) { - return new TypeAdapter() { - @Override public void write(JsonWriter out, AtomicLongArray value) throws IOException { - out.beginArray(); - for (int i = 0, length = value.length(); i < length; i++) { - longAdapter.write(out, value.get(i)); - } - out.endArray(); - } - @Override public AtomicLongArray read(JsonReader in) throws IOException { - List list = new ArrayList<>(); - in.beginArray(); - while (in.hasNext()) { - long value = longAdapter.read(in).longValue(); - list.add(value); - } - in.endArray(); - int length = list.size(); - AtomicLongArray array = new AtomicLongArray(length); - for (int i = 0; i < length; ++i) { - array.set(i, list.get(i)); - } - return array; - } - }.nullSafe(); - } - /** * Returns the type adapter for {@code type}. * @@ -1139,18 +1146,6 @@ public T fromJson(Reader json, TypeToken typeOfT) throws JsonIOException, return object; } - private static void assertFullConsumption(Object obj, JsonReader reader) { - try { - if (obj != null && reader.peek() != JsonToken.END_DOCUMENT) { - throw new JsonSyntaxException("JSON document was not fully consumed."); - } - } catch (MalformedJsonException e) { - throw new JsonSyntaxException(e); - } catch (IOException e) { - throw new JsonIOException(e); - } - } - // fromJson(JsonReader, Class) is unfortunately missing and cannot be added now without breaking // source compatibility in certain cases, see https://github.com/google/gson/pull/1700#discussion_r973764414 @@ -1329,6 +1324,14 @@ public T fromJson(JsonElement json, TypeToken typeOfT) throws JsonSyntaxE return fromJson(new JsonTreeReader(json), typeOfT); } + @Override + public String toString() { + return "{serializeNulls:" + serializeNulls + + ",factories:" + factories + + ",instanceCreators:" + constructorConstructor + + "}"; + } + /** * Proxy type adapter for cyclic type graphs. * @@ -1371,12 +1374,4 @@ private TypeAdapter delegate() { delegate().write(out, value); } } - - @Override - public String toString() { - return "{serializeNulls:" + serializeNulls - + ",factories:" + factories - + ",instanceCreators:" + constructorConstructor - + "}"; - } } diff --git a/gson/src/main/java/com/google/gson/GsonBuilder.java b/gson/src/main/java/com/google/gson/GsonBuilder.java index 8b04430f7f..0c163a5110 100644 --- a/gson/src/main/java/com/google/gson/GsonBuilder.java +++ b/gson/src/main/java/com/google/gson/GsonBuilder.java @@ -84,13 +84,14 @@ * @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<>(); /** tree-style hierarchy factories. These come after factories for backwards compatibility. */ private final List hierarchyFactories = new ArrayList<>(); + private final LinkedList reflectionFilters = new LinkedList<>(); + private Excluder excluder = Excluder.DEFAULT; + private LongSerializationPolicy longSerializationPolicy = LongSerializationPolicy.DEFAULT; + private FieldNamingStrategy fieldNamingPolicy = FieldNamingPolicy.IDENTITY; private boolean serializeNulls = DEFAULT_SERIALIZE_NULLS; private String datePattern = DEFAULT_DATE_PATTERN; private int dateStyle = DateFormat.DEFAULT; @@ -104,7 +105,6 @@ public final class GsonBuilder { 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 LinkedList reflectionFilters = new LinkedList<>(); /** * Creates a GsonBuilder instance that can be used to build Gson with various configuration diff --git a/gson/src/main/java/com/google/gson/JsonPrimitive.java b/gson/src/main/java/com/google/gson/JsonPrimitive.java index 66dacfe523..ec5327faba 100644 --- a/gson/src/main/java/com/google/gson/JsonPrimitive.java +++ b/gson/src/main/java/com/google/gson/JsonPrimitive.java @@ -76,6 +76,19 @@ public JsonPrimitive(Character c) { value = Objects.requireNonNull(c).toString(); } + /** + * Returns true if the specified number is an integral type + * (Long, Integer, Short, Byte, BigInteger) + */ + private static boolean isIntegral(JsonPrimitive primitive) { + if (primitive.value instanceof Number) { + Number number = (Number) primitive.value; + return number instanceof BigInteger || number instanceof Long || number instanceof Integer + || number instanceof Short || number instanceof Byte; + } + return false; + } + /** * Returns the same value as primitives are immutable. * @@ -293,17 +306,4 @@ public boolean equals(Object obj) { } return value.equals(other.value); } - - /** - * Returns true if the specified number is an integral type - * (Long, Integer, Short, Byte, BigInteger) - */ - private static boolean isIntegral(JsonPrimitive primitive) { - if (primitive.value instanceof Number) { - Number number = (Number) primitive.value; - return number instanceof BigInteger || number instanceof Long || number instanceof Integer - || number instanceof Short || number instanceof Byte; - } - return false; - } } diff --git a/gson/src/main/java/com/google/gson/ReflectionAccessFilter.java b/gson/src/main/java/com/google/gson/ReflectionAccessFilter.java index 254d2e5d2c..6ec3509f18 100644 --- a/gson/src/main/java/com/google/gson/ReflectionAccessFilter.java +++ b/gson/src/main/java/com/google/gson/ReflectionAccessFilter.java @@ -30,56 +30,6 @@ * @since 2.9.1 */ public interface ReflectionAccessFilter { - /** - * Result of a filter check. - * - * @since 2.9.1 - */ - enum FilterResult { - /** - * Reflection access for the class is allowed. - * - *

Note that this does not affect the Java access checks in any way, - * it only permits Gson to try using reflection for a class. The Java - * runtime might still deny such access. - */ - ALLOW, - /** - * The filter is indecisive whether reflection access should be allowed. - * The next registered filter will be consulted to get the result. If - * there is no next filter, this result acts like {@link #ALLOW}. - */ - INDECISIVE, - /** - * Blocks reflection access if a member of the class is not accessible - * by default and would have to be made accessible. This is unaffected - * by any {@code java} command line arguments being used to make packages - * accessible, or by module declaration directives which open the - * complete module or certain packages for reflection and will consider - * such packages inaccessible. - * - *

Note that this only works for Java 9 and higher, for older - * Java versions its functionality will be limited and it might behave like - * {@link #ALLOW}. Access checks are only performed as defined by the Java - * Language Specification (JLS 11 §6.6), - * restrictions imposed by a {@link SecurityManager} are not considered. - * - *

This result type is mainly intended to help enforce the access checks of - * the Java Platform Module System. It allows detecting illegal access, even if - * the used Java version would only log a warning, or is configured to open - * packages for reflection using command line arguments. - * - * @see AccessibleObject#canAccess(Object) - */ - BLOCK_INACCESSIBLE, - /** - * Blocks all reflection access for the class. Other means for serializing - * and deserializing the class, such as a {@link TypeAdapter}, have to - * be used. - */ - BLOCK_ALL - } - /** * Blocks all reflection access to members of standard Java classes which are * not accessible by default. However, reflection access is still allowed for @@ -109,7 +59,6 @@ enum FilterResult { : FilterResult.INDECISIVE; } }; - /** * Blocks all reflection access to members of standard Java classes. * @@ -134,7 +83,6 @@ enum FilterResult { : FilterResult.INDECISIVE; } }; - /** * Blocks all reflection access to members of standard Android classes. * @@ -158,7 +106,6 @@ enum FilterResult { : FilterResult.INDECISIVE; } }; - /** * Blocks all reflection access to members of classes belonging to programming * language platforms, such as Java, Android, Kotlin or Scala. @@ -193,4 +140,54 @@ enum FilterResult { * Result indicating whether reflection access is allowed */ FilterResult check(Class rawClass); + + /** + * Result of a filter check. + * + * @since 2.9.1 + */ + enum FilterResult { + /** + * Reflection access for the class is allowed. + * + *

Note that this does not affect the Java access checks in any way, + * it only permits Gson to try using reflection for a class. The Java + * runtime might still deny such access. + */ + ALLOW, + /** + * The filter is indecisive whether reflection access should be allowed. + * The next registered filter will be consulted to get the result. If + * there is no next filter, this result acts like {@link #ALLOW}. + */ + INDECISIVE, + /** + * Blocks reflection access if a member of the class is not accessible + * by default and would have to be made accessible. This is unaffected + * by any {@code java} command line arguments being used to make packages + * accessible, or by module declaration directives which open the + * complete module or certain packages for reflection and will consider + * such packages inaccessible. + * + *

Note that this only works for Java 9 and higher, for older + * Java versions its functionality will be limited and it might behave like + * {@link #ALLOW}. Access checks are only performed as defined by the Java + * Language Specification (JLS 11 §6.6), + * restrictions imposed by a {@link SecurityManager} are not considered. + * + *

This result type is mainly intended to help enforce the access checks of + * the Java Platform Module System. It allows detecting illegal access, even if + * the used Java version would only log a warning, or is configured to open + * packages for reflection using command line arguments. + * + * @see AccessibleObject#canAccess(Object) + */ + BLOCK_INACCESSIBLE, + /** + * Blocks all reflection access for the class. Other means for serializing + * and deserializing the class, such as a {@link TypeAdapter}, have to + * be used. + */ + BLOCK_ALL + } } diff --git a/gson/src/main/java/com/google/gson/internal/$Gson$Types.java b/gson/src/main/java/com/google/gson/internal/$Gson$Types.java index 4a925aa49b..68cd5170f0 100644 --- a/gson/src/main/java/com/google/gson/internal/$Gson$Types.java +++ b/gson/src/main/java/com/google/gson/internal/$Gson$Types.java @@ -33,8 +33,8 @@ import java.util.HashMap; import java.util.Map; import java.util.NoSuchElementException; -import java.util.Properties; import java.util.Objects; +import java.util.Properties; /** * Static methods for working with types. @@ -482,6 +482,7 @@ static void checkNotPrimitive(Type type) { } private static final class ParameterizedTypeImpl implements ParameterizedType, Serializable { + private static final long serialVersionUID = 0; private final Type ownerType; private final Type rawType; private final Type[] typeArguments; @@ -506,6 +507,10 @@ public ParameterizedTypeImpl(Type ownerType, Type rawType, Type... typeArguments } } + private static int hashCodeOrZero(Object o) { + return o != null ? o.hashCode() : 0; + } + @Override public Type[] getActualTypeArguments() { return typeArguments.clone(); } @@ -523,10 +528,6 @@ public ParameterizedTypeImpl(Type ownerType, Type rawType, Type... typeArguments && $Gson$Types.equals(this, (ParameterizedType) other); } - private static int hashCodeOrZero(Object o) { - return o != null ? o.hashCode() : 0; - } - @Override public int hashCode() { return Arrays.hashCode(typeArguments) ^ rawType.hashCode() @@ -546,11 +547,10 @@ private static int hashCodeOrZero(Object o) { } return stringBuilder.append(">").toString(); } - - private static final long serialVersionUID = 0; } private static final class GenericArrayTypeImpl implements GenericArrayType, Serializable { + private static final long serialVersionUID = 0; private final Type componentType; public GenericArrayTypeImpl(Type componentType) { @@ -574,8 +574,6 @@ public GenericArrayTypeImpl(Type componentType) { @Override public String toString() { return typeToString(componentType) + "[]"; } - - private static final long serialVersionUID = 0; } /** @@ -585,6 +583,7 @@ public GenericArrayTypeImpl(Type componentType) { * is set, the upper bound must be Object.class. */ private static final class WildcardTypeImpl implements WildcardType, Serializable { + private static final long serialVersionUID = 0; private final Type upperBound; private final Type lowerBound; @@ -635,7 +634,5 @@ public WildcardTypeImpl(Type[] upperBounds, Type[] lowerBounds) { return "? extends " + typeToString(upperBound); } } - - private static final long serialVersionUID = 0; } } diff --git a/gson/src/main/java/com/google/gson/internal/ConstructorConstructor.java b/gson/src/main/java/com/google/gson/internal/ConstructorConstructor.java index 115a2a0911..f4391cc264 100644 --- a/gson/src/main/java/com/google/gson/internal/ConstructorConstructor.java +++ b/gson/src/main/java/com/google/gson/internal/ConstructorConstructor.java @@ -80,81 +80,6 @@ static String checkInstantiable(Class c) { return null; } - public ObjectConstructor get(TypeToken typeToken) { - final Type type = typeToken.getType(); - final Class rawType = typeToken.getRawType(); - - // first try an instance creator - - @SuppressWarnings("unchecked") // types must agree - final InstanceCreator typeCreator = (InstanceCreator) instanceCreators.get(type); - if (typeCreator != null) { - return new ObjectConstructor() { - @Override public T construct() { - return typeCreator.createInstance(type); - } - }; - } - - // Next try raw type match for instance creators - @SuppressWarnings("unchecked") // types must agree - final InstanceCreator rawTypeCreator = - (InstanceCreator) instanceCreators.get(rawType); - if (rawTypeCreator != null) { - return new ObjectConstructor() { - @Override public T construct() { - return rawTypeCreator.createInstance(type); - } - }; - } - - // First consider special constructors before checking for no-args constructors - // below to avoid matching internal no-args constructors which might be added in - // future JDK versions - ObjectConstructor specialConstructor = newSpecialCollectionConstructor(type, rawType); - if (specialConstructor != null) { - return specialConstructor; - } - - FilterResult filterResult = ReflectionAccessFilterHelper.getFilterResult(reflectionFilters, rawType); - ObjectConstructor defaultConstructor = newDefaultConstructor(rawType, filterResult); - if (defaultConstructor != null) { - return defaultConstructor; - } - - ObjectConstructor defaultImplementation = newDefaultImplementationConstructor(type, rawType); - if (defaultImplementation != null) { - return defaultImplementation; - } - - // Check whether type is instantiable; otherwise ReflectionAccessFilter recommendation - // of adjusting filter suggested below is irrelevant since it would not solve the problem - final String exceptionMessage = checkInstantiable(rawType); - if (exceptionMessage != null) { - return new ObjectConstructor() { - @Override public T construct() { - throw new JsonIOException(exceptionMessage); - } - }; - } - - // Consider usage of Unsafe as reflection, so don't use if BLOCK_ALL - // Additionally, since it is not calling any constructor at all, don't use if BLOCK_INACCESSIBLE - if (filterResult == FilterResult.ALLOW) { - // finally try unsafe - return newUnsafeAllocator(rawType); - } else { - final String message = "Unable to create instance of " + rawType + "; ReflectionAccessFilter " - + "does not permit using reflection or Unsafe. Register an InstanceCreator or a TypeAdapter " - + "for this type or adjust the access filter to allow using reflection."; - return new ObjectConstructor() { - @Override public T construct() { - throw new JsonIOException(message); - } - }; - } - } - /** * Creates constructors for special JDK collection types which do not have a public no-args constructor. */ @@ -361,6 +286,81 @@ private static ObjectConstructor newDefaultImplementationConstructor( return null; } + public ObjectConstructor get(TypeToken typeToken) { + final Type type = typeToken.getType(); + final Class rawType = typeToken.getRawType(); + + // first try an instance creator + + @SuppressWarnings("unchecked") // types must agree + final InstanceCreator typeCreator = (InstanceCreator) instanceCreators.get(type); + if (typeCreator != null) { + return new ObjectConstructor() { + @Override public T construct() { + return typeCreator.createInstance(type); + } + }; + } + + // Next try raw type match for instance creators + @SuppressWarnings("unchecked") // types must agree + final InstanceCreator rawTypeCreator = + (InstanceCreator) instanceCreators.get(rawType); + if (rawTypeCreator != null) { + return new ObjectConstructor() { + @Override public T construct() { + return rawTypeCreator.createInstance(type); + } + }; + } + + // First consider special constructors before checking for no-args constructors + // below to avoid matching internal no-args constructors which might be added in + // future JDK versions + ObjectConstructor specialConstructor = newSpecialCollectionConstructor(type, rawType); + if (specialConstructor != null) { + return specialConstructor; + } + + FilterResult filterResult = ReflectionAccessFilterHelper.getFilterResult(reflectionFilters, rawType); + ObjectConstructor defaultConstructor = newDefaultConstructor(rawType, filterResult); + if (defaultConstructor != null) { + return defaultConstructor; + } + + ObjectConstructor defaultImplementation = newDefaultImplementationConstructor(type, rawType); + if (defaultImplementation != null) { + return defaultImplementation; + } + + // Check whether type is instantiable; otherwise ReflectionAccessFilter recommendation + // of adjusting filter suggested below is irrelevant since it would not solve the problem + final String exceptionMessage = checkInstantiable(rawType); + if (exceptionMessage != null) { + return new ObjectConstructor() { + @Override public T construct() { + throw new JsonIOException(exceptionMessage); + } + }; + } + + // Consider usage of Unsafe as reflection, so don't use if BLOCK_ALL + // Additionally, since it is not calling any constructor at all, don't use if BLOCK_INACCESSIBLE + if (filterResult == FilterResult.ALLOW) { + // finally try unsafe + return newUnsafeAllocator(rawType); + } else { + final String message = "Unable to create instance of " + rawType + "; ReflectionAccessFilter " + + "does not permit using reflection or Unsafe. Register an InstanceCreator or a TypeAdapter " + + "for this type or adjust the access filter to allow using reflection."; + return new ObjectConstructor() { + @Override public T construct() { + throw new JsonIOException(message); + } + }; + } + } + private ObjectConstructor newUnsafeAllocator(final Class rawType) { if (useJdkUnsafe) { return new ObjectConstructor() { diff --git a/gson/src/main/java/com/google/gson/internal/Excluder.java b/gson/src/main/java/com/google/gson/internal/Excluder.java index dd167b4838..726d8bea29 100644 --- a/gson/src/main/java/com/google/gson/internal/Excluder.java +++ b/gson/src/main/java/com/google/gson/internal/Excluder.java @@ -48,9 +48,8 @@ * @author Jesse Wilson */ public final class Excluder implements TypeAdapterFactory, Cloneable { - private static final double IGNORE_VERSIONS = -1.0d; public static final Excluder DEFAULT = new Excluder(); - + private static final double IGNORE_VERSIONS = -1.0d; private double version = IGNORE_VERSIONS; private int modifiers = Modifier.TRANSIENT | Modifier.STATIC; private boolean serializeInnerClasses = true; diff --git a/gson/src/main/java/com/google/gson/internal/JavaVersion.java b/gson/src/main/java/com/google/gson/internal/JavaVersion.java index fab1b7c274..12f271cacc 100644 --- a/gson/src/main/java/com/google/gson/internal/JavaVersion.java +++ b/gson/src/main/java/com/google/gson/internal/JavaVersion.java @@ -25,6 +25,8 @@ public final class JavaVersion { private static final int majorJavaVersion = determineMajorJavaVersion(); + private JavaVersion() { } + private static int determineMajorJavaVersion() { String javaVersion = System.getProperty("java.version"); return getMajorJavaVersion(javaVersion); @@ -42,7 +44,7 @@ static int getMajorJavaVersion(String javaVersion) { return version; } - // Parses both legacy 1.8 style and newer 9.0.4 style + // Parses both legacy 1.8 style and newer 9.0.4 style private static int parseDotted(String javaVersion) { try { String[] parts = javaVersion.split("[._]"); @@ -87,6 +89,4 @@ public static int getMajorJavaVersion() { public static boolean isJava9OrLater() { return majorJavaVersion >= 9; } - - private JavaVersion() { } } diff --git a/gson/src/main/java/com/google/gson/internal/LinkedTreeMap.java b/gson/src/main/java/com/google/gson/internal/LinkedTreeMap.java index 1fe512ad3e..9a29b9b9a1 100644 --- a/gson/src/main/java/com/google/gson/internal/LinkedTreeMap.java +++ b/gson/src/main/java/com/google/gson/internal/LinkedTreeMap.java @@ -29,8 +29,8 @@ import java.util.Iterator; import java.util.LinkedHashMap; import java.util.NoSuchElementException; -import java.util.Set; import java.util.Objects; +import java.util.Set; /** * A map of comparable keys to values. Unlike {@code TreeMap}, this class uses @@ -47,15 +47,15 @@ public final class LinkedTreeMap extends AbstractMap implements Seri return a.compareTo(b); } }; - + // Used to preserve iteration order + final Node header; private final Comparator comparator; private final boolean allowNullValues; Node root; int size = 0; int modCount = 0; - - // Used to preserve iteration order - final Node header; + private EntrySet entrySet; + private KeySet keySet; /** * Create a natural order, empty tree map whose keys must be mutually @@ -446,9 +446,6 @@ private void rotateRight(Node root) { pivotLeft != null ? pivotLeft.height : 0) + 1; } - private EntrySet entrySet; - private KeySet keySet; - @Override public Set> entrySet() { EntrySet result = entrySet; return result != null ? result : (entrySet = new EntrySet()); @@ -459,14 +456,29 @@ private void rotateRight(Node root) { return result != null ? result : (keySet = new KeySet()); } + /** + * If somebody is unlucky enough to have to serialize one of these, serialize + * it as a LinkedHashMap so that they won't need Gson on the other side to + * deserialize it. Using serialization defeats our DoS defence, so most apps + * shouldn't use it. + */ + private Object writeReplace() throws ObjectStreamException { + return new LinkedHashMap<>(this); + } + + private void readObject(ObjectInputStream in) throws IOException { + // Don't permit directly deserializing this class; writeReplace() should have written a replacement + throw new InvalidObjectException("Deserialization is unsupported"); + } + static final class Node implements Entry { + final K key; + final boolean allowNullValue; Node parent; Node left; Node right; Node next; Node prev; - final K key; - final boolean allowNullValue; V value; int height; @@ -645,19 +657,4 @@ final class KeySet extends AbstractSet { LinkedTreeMap.this.clear(); } } - - /** - * If somebody is unlucky enough to have to serialize one of these, serialize - * it as a LinkedHashMap so that they won't need Gson on the other side to - * deserialize it. Using serialization defeats our DoS defence, so most apps - * shouldn't use it. - */ - private Object writeReplace() throws ObjectStreamException { - return new LinkedHashMap<>(this); - } - - private void readObject(ObjectInputStream in) throws IOException { - // Don't permit directly deserializing this class; writeReplace() should have written a replacement - throw new InvalidObjectException("Deserialization is unsupported"); - } } diff --git a/gson/src/main/java/com/google/gson/internal/ReflectionAccessFilterHelper.java b/gson/src/main/java/com/google/gson/internal/ReflectionAccessFilterHelper.java index a07b2c73d5..3929b76a5f 100644 --- a/gson/src/main/java/com/google/gson/internal/ReflectionAccessFilterHelper.java +++ b/gson/src/main/java/com/google/gson/internal/ReflectionAccessFilterHelper.java @@ -1,12 +1,11 @@ package com.google.gson.internal; +import com.google.gson.ReflectionAccessFilter; +import com.google.gson.ReflectionAccessFilter.FilterResult; import java.lang.reflect.AccessibleObject; import java.lang.reflect.Method; import java.util.List; -import com.google.gson.ReflectionAccessFilter; -import com.google.gson.ReflectionAccessFilter.FilterResult; - /** * Internal helper class for {@link ReflectionAccessFilter}. */ diff --git a/gson/src/main/java/com/google/gson/internal/UnsafeAllocator.java b/gson/src/main/java/com/google/gson/internal/UnsafeAllocator.java index fae6f80266..e531c28e6e 100644 --- a/gson/src/main/java/com/google/gson/internal/UnsafeAllocator.java +++ b/gson/src/main/java/com/google/gson/internal/UnsafeAllocator.java @@ -28,7 +28,7 @@ * @author Jesse Wilson */ public abstract class UnsafeAllocator { - public abstract T newInstance(Class c) throws Exception; + public static final UnsafeAllocator INSTANCE = create(); /** * Asserts that the class is instantiable. This check should have already occurred @@ -42,8 +42,6 @@ private static void assertInstantiable(Class c) { } } - public static final UnsafeAllocator INSTANCE = create(); - private static UnsafeAllocator create() { // try JVM // public class Unsafe { @@ -119,4 +117,6 @@ public T newInstance(Class c) { } }; } + + public abstract T newInstance(Class c) throws Exception; } diff --git a/gson/src/main/java/com/google/gson/internal/bind/DateTypeAdapter.java b/gson/src/main/java/com/google/gson/internal/bind/DateTypeAdapter.java index decd3c5c28..ba1ed371de 100644 --- a/gson/src/main/java/com/google/gson/internal/bind/DateTypeAdapter.java +++ b/gson/src/main/java/com/google/gson/internal/bind/DateTypeAdapter.java @@ -27,7 +27,6 @@ import com.google.gson.stream.JsonReader; import com.google.gson.stream.JsonToken; import com.google.gson.stream.JsonWriter; - import java.io.IOException; import java.text.DateFormat; import java.text.ParseException; diff --git a/gson/src/main/java/com/google/gson/internal/bind/DefaultDateTypeAdapter.java b/gson/src/main/java/com/google/gson/internal/bind/DefaultDateTypeAdapter.java index 9e578bf448..c6b9d4e6c8 100644 --- a/gson/src/main/java/com/google/gson/internal/bind/DefaultDateTypeAdapter.java +++ b/gson/src/main/java/com/google/gson/internal/bind/DefaultDateTypeAdapter.java @@ -46,45 +46,7 @@ */ public final class DefaultDateTypeAdapter extends TypeAdapter { private static final String SIMPLE_NAME = "DefaultDateTypeAdapter"; - - public static abstract class DateType { - public static final DateType DATE = new DateType(Date.class) { - @Override protected Date deserialize(Date date) { - return date; - } - }; - - private final Class dateClass; - - protected DateType(Class dateClass) { - this.dateClass = dateClass; - } - - protected abstract T deserialize(Date date); - - private TypeAdapterFactory createFactory(DefaultDateTypeAdapter adapter) { - return TypeAdapters.newFactory(dateClass, adapter); - } - - public final TypeAdapterFactory createAdapterFactory(String datePattern) { - return createFactory(new DefaultDateTypeAdapter<>(this, datePattern)); - } - - public final TypeAdapterFactory createAdapterFactory(int style) { - return createFactory(new DefaultDateTypeAdapter<>(this, style)); - } - - public final TypeAdapterFactory createAdapterFactory(int dateStyle, int timeStyle) { - return createFactory(new DefaultDateTypeAdapter<>(this, dateStyle, timeStyle)); - } - - public final TypeAdapterFactory createDefaultsAdapterFactory() { - return createFactory(new DefaultDateTypeAdapter<>(this, DateFormat.DEFAULT, DateFormat.DEFAULT)); - } - } - private final DateType dateType; - /** * List of 1 or more different date formats used for de-serialization attempts. * The first of them is used for serialization as well. @@ -174,4 +136,40 @@ public String toString() { return SIMPLE_NAME + '(' + defaultFormat.getClass().getSimpleName() + ')'; } } + + public static abstract class DateType { + public static final DateType DATE = new DateType(Date.class) { + @Override protected Date deserialize(Date date) { + return date; + } + }; + + private final Class dateClass; + + protected DateType(Class dateClass) { + this.dateClass = dateClass; + } + + protected abstract T deserialize(Date date); + + private TypeAdapterFactory createFactory(DefaultDateTypeAdapter adapter) { + return TypeAdapters.newFactory(dateClass, adapter); + } + + public final TypeAdapterFactory createAdapterFactory(String datePattern) { + return createFactory(new DefaultDateTypeAdapter<>(this, datePattern)); + } + + public final TypeAdapterFactory createAdapterFactory(int style) { + return createFactory(new DefaultDateTypeAdapter<>(this, style)); + } + + public final TypeAdapterFactory createAdapterFactory(int dateStyle, int timeStyle) { + return createFactory(new DefaultDateTypeAdapter<>(this, dateStyle, timeStyle)); + } + + public final TypeAdapterFactory createDefaultsAdapterFactory() { + return createFactory(new DefaultDateTypeAdapter<>(this, DateFormat.DEFAULT, DateFormat.DEFAULT)); + } + } } diff --git a/gson/src/main/java/com/google/gson/internal/bind/MapTypeAdapterFactory.java b/gson/src/main/java/com/google/gson/internal/bind/MapTypeAdapterFactory.java index 68ecffb931..7dcae9e527 100644 --- a/gson/src/main/java/com/google/gson/internal/bind/MapTypeAdapterFactory.java +++ b/gson/src/main/java/com/google/gson/internal/bind/MapTypeAdapterFactory.java @@ -103,8 +103,8 @@ * is registered. */ public final class MapTypeAdapterFactory implements TypeAdapterFactory { - private final ConstructorConstructor constructorConstructor; final boolean complexMapKeySerialization; + private final ConstructorConstructor constructorConstructor; public MapTypeAdapterFactory(ConstructorConstructor constructorConstructor, boolean complexMapKeySerialization) { diff --git a/gson/src/main/java/com/google/gson/internal/bind/NumberTypeAdapter.java b/gson/src/main/java/com/google/gson/internal/bind/NumberTypeAdapter.java index 5aaeae3261..8f0a631328 100644 --- a/gson/src/main/java/com/google/gson/internal/bind/NumberTypeAdapter.java +++ b/gson/src/main/java/com/google/gson/internal/bind/NumberTypeAdapter.java @@ -18,15 +18,14 @@ import com.google.gson.Gson; import com.google.gson.JsonSyntaxException; -import com.google.gson.ToNumberStrategy; import com.google.gson.ToNumberPolicy; +import com.google.gson.ToNumberStrategy; import com.google.gson.TypeAdapter; import com.google.gson.TypeAdapterFactory; 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; /** 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 4db9bd72f1..aa1d49164b 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 @@ -76,6 +76,15 @@ public ReflectiveTypeAdapterFactory(ConstructorConstructor constructorConstructo this.reflectionFilters = reflectionFilters; } + private static void checkAccessible(Object object, M member) { + if (!ReflectionAccessFilterHelper.canAccess(member, Modifier.isStatic(member.getModifiers()) ? null : object)) { + String memberDescription = ReflectionHelper.getAccessibleObjectDescription(member, true); + throw new JsonIOException(memberDescription + " is not accessible and ReflectionAccessFilter does not" + + " permit making it accessible. Register a TypeAdapter for the declaring type, adjust the" + + " access filter or increase the visibility of the element and its declaring type."); + } + } + private boolean includeField(Field f, boolean serialize) { return !excluder.excludeClass(f.getType(), serialize) && !excluder.excludeField(f, serialize); } @@ -130,15 +139,6 @@ public TypeAdapter create(Gson gson, final TypeToken type) { return new FieldReflectionAdapter<>(constructor, getBoundFields(gson, type, raw, blockInaccessible, false)); } - private static void checkAccessible(Object object, M member) { - if (!ReflectionAccessFilterHelper.canAccess(member, Modifier.isStatic(member.getModifiers()) ? null : object)) { - String memberDescription = ReflectionHelper.getAccessibleObjectDescription(member, true); - throw new JsonIOException(memberDescription + " is not accessible and ReflectionAccessFilter does not" - + " permit making it accessible. Register a TypeAdapter for the declaring type, adjust the" - + " access filter or increase the visibility of the element and its declaring type."); - } - } - private BoundField createBoundField( final Gson context, final Field field, final Method accessor, final String name, final TypeToken fieldType, boolean serialize, boolean deserialize, diff --git a/gson/src/main/java/com/google/gson/internal/bind/TreeTypeAdapter.java b/gson/src/main/java/com/google/gson/internal/bind/TreeTypeAdapter.java index 2efd6c6bc1..180ca8b5b0 100644 --- a/gson/src/main/java/com/google/gson/internal/bind/TreeTypeAdapter.java +++ b/gson/src/main/java/com/google/gson/internal/bind/TreeTypeAdapter.java @@ -39,9 +39,9 @@ * has a facility to lookup a delegate type adapter on demand. */ public final class TreeTypeAdapter extends SerializationDelegatingTypeAdapter { + final Gson gson; private final JsonSerializer serializer; private final JsonDeserializer deserializer; - final Gson gson; private final TypeToken typeToken; private final TypeAdapterFactory skipPast; private final GsonContextImpl context = new GsonContextImpl(); @@ -65,6 +65,33 @@ public TreeTypeAdapter(JsonSerializer serializer, JsonDeserializer deseria this(serializer, deserializer, gson, typeToken, skipPast, true); } + /** + * Returns a new factory that will match each type against {@code exactType}. + */ + public static TypeAdapterFactory newFactory(TypeToken exactType, Object typeAdapter) { + return new SingleTypeFactory(typeAdapter, exactType, false, null); + } + + /** + * Returns a new factory that will match each type and its raw type against + * {@code exactType}. + */ + public static TypeAdapterFactory newFactoryWithMatchRawType( + TypeToken exactType, Object typeAdapter) { + // only bother matching raw types if exact type is a raw type + boolean matchRawType = exactType.getType() == exactType.getRawType(); + return new SingleTypeFactory(typeAdapter, exactType, matchRawType, null); + } + + /** + * Returns a new factory that will match each type's raw type for assignability + * to {@code hierarchyType}. + */ + public static TypeAdapterFactory newTypeHierarchyFactory( + Class hierarchyType, Object typeAdapter) { + return new SingleTypeFactory(typeAdapter, null, false, hierarchyType); + } + @Override public T read(JsonReader in) throws IOException { if (deserializer == null) { return delegate().read(in); @@ -106,33 +133,6 @@ private TypeAdapter delegate() { return serializer != null ? this : delegate(); } - /** - * Returns a new factory that will match each type against {@code exactType}. - */ - public static TypeAdapterFactory newFactory(TypeToken exactType, Object typeAdapter) { - return new SingleTypeFactory(typeAdapter, exactType, false, null); - } - - /** - * Returns a new factory that will match each type and its raw type against - * {@code exactType}. - */ - public static TypeAdapterFactory newFactoryWithMatchRawType( - TypeToken exactType, Object typeAdapter) { - // only bother matching raw types if exact type is a raw type - boolean matchRawType = exactType.getType() == exactType.getRawType(); - return new SingleTypeFactory(typeAdapter, exactType, matchRawType, null); - } - - /** - * Returns a new factory that will match each type's raw type for assignability - * to {@code hierarchyType}. - */ - public static TypeAdapterFactory newTypeHierarchyFactory( - Class hierarchyType, Object typeAdapter) { - return new SingleTypeFactory(typeAdapter, null, false, hierarchyType); - } - private static final class SingleTypeFactory implements TypeAdapterFactory { private final TypeToken exactType; private final boolean matchRawType; diff --git a/gson/src/main/java/com/google/gson/internal/bind/TypeAdapterRuntimeTypeWrapper.java b/gson/src/main/java/com/google/gson/internal/bind/TypeAdapterRuntimeTypeWrapper.java index 75a991ead7..849dc715ba 100644 --- a/gson/src/main/java/com/google/gson/internal/bind/TypeAdapterRuntimeTypeWrapper.java +++ b/gson/src/main/java/com/google/gson/internal/bind/TypeAdapterRuntimeTypeWrapper.java @@ -35,6 +35,35 @@ final class TypeAdapterRuntimeTypeWrapper extends TypeAdapter { this.type = type; } + /** + * Returns whether the type adapter uses reflection. + * + * @param typeAdapter the type adapter to check. + */ + private static boolean isReflective(TypeAdapter typeAdapter) { + // Run this in loop in case multiple delegating adapters are nested + while (typeAdapter instanceof SerializationDelegatingTypeAdapter) { + TypeAdapter delegate = ((SerializationDelegatingTypeAdapter) typeAdapter).getSerializationDelegate(); + // Break if adapter does not delegate serialization + if (delegate == typeAdapter) { + break; + } + typeAdapter = delegate; + } + + return typeAdapter instanceof ReflectiveTypeAdapterFactory.Adapter; + } + + /** + * Finds a compatible runtime type if it is more specific + */ + private static Type getRuntimeTypeIfMoreSpecific(Type type, Object value) { + if (value != null && (type instanceof Class || type instanceof TypeVariable)) { + type = value.getClass(); + } + return type; + } + @Override public T read(JsonReader in) throws IOException { return delegate.read(in); @@ -69,33 +98,4 @@ public void write(JsonWriter out, T value) throws IOException { } chosen.write(out, value); } - - /** - * Returns whether the type adapter uses reflection. - * - * @param typeAdapter the type adapter to check. - */ - private static boolean isReflective(TypeAdapter typeAdapter) { - // Run this in loop in case multiple delegating adapters are nested - while (typeAdapter instanceof SerializationDelegatingTypeAdapter) { - TypeAdapter delegate = ((SerializationDelegatingTypeAdapter) typeAdapter).getSerializationDelegate(); - // Break if adapter does not delegate serialization - if (delegate == typeAdapter) { - break; - } - typeAdapter = delegate; - } - - return typeAdapter instanceof ReflectiveTypeAdapterFactory.Adapter; - } - - /** - * Finds a compatible runtime type if it is more specific - */ - private static Type getRuntimeTypeIfMoreSpecific(Type type, Object value) { - if (value != null && (type instanceof Class || type instanceof TypeVariable)) { - type = value.getClass(); - } - return type; - } } diff --git a/gson/src/main/java/com/google/gson/internal/bind/TypeAdapters.java b/gson/src/main/java/com/google/gson/internal/bind/TypeAdapters.java index cb069ae7d4..95efc281ff 100644 --- a/gson/src/main/java/com/google/gson/internal/bind/TypeAdapters.java +++ b/gson/src/main/java/com/google/gson/internal/bind/TypeAdapters.java @@ -64,10 +64,6 @@ * Type adapters for basic types. */ public final class TypeAdapters { - private TypeAdapters() { - throw new UnsupportedOperationException(); - } - @SuppressWarnings("rawtypes") public static final TypeAdapter CLASS = new TypeAdapter() { @Override @@ -81,9 +77,7 @@ public Class read(JsonReader in) throws IOException { "Attempted to deserialize a java.lang.Class. Forgot to register a type adapter?"); } }.nullSafe(); - public static final TypeAdapterFactory CLASS_FACTORY = newFactory(Class.class, CLASS); - public static final TypeAdapter BIT_SET = new TypeAdapter() { @Override public BitSet read(JsonReader in) throws IOException { BitSet bitset = new BitSet(); @@ -129,9 +123,7 @@ public Class read(JsonReader in) throws IOException { out.endArray(); } }.nullSafe(); - public static final TypeAdapterFactory BIT_SET_FACTORY = newFactory(BitSet.class, BIT_SET); - public static final TypeAdapter BOOLEAN = new TypeAdapter() { @Override public Boolean read(JsonReader in) throws IOException { @@ -150,7 +142,6 @@ public void write(JsonWriter out, Boolean value) throws IOException { out.value(value); } }; - /** * Writes a boolean as a string. Useful for map keys, where booleans aren't * otherwise permitted. @@ -168,10 +159,8 @@ public void write(JsonWriter out, Boolean value) throws IOException { out.value(value == null ? "null" : value.toString()); } }; - public static final TypeAdapterFactory BOOLEAN_FACTORY = newFactory(boolean.class, Boolean.class, BOOLEAN); - public static final TypeAdapter BYTE = new TypeAdapter() { @Override public Number read(JsonReader in) throws IOException { @@ -201,10 +190,8 @@ public void write(JsonWriter out, Number value) throws IOException { } } }; - public static final TypeAdapterFactory BYTE_FACTORY = newFactory(byte.class, Byte.class, BYTE); - public static final TypeAdapter SHORT = new TypeAdapter() { @Override public Number read(JsonReader in) throws IOException { @@ -234,10 +221,8 @@ public void write(JsonWriter out, Number value) throws IOException { } } }; - public static final TypeAdapterFactory SHORT_FACTORY = newFactory(short.class, Short.class, SHORT); - public static final TypeAdapter INTEGER = new TypeAdapter() { @Override public Number read(JsonReader in) throws IOException { @@ -262,7 +247,6 @@ public void write(JsonWriter out, Number value) throws IOException { }; public static final TypeAdapterFactory INTEGER_FACTORY = newFactory(int.class, Integer.class, INTEGER); - public static final TypeAdapter ATOMIC_INTEGER = new TypeAdapter() { @Override public AtomicInteger read(JsonReader in) throws IOException { try { @@ -277,7 +261,6 @@ public void write(JsonWriter out, Number value) throws IOException { }.nullSafe(); public static final TypeAdapterFactory ATOMIC_INTEGER_FACTORY = newFactory(AtomicInteger.class, TypeAdapters.ATOMIC_INTEGER); - public static final TypeAdapter ATOMIC_BOOLEAN = new TypeAdapter() { @Override public AtomicBoolean read(JsonReader in) throws IOException { return new AtomicBoolean(in.nextBoolean()); @@ -288,7 +271,6 @@ public void write(JsonWriter out, Number value) throws IOException { }.nullSafe(); public static final TypeAdapterFactory ATOMIC_BOOLEAN_FACTORY = newFactory(AtomicBoolean.class, TypeAdapters.ATOMIC_BOOLEAN); - public static final TypeAdapter ATOMIC_INTEGER_ARRAY = new TypeAdapter() { @Override public AtomicIntegerArray read(JsonReader in) throws IOException { List list = new ArrayList<>(); @@ -319,7 +301,6 @@ public void write(JsonWriter out, Number value) throws IOException { }.nullSafe(); public static final TypeAdapterFactory ATOMIC_INTEGER_ARRAY_FACTORY = newFactory(AtomicIntegerArray.class, TypeAdapters.ATOMIC_INTEGER_ARRAY); - public static final TypeAdapter LONG = new TypeAdapter() { @Override public Number read(JsonReader in) throws IOException { @@ -342,7 +323,6 @@ public void write(JsonWriter out, Number value) throws IOException { } } }; - public static final TypeAdapter FLOAT = new TypeAdapter() { @Override public Number read(JsonReader in) throws IOException { @@ -364,7 +344,6 @@ public void write(JsonWriter out, Number value) throws IOException { } } }; - public static final TypeAdapter DOUBLE = new TypeAdapter() { @Override public Number read(JsonReader in) throws IOException { @@ -383,7 +362,6 @@ public void write(JsonWriter out, Number value) throws IOException { } } }; - public static final TypeAdapter CHARACTER = new TypeAdapter() { @Override public Character read(JsonReader in) throws IOException { @@ -402,10 +380,8 @@ public void write(JsonWriter out, Character value) throws IOException { out.value(value == null ? null : String.valueOf(value)); } }; - public static final TypeAdapterFactory CHARACTER_FACTORY = newFactory(char.class, Character.class, CHARACTER); - public static final TypeAdapter STRING = new TypeAdapter() { @Override public String read(JsonReader in) throws IOException { @@ -425,7 +401,6 @@ public void write(JsonWriter out, String value) throws IOException { out.value(value); } }; - public static final TypeAdapter BIG_DECIMAL = new TypeAdapter() { @Override public BigDecimal read(JsonReader in) throws IOException { if (in.peek() == JsonToken.NULL) { @@ -444,7 +419,6 @@ public void write(JsonWriter out, String value) throws IOException { out.value(value); } }; - public static final TypeAdapter BIG_INTEGER = new TypeAdapter() { @Override public BigInteger read(JsonReader in) throws IOException { if (in.peek() == JsonToken.NULL) { @@ -463,7 +437,6 @@ public void write(JsonWriter out, String value) throws IOException { out.value(value); } }; - public static final TypeAdapter LAZILY_PARSED_NUMBER = new TypeAdapter() { // Normally users should not be able to access and deserialize LazilyParsedNumber because // it is an internal type, but implement this nonetheless in case there are legit corner @@ -480,9 +453,7 @@ public void write(JsonWriter out, String value) throws IOException { out.value(value); } }; - public static final TypeAdapterFactory STRING_FACTORY = newFactory(String.class, STRING); - public static final TypeAdapter STRING_BUILDER = new TypeAdapter() { @Override public StringBuilder read(JsonReader in) throws IOException { @@ -497,10 +468,8 @@ public void write(JsonWriter out, StringBuilder value) throws IOException { out.value(value == null ? null : value.toString()); } }; - public static final TypeAdapterFactory STRING_BUILDER_FACTORY = newFactory(StringBuilder.class, STRING_BUILDER); - public static final TypeAdapter STRING_BUFFER = new TypeAdapter() { @Override public StringBuffer read(JsonReader in) throws IOException { @@ -515,10 +484,8 @@ public void write(JsonWriter out, StringBuffer value) throws IOException { out.value(value == null ? null : value.toString()); } }; - public static final TypeAdapterFactory STRING_BUFFER_FACTORY = newFactory(StringBuffer.class, STRING_BUFFER); - public static final TypeAdapter URL = new TypeAdapter() { @Override public URL read(JsonReader in) throws IOException { @@ -534,9 +501,7 @@ public void write(JsonWriter out, URL value) throws IOException { out.value(value == null ? null : value.toExternalForm()); } }; - public static final TypeAdapterFactory URL_FACTORY = newFactory(URL.class, URL); - public static final TypeAdapter URI = new TypeAdapter() { @Override public URI read(JsonReader in) throws IOException { @@ -556,9 +521,7 @@ public void write(JsonWriter out, URI value) throws IOException { out.value(value == null ? null : value.toASCIIString()); } }; - public static final TypeAdapterFactory URI_FACTORY = newFactory(URI.class, URI); - public static final TypeAdapter INET_ADDRESS = new TypeAdapter() { @Override public InetAddress read(JsonReader in) throws IOException { @@ -574,10 +537,8 @@ public void write(JsonWriter out, InetAddress value) throws IOException { out.value(value == null ? null : value.getHostAddress()); } }; - public static final TypeAdapterFactory INET_ADDRESS_FACTORY = newTypeHierarchyFactory(InetAddress.class, INET_ADDRESS); - public static final TypeAdapter UUID = new TypeAdapter() { @Override public UUID read(JsonReader in) throws IOException { @@ -597,9 +558,7 @@ public void write(JsonWriter out, UUID value) throws IOException { out.value(value == null ? null : value.toString()); } }; - public static final TypeAdapterFactory UUID_FACTORY = newFactory(UUID.class, UUID); - public static final TypeAdapter CURRENCY = new TypeAdapter() { @Override public Currency read(JsonReader in) throws IOException { @@ -616,7 +575,6 @@ public void write(JsonWriter out, Currency value) throws IOException { } }.nullSafe(); public static final TypeAdapterFactory CURRENCY_FACTORY = newFactory(Currency.class, CURRENCY); - public static final TypeAdapter CALENDAR = new TypeAdapter() { private static final String YEAR = "year"; private static final String MONTH = "month"; @@ -681,10 +639,8 @@ public void write(JsonWriter out, Calendar value) throws IOException { out.endObject(); } }; - public static final TypeAdapterFactory CALENDAR_FACTORY = newFactoryForMultipleTypes(Calendar.class, GregorianCalendar.class, CALENDAR); - public static final TypeAdapter LOCALE = new TypeAdapter() { @Override public Locale read(JsonReader in) throws IOException { @@ -719,9 +675,7 @@ public void write(JsonWriter out, Locale value) throws IOException { out.value(value == null ? null : value.toString()); } }; - public static final TypeAdapterFactory LOCALE_FACTORY = newFactory(Locale.class, LOCALE); - public static final TypeAdapter JSON_ELEMENT = new TypeAdapter() { /** * Tries to begin reading a JSON array or JSON object, returning {@code null} if @@ -852,71 +806,8 @@ private JsonElement readTerminal(JsonReader in, JsonToken peeked) throws IOExcep } } }; - public static final TypeAdapterFactory JSON_ELEMENT_FACTORY = newTypeHierarchyFactory(JsonElement.class, JSON_ELEMENT); - - private static final class EnumTypeAdapter> extends TypeAdapter { - private final Map nameToConstant = new HashMap<>(); - private final Map stringToConstant = new HashMap<>(); - private final Map constantToName = new HashMap<>(); - - public EnumTypeAdapter(final Class classOfT) { - try { - // Uses reflection to find enum constants to work around name mismatches for obfuscated classes - // Reflection access might throw SecurityException, therefore run this in privileged context; - // should be acceptable because this only retrieves enum constants, but does not expose anything else - Field[] constantFields = AccessController.doPrivileged(new PrivilegedAction() { - @Override public Field[] run() { - Field[] fields = classOfT.getDeclaredFields(); - ArrayList constantFieldsList = new ArrayList<>(fields.length); - for (Field f : fields) { - if (f.isEnumConstant()) { - constantFieldsList.add(f); - } - } - - Field[] constantFields = constantFieldsList.toArray(new Field[0]); - AccessibleObject.setAccessible(constantFields, true); - return constantFields; - } - }); - for (Field constantField : constantFields) { - @SuppressWarnings("unchecked") - T constant = (T)(constantField.get(null)); - String name = constant.name(); - String toStringVal = constant.toString(); - - SerializedName annotation = constantField.getAnnotation(SerializedName.class); - if (annotation != null) { - name = annotation.value(); - for (String alternate : annotation.alternate()) { - nameToConstant.put(alternate, constant); - } - } - nameToConstant.put(name, constant); - stringToConstant.put(toStringVal, constant); - constantToName.put(constant, name); - } - } catch (IllegalAccessException e) { - throw new AssertionError(e); - } - } - @Override public T read(JsonReader in) throws IOException { - if (in.peek() == JsonToken.NULL) { - in.nextNull(); - return null; - } - String key = in.nextString(); - T constant = nameToConstant.get(key); - return (constant == null) ? stringToConstant.get(key) : constant; - } - - @Override public void write(JsonWriter out, T value) throws IOException { - out.value(value == null ? null : constantToName.get(value)); - } - } - public static final TypeAdapterFactory ENUM_FACTORY = new TypeAdapterFactory() { @Override public TypeAdapter create(Gson gson, TypeToken typeToken) { Class rawType = typeToken.getRawType(); @@ -932,6 +823,10 @@ public EnumTypeAdapter(final Class classOfT) { } }; + private TypeAdapters() { + throw new UnsupportedOperationException(); + } + public static TypeAdapterFactory newFactory( final TypeToken type, final TypeAdapter typeAdapter) { return new TypeAdapterFactory() { @@ -1018,4 +913,65 @@ public static TypeAdapterFactory newTypeHierarchyFactory( } }; } + + private static final class EnumTypeAdapter> extends TypeAdapter { + private final Map nameToConstant = new HashMap<>(); + private final Map stringToConstant = new HashMap<>(); + private final Map constantToName = new HashMap<>(); + + public EnumTypeAdapter(final Class classOfT) { + try { + // Uses reflection to find enum constants to work around name mismatches for obfuscated classes + // Reflection access might throw SecurityException, therefore run this in privileged context; + // should be acceptable because this only retrieves enum constants, but does not expose anything else + Field[] constantFields = AccessController.doPrivileged(new PrivilegedAction() { + @Override public Field[] run() { + Field[] fields = classOfT.getDeclaredFields(); + ArrayList constantFieldsList = new ArrayList<>(fields.length); + for (Field f : fields) { + if (f.isEnumConstant()) { + constantFieldsList.add(f); + } + } + + Field[] constantFields = constantFieldsList.toArray(new Field[0]); + AccessibleObject.setAccessible(constantFields, true); + return constantFields; + } + }); + for (Field constantField : constantFields) { + @SuppressWarnings("unchecked") + T constant = (T)(constantField.get(null)); + String name = constant.name(); + String toStringVal = constant.toString(); + + SerializedName annotation = constantField.getAnnotation(SerializedName.class); + if (annotation != null) { + name = annotation.value(); + for (String alternate : annotation.alternate()) { + nameToConstant.put(alternate, constant); + } + } + nameToConstant.put(name, constant); + stringToConstant.put(toStringVal, constant); + constantToName.put(constant, name); + } + } catch (IllegalAccessException e) { + throw new AssertionError(e); + } + } + @Override public T read(JsonReader in) throws IOException { + if (in.peek() == JsonToken.NULL) { + in.nextNull(); + return null; + } + String key = in.nextString(); + T constant = nameToConstant.get(key); + return (constant == null) ? stringToConstant.get(key) : constant; + } + + @Override public void write(JsonWriter out, T value) throws IOException { + out.value(value == null ? null : constantToName.get(value)); + } + } } diff --git a/gson/src/main/java/com/google/gson/internal/sql/SqlTimestampTypeAdapter.java b/gson/src/main/java/com/google/gson/internal/sql/SqlTimestampTypeAdapter.java index 06688c5776..9316b9ccdc 100644 --- a/gson/src/main/java/com/google/gson/internal/sql/SqlTimestampTypeAdapter.java +++ b/gson/src/main/java/com/google/gson/internal/sql/SqlTimestampTypeAdapter.java @@ -6,7 +6,6 @@ import com.google.gson.reflect.TypeToken; import com.google.gson.stream.JsonReader; import com.google.gson.stream.JsonWriter; - import java.io.IOException; import java.sql.Timestamp; import java.util.Date; diff --git a/gson/src/main/java/com/google/gson/internal/sql/SqlTypesSupport.java b/gson/src/main/java/com/google/gson/internal/sql/SqlTypesSupport.java index 92bdfd3f6c..13e05bd4f1 100644 --- a/gson/src/main/java/com/google/gson/internal/sql/SqlTypesSupport.java +++ b/gson/src/main/java/com/google/gson/internal/sql/SqlTypesSupport.java @@ -1,10 +1,9 @@ package com.google.gson.internal.sql; -import java.sql.Timestamp; -import java.util.Date; - import com.google.gson.TypeAdapterFactory; import com.google.gson.internal.bind.DefaultDateTypeAdapter.DateType; +import java.sql.Timestamp; +import java.util.Date; /** * Encapsulates access to {@code java.sql} types, to allow Gson to diff --git a/gson/src/main/java/com/google/gson/reflect/TypeToken.java b/gson/src/main/java/com/google/gson/reflect/TypeToken.java index 39e81f33f9..9d81ffa845 100644 --- a/gson/src/main/java/com/google/gson/reflect/TypeToken.java +++ b/gson/src/main/java/com/google/gson/reflect/TypeToken.java @@ -82,95 +82,6 @@ private TypeToken(Type type) { this.hashCode = this.type.hashCode(); } - /** - * Verifies that {@code this} is an instance of a direct subclass of TypeToken and - * returns the type argument for {@code T} in {@link $Gson$Types#canonicalize - * canonical form}. - */ - private Type getTypeTokenTypeArgument() { - Type superclass = getClass().getGenericSuperclass(); - if (superclass instanceof ParameterizedType) { - ParameterizedType parameterized = (ParameterizedType) superclass; - if (parameterized.getRawType() == TypeToken.class) { - return $Gson$Types.canonicalize(parameterized.getActualTypeArguments()[0]); - } - } - // Check for raw TypeToken as superclass - else if (superclass == TypeToken.class) { - throw new IllegalStateException("TypeToken must be created with a type argument: new TypeToken<...>() {}; " - + "When using code shrinkers (ProGuard, R8, ...) make sure that generic signatures are preserved."); - } - - // User created subclass of subclass of TypeToken - throw new IllegalStateException("Must only create direct subclasses of TypeToken"); - } - - /** - * Returns the raw (non-generic) type for this type. - */ - public final Class getRawType() { - return rawType; - } - - /** - * Gets underlying {@code Type} instance. - */ - public final Type getType() { - return type; - } - - /** - * Check if this type is assignable from the given class object. - * - * @deprecated this implementation may be inconsistent with javac for types - * with wildcards. - */ - @Deprecated - public boolean isAssignableFrom(Class cls) { - return isAssignableFrom((Type) cls); - } - - /** - * Check if this type is assignable from the given Type. - * - * @deprecated this implementation may be inconsistent with javac for types - * with wildcards. - */ - @Deprecated - public boolean isAssignableFrom(Type from) { - if (from == null) { - return false; - } - - if (type.equals(from)) { - return true; - } - - if (type instanceof Class) { - return rawType.isAssignableFrom($Gson$Types.getRawType(from)); - } else if (type instanceof ParameterizedType) { - return isAssignableFrom(from, (ParameterizedType) type, - new HashMap()); - } else if (type instanceof GenericArrayType) { - return rawType.isAssignableFrom($Gson$Types.getRawType(from)) - && isAssignableFrom(from, (GenericArrayType) type); - } else { - throw buildUnexpectedTypeError( - type, Class.class, ParameterizedType.class, GenericArrayType.class); - } - } - - /** - * Check if this type is assignable from the given type token. - * - * @deprecated this implementation may be inconsistent with javac for types - * with wildcards. - */ - @Deprecated - public boolean isAssignableFrom(TypeToken token) { - return isAssignableFrom(token.getType()); - } - /** * Private helper function that performs some assignability checks for * the provided GenericArrayType. @@ -294,19 +205,6 @@ private static boolean matches(Type from, Type to, Map typeMap) { } - @Override public final int hashCode() { - return this.hashCode; - } - - @Override public final boolean equals(Object o) { - return o instanceof TypeToken - && $Gson$Types.equals(type, ((TypeToken) o).type); - } - - @Override public final String toString() { - return $Gson$Types.typeToString(type); - } - /** * Gets type literal for the given {@code Type} instance. */ @@ -382,4 +280,106 @@ public static TypeToken getParameterized(Type rawType, Type... typeArguments) public static TypeToken getArray(Type componentType) { return new TypeToken<>($Gson$Types.arrayOf(componentType)); } + + /** + * Verifies that {@code this} is an instance of a direct subclass of TypeToken and + * returns the type argument for {@code T} in {@link $Gson$Types#canonicalize + * canonical form}. + */ + private Type getTypeTokenTypeArgument() { + Type superclass = getClass().getGenericSuperclass(); + if (superclass instanceof ParameterizedType) { + ParameterizedType parameterized = (ParameterizedType) superclass; + if (parameterized.getRawType() == TypeToken.class) { + return $Gson$Types.canonicalize(parameterized.getActualTypeArguments()[0]); + } + } + // Check for raw TypeToken as superclass + else if (superclass == TypeToken.class) { + throw new IllegalStateException("TypeToken must be created with a type argument: new TypeToken<...>() {}; " + + "When using code shrinkers (ProGuard, R8, ...) make sure that generic signatures are preserved."); + } + + // User created subclass of subclass of TypeToken + throw new IllegalStateException("Must only create direct subclasses of TypeToken"); + } + + /** + * Returns the raw (non-generic) type for this type. + */ + public final Class getRawType() { + return rawType; + } + + /** + * Gets underlying {@code Type} instance. + */ + public final Type getType() { + return type; + } + + /** + * Check if this type is assignable from the given class object. + * + * @deprecated this implementation may be inconsistent with javac for types + * with wildcards. + */ + @Deprecated + public boolean isAssignableFrom(Class cls) { + return isAssignableFrom((Type) cls); + } + + /** + * Check if this type is assignable from the given Type. + * + * @deprecated this implementation may be inconsistent with javac for types + * with wildcards. + */ + @Deprecated + public boolean isAssignableFrom(Type from) { + if (from == null) { + return false; + } + + if (type.equals(from)) { + return true; + } + + if (type instanceof Class) { + return rawType.isAssignableFrom($Gson$Types.getRawType(from)); + } else if (type instanceof ParameterizedType) { + return isAssignableFrom(from, (ParameterizedType) type, + new HashMap()); + } else if (type instanceof GenericArrayType) { + return rawType.isAssignableFrom($Gson$Types.getRawType(from)) + && isAssignableFrom(from, (GenericArrayType) type); + } else { + throw buildUnexpectedTypeError( + type, Class.class, ParameterizedType.class, GenericArrayType.class); + } + } + + /** + * Check if this type is assignable from the given type token. + * + * @deprecated this implementation may be inconsistent with javac for types + * with wildcards. + */ + @Deprecated + public boolean isAssignableFrom(TypeToken token) { + return isAssignableFrom(token.getType()); + } + + @Override public final int hashCode() { + return this.hashCode; + } + + @Override public final boolean equals(Object o) { + return o instanceof TypeToken + && $Gson$Types.equals(type, ((TypeToken) o).type); + } + + @Override public final String toString() { + return $Gson$Types.typeToString(type); + } } diff --git a/gson/src/main/java/com/google/gson/stream/JsonReader.java b/gson/src/main/java/com/google/gson/stream/JsonReader.java index 718a7c2a8c..0343b5a757 100644 --- a/gson/src/main/java/com/google/gson/stream/JsonReader.java +++ b/gson/src/main/java/com/google/gson/stream/JsonReader.java @@ -190,8 +190,8 @@ * @since 1.6 */ public class JsonReader implements Closeable { + static final int BUFFER_SIZE = 1024; private static final long MIN_INCOMPLETE_INTEGER = Long.MIN_VALUE / 10; - private static final int PEEKED_NONE = 0; private static final int PEEKED_BEGIN_OBJECT = 1; private static final int PEEKED_END_OBJECT = 2; @@ -212,7 +212,6 @@ public class JsonReader implements Closeable { private static final int PEEKED_LONG = 15; private static final int PEEKED_NUMBER = 16; private static final int PEEKED_EOF = 17; - /* State machine when parsing numbers */ private static final int NUMBER_CHAR_NONE = 0; private static final int NUMBER_CHAR_SIGN = 1; @@ -223,13 +222,33 @@ public class JsonReader implements Closeable { private static final int NUMBER_CHAR_EXP_SIGN = 6; private static final int NUMBER_CHAR_EXP_DIGIT = 7; + static { + JsonReaderInternalAccess.INSTANCE = new JsonReaderInternalAccess() { + @Override public void promoteNameToValue(JsonReader reader) throws IOException { + if (reader instanceof JsonTreeReader) { + ((JsonTreeReader)reader).promoteNameToValue(); + return; + } + int p = reader.peeked; + if (p == PEEKED_NONE) { + p = reader.doPeek(); + } + if (p == PEEKED_DOUBLE_QUOTED_NAME) { + reader.peeked = PEEKED_DOUBLE_QUOTED; + } else if (p == PEEKED_SINGLE_QUOTED_NAME) { + reader.peeked = PEEKED_SINGLE_QUOTED; + } else if (p == PEEKED_UNQUOTED_NAME) { + reader.peeked = PEEKED_UNQUOTED; + } else { + throw new IllegalStateException( + "Expected a name but was " + reader.peek() + reader.locationString()); + } + } + }; + } + /** The input JSON. */ private final Reader in; - - /** True to accept non-spec compliant JSON */ - private boolean lenient = false; - - static final int BUFFER_SIZE = 1024; /** * Use a manual buffer to easily read and unread upcoming characters, and * also so we can create strings without an intermediate StringBuilder. @@ -237,42 +256,34 @@ public class JsonReader implements Closeable { * long as the longest token that can be reported as a number. */ private final char[] buffer = new char[BUFFER_SIZE]; + int peeked = PEEKED_NONE; + /** True to accept non-spec compliant JSON */ + private boolean lenient = false; private int pos = 0; private int limit = 0; - private int lineNumber = 0; private int lineStart = 0; - - int peeked = PEEKED_NONE; - /** * A peeked value that was composed entirely of digits with an optional * leading dash. Positive values may not have a leading 0. */ private long peekedLong; - /** * The number of characters in a peeked number literal. Increment 'pos' by * this after reading a number. */ private int peekedNumberLength; - /** * A peeked string that should be parsed on the next double, long or string. * This is populated before a numeric value is parsed and used if that parsing * fails. */ private String peekedString; - /* * The nesting stack. Using a manual array rather than an ArrayList saves 20%. */ private int[] stack = new int[32]; private int stackSize = 0; - { - stack[stackSize++] = JsonScope.EMPTY_DOCUMENT; - } - /* * The path members. It corresponds directly to stack: At indices where the * stack contains an object (EMPTY_OBJECT, DANGLING_NAME or NONEMPTY_OBJECT), @@ -284,6 +295,10 @@ public class JsonReader implements Closeable { private String[] pathNames = new String[32]; private int[] pathIndices = new int[32]; + { + stack[stackSize++] = JsonScope.EMPTY_DOCUMENT; + } + /** * Creates a new instance that reads a JSON-encoded stream from {@code in}. */ @@ -291,6 +306,13 @@ public JsonReader(Reader in) { this.in = Objects.requireNonNull(in, "in == null"); } + /** + * Returns true if this parser is liberal in what it accepts. + */ + public final boolean isLenient() { + return lenient; + } + /** * Configure this parser to be liberal in what it accepts. By default, * this parser is strict and only accepts JSON as specified by c) { + // Note: Don't consider LazilyParsedNumber trusted because it could contain + // an arbitrary malformed string + return c == Integer.class || c == Long.class || c == Double.class || c == Float.class || c == Byte.class || c == Short.class + || c == BigDecimal.class || c == BigInteger.class || c == AtomicInteger.class || c == AtomicLong.class; + } + /** * Sets the indentation string to be repeated for each level of indentation * in the encoded document. If {@code indent.isEmpty()} the encoded document @@ -225,6 +231,13 @@ public final void setIndent(String indent) { } } + /** + * Returns true if this writer has relaxed syntax rules. + */ + public boolean isLenient() { + return lenient; + } + /** * Configure this writer to relax its syntax rules. By default, this writer * only emits well-formed JSON as specified by c) { - // Note: Don't consider LazilyParsedNumber trusted because it could contain - // an arbitrary malformed string - return c == Integer.class || c == Long.class || c == Double.class || c == Float.class || c == Byte.class || c == Short.class - || c == BigDecimal.class || c == BigInteger.class || c == AtomicInteger.class || c == AtomicLong.class; - } - /** * Encodes {@code value}. The value is written by directly writing the {@link Number#toString()} * result to JSON. Implementations must make sure that the result represents a valid JSON number. diff --git a/gson/src/test/java/com/google/gson/DefaultInetAddressTypeAdapterTest.java b/gson/src/test/java/com/google/gson/DefaultInetAddressTypeAdapterTest.java index 6b853f5de8..f5f080f7c9 100644 --- a/gson/src/test/java/com/google/gson/DefaultInetAddressTypeAdapterTest.java +++ b/gson/src/test/java/com/google/gson/DefaultInetAddressTypeAdapterTest.java @@ -17,7 +17,6 @@ package com.google.gson; import java.net.InetAddress; - import junit.framework.TestCase; /** diff --git a/gson/src/test/java/com/google/gson/ExposeAnnotationExclusionStrategyTest.java b/gson/src/test/java/com/google/gson/ExposeAnnotationExclusionStrategyTest.java index dd8a7a926b..cf02f3253e 100644 --- a/gson/src/test/java/com/google/gson/ExposeAnnotationExclusionStrategyTest.java +++ b/gson/src/test/java/com/google/gson/ExposeAnnotationExclusionStrategyTest.java @@ -17,11 +17,9 @@ package com.google.gson; import com.google.gson.annotations.Expose; - import com.google.gson.internal.Excluder; -import junit.framework.TestCase; - import java.lang.reflect.Field; +import junit.framework.TestCase; /** * Unit tests for GsonBuilder.REQUIRE_EXPOSE_DESERIALIZE. @@ -31,6 +29,10 @@ public class ExposeAnnotationExclusionStrategyTest extends TestCase { private Excluder excluder = Excluder.DEFAULT.excludeFieldsWithoutExposeAnnotation(); + private static Field createFieldAttributes(String fieldName) throws Exception { + return MockObject.class.getField(fieldName); + } + public void testNeverSkipClasses() throws Exception { assertFalse(excluder.excludeClass(MockObject.class, true)); assertFalse(excluder.excludeClass(MockObject.class, false)); @@ -66,10 +68,6 @@ public void testDifferentSerializeAndDeserializeField() throws Exception { assertTrue(excluder.excludeField(f, false)); } - private static Field createFieldAttributes(String fieldName) throws Exception { - return MockObject.class.getField(fieldName); - } - @SuppressWarnings("unused") private static class MockObject { @Expose diff --git a/gson/src/test/java/com/google/gson/FieldNamingPolicyTest.java b/gson/src/test/java/com/google/gson/FieldNamingPolicyTest.java index 4d4c716b1e..139dec827e 100644 --- a/gson/src/test/java/com/google/gson/FieldNamingPolicyTest.java +++ b/gson/src/test/java/com/google/gson/FieldNamingPolicyTest.java @@ -2,10 +2,11 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotEquals; + +import com.google.gson.functional.FieldNamingTest; import java.lang.reflect.Field; import java.util.Locale; import org.junit.Test; -import com.google.gson.functional.FieldNamingTest; /** * Performs tests directly against {@link FieldNamingPolicy}; for integration tests diff --git a/gson/src/test/java/com/google/gson/GenericArrayTypeTest.java b/gson/src/test/java/com/google/gson/GenericArrayTypeTest.java index 42acb8a2d9..61c7843d48 100644 --- a/gson/src/test/java/com/google/gson/GenericArrayTypeTest.java +++ b/gson/src/test/java/com/google/gson/GenericArrayTypeTest.java @@ -18,12 +18,10 @@ import com.google.gson.internal.$Gson$Types; import com.google.gson.reflect.TypeToken; - -import junit.framework.TestCase; - import java.lang.reflect.GenericArrayType; import java.lang.reflect.Type; import java.util.List; +import junit.framework.TestCase; /** * Unit tests for the {@code GenericArrayType}s created by the {@link $Gson$Types} class. diff --git a/gson/src/test/java/com/google/gson/GsonBuilderTest.java b/gson/src/test/java/com/google/gson/GsonBuilderTest.java index e1a013b5de..db64ca5b21 100644 --- a/gson/src/test/java/com/google/gson/GsonBuilderTest.java +++ b/gson/src/test/java/com/google/gson/GsonBuilderTest.java @@ -44,6 +44,31 @@ public class GsonBuilderTest { } }; + private static void assertDefaultGson(Gson gson) { + // Should use default reflective adapter + String json1 = gson.toJson(new CustomClass1()); + assertEquals("{}", json1); + + // Should use default reflective adapter + String json2 = gson.toJson(new CustomClass2()); + assertEquals("{}", json2); + + // Should use default instance creator + CustomClass3 customClass3 = gson.fromJson("{}", CustomClass3.class); + assertEquals(CustomClass3.NO_ARG_CONSTRUCTOR_VALUE, customClass3.s); + } + + private static void assertCustomGson(Gson gson) { + String json1 = gson.toJson(new CustomClass1()); + assertEquals("\"custom-adapter\"", json1); + + String json2 = gson.toJson(new CustomClass2()); + assertEquals("\"custom-hierarchy-adapter\"", json2); + + CustomClass3 customClass3 = gson.fromJson("{}", CustomClass3.class); + assertEquals("custom-instance", customClass3.s); + } + @Test public void testCreatingMoreThanOnce() { GsonBuilder builder = new GsonBuilder(); @@ -102,47 +127,6 @@ public void testModificationAfterCreate() { assertCustomGson(gsonBuilder.create()); } - private static void assertDefaultGson(Gson gson) { - // Should use default reflective adapter - String json1 = gson.toJson(new CustomClass1()); - assertEquals("{}", json1); - - // Should use default reflective adapter - String json2 = gson.toJson(new CustomClass2()); - assertEquals("{}", json2); - - // Should use default instance creator - CustomClass3 customClass3 = gson.fromJson("{}", CustomClass3.class); - assertEquals(CustomClass3.NO_ARG_CONSTRUCTOR_VALUE, customClass3.s); - } - - private static void assertCustomGson(Gson gson) { - String json1 = gson.toJson(new CustomClass1()); - assertEquals("\"custom-adapter\"", json1); - - String json2 = gson.toJson(new CustomClass2()); - assertEquals("\"custom-hierarchy-adapter\"", json2); - - CustomClass3 customClass3 = gson.fromJson("{}", CustomClass3.class); - assertEquals("custom-instance", customClass3.s); - } - - static class CustomClass1 { } - static class CustomClass2 { } - static class CustomClass3 { - static final String NO_ARG_CONSTRUCTOR_VALUE = "default instance"; - - final String s; - - public CustomClass3(String s) { - this.s = s; - } - - public CustomClass3() { - this(NO_ARG_CONSTRUCTOR_VALUE); - } - } - @Test public void testExcludeFieldsWithModifiers() { Gson gson = new GsonBuilder() @@ -151,14 +135,6 @@ public void testExcludeFieldsWithModifiers() { assertEquals("{\"d\":\"d\"}", gson.toJson(new HasModifiers())); } - @SuppressWarnings("unused") - static class HasModifiers { - private String a = "a"; - volatile String b = "b"; - private volatile String c = "c"; - String d = "d"; - } - @Test public void testTransientFieldExclusion() { Gson gson = new GsonBuilder() @@ -167,10 +143,6 @@ public void testTransientFieldExclusion() { assertEquals("{\"a\":\"a\"}", gson.toJson(new HasTransients())); } - static class HasTransients { - transient String a = "a"; - } - @Test public void testRegisterTypeAdapterForCoreType() { Type[] types = { @@ -204,12 +176,6 @@ public void testDisableJdkUnsafe() { } } - private static class ClassWithoutNoArgsConstructor { - @SuppressWarnings("unused") - public ClassWithoutNoArgsConstructor(String s) { - } - } - @Test public void testSetVersionInvalid() { GsonBuilder builder = new GsonBuilder(); @@ -227,4 +193,40 @@ public void testSetVersionInvalid() { assertEquals("Invalid version: -0.1", e.getMessage()); } } + + static class CustomClass1 { } + + static class CustomClass2 { } + + static class CustomClass3 { + static final String NO_ARG_CONSTRUCTOR_VALUE = "default instance"; + + final String s; + + public CustomClass3(String s) { + this.s = s; + } + + public CustomClass3() { + this(NO_ARG_CONSTRUCTOR_VALUE); + } + } + + @SuppressWarnings("unused") + static class HasModifiers { + volatile String b = "b"; + String d = "d"; + private String a = "a"; + private volatile String c = "c"; + } + + static class HasTransients { + transient String a = "a"; + } + + private static class ClassWithoutNoArgsConstructor { + @SuppressWarnings("unused") + public ClassWithoutNoArgsConstructor(String s) { + } + } } diff --git a/gson/src/test/java/com/google/gson/GsonTest.java b/gson/src/test/java/com/google/gson/GsonTest.java index fd335e49bd..21aa82eb8f 100644 --- a/gson/src/test/java/com/google/gson/GsonTest.java +++ b/gson/src/test/java/com/google/gson/GsonTest.java @@ -60,6 +60,31 @@ public final class GsonTest { private static final ToNumberStrategy CUSTOM_OBJECT_TO_NUMBER_STRATEGY = ToNumberPolicy.DOUBLE; private static final ToNumberStrategy CUSTOM_NUMBER_TO_NUMBER_STRATEGY = ToNumberPolicy.LAZILY_PARSED_NUMBER; + private static void assertDefaultGson(Gson gson) { + // Should use default reflective adapter + String json1 = gson.toJson(new CustomClass1()); + assertEquals("{}", json1); + + // Should use default reflective adapter + String json2 = gson.toJson(new CustomClass2()); + assertEquals("{}", json2); + + // Should use default instance creator + CustomClass3 customClass3 = gson.fromJson("{}", CustomClass3.class); + assertEquals(CustomClass3.NO_ARG_CONSTRUCTOR_VALUE, customClass3.s); + } + + private static void assertCustomGson(Gson gson) { + String json1 = gson.toJson(new CustomClass1()); + assertEquals("\"custom-adapter\"", json1); + + String json2 = gson.toJson(new CustomClass2()); + assertEquals("\"custom-hierarchy-adapter\"", json2); + + CustomClass3 customClass3 = gson.fromJson("{}", CustomClass3.class); + assertEquals("custom-instance", customClass3.s); + } + @Test public void testOverridesDefaultExcluder() { Gson gson = new Gson(CUSTOM_EXCLUDER, CUSTOM_FIELD_NAMING_STRATEGY, @@ -93,13 +118,6 @@ public void testClonedTypeAdapterFactoryListsAreIndependent() { 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; } - } - @Test public void testGetAdapter_Null() { Gson gson = new Gson(); @@ -379,20 +397,6 @@ public void testDefaultGsonNewBuilderModification() { assertCustomGson(gsonBuilder.create()); } - private static void assertDefaultGson(Gson gson) { - // Should use default reflective adapter - String json1 = gson.toJson(new CustomClass1()); - assertEquals("{}", json1); - - // Should use default reflective adapter - String json2 = gson.toJson(new CustomClass2()); - assertEquals("{}", json2); - - // Should use default instance creator - CustomClass3 customClass3 = gson.fromJson("{}", CustomClass3.class); - assertEquals(CustomClass3.NO_ARG_CONSTRUCTOR_VALUE, customClass3.s); - } - /** * Modifying a GsonBuilder obtained from {@link Gson#newBuilder()} of a custom * Gson instance (created using a GsonBuilder) should not affect the Gson instance @@ -463,15 +467,11 @@ public void testNewBuilderModification() { assertEquals("overwritten custom-instance", customClass3.s); } - private static void assertCustomGson(Gson gson) { - String json1 = gson.toJson(new CustomClass1()); - assertEquals("\"custom-adapter\"", json1); - - String json2 = gson.toJson(new CustomClass2()); - assertEquals("\"custom-hierarchy-adapter\"", json2); - - CustomClass3 customClass3 = gson.fromJson("{}", CustomClass3.class); - assertEquals("custom-instance", customClass3.s); + 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; } } private static class CustomClass1 { } diff --git a/gson/src/test/java/com/google/gson/GsonTypeAdapterTest.java b/gson/src/test/java/com/google/gson/GsonTypeAdapterTest.java index d92994fa48..ec00b964f4 100644 --- a/gson/src/test/java/com/google/gson/GsonTypeAdapterTest.java +++ b/gson/src/test/java/com/google/gson/GsonTypeAdapterTest.java @@ -84,40 +84,6 @@ public void testTypeAdapterDoesNotAffectNonAdaptedTypes() throws Exception { assertEquals(expected, actual); } - private static class ExceptionTypeAdapter - implements JsonSerializer, JsonDeserializer { - @Override public JsonElement serialize( - AtomicLong src, Type typeOfSrc, JsonSerializationContext context) { - throw new IllegalStateException(); - } - @Override public AtomicLong deserialize( - JsonElement json, Type typeOfT, JsonDeserializationContext context) - throws JsonParseException { - throw new IllegalStateException(); - } - } - - private static class AtomicIntegerTypeAdapter - implements JsonSerializer, JsonDeserializer { - @Override public JsonElement serialize(AtomicInteger src, Type typeOfSrc, JsonSerializationContext context) { - return new JsonPrimitive(src.incrementAndGet()); - } - - @Override public AtomicInteger deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) - throws JsonParseException { - int intValue = json.getAsInt(); - return new AtomicInteger(--intValue); - } - } - - static abstract class Abstract { - String a; - } - - static class Concrete extends Abstract { - String b; - } - // https://groups.google.com/d/topic/google-gson/EBmOCa8kJPE/discussion public void testDeserializerForAbstractClass() { Concrete instance = new Concrete(); @@ -151,4 +117,38 @@ private void assertSerialized(String expected, Class instanceType, boolean re Gson gson = builder.create(); assertEquals(expected, gson.toJson(instance, instanceType)); } + + private static class ExceptionTypeAdapter + implements JsonSerializer, JsonDeserializer { + @Override public JsonElement serialize( + AtomicLong src, Type typeOfSrc, JsonSerializationContext context) { + throw new IllegalStateException(); + } + @Override public AtomicLong deserialize( + JsonElement json, Type typeOfT, JsonDeserializationContext context) + throws JsonParseException { + throw new IllegalStateException(); + } + } + + private static class AtomicIntegerTypeAdapter + implements JsonSerializer, JsonDeserializer { + @Override public JsonElement serialize(AtomicInteger src, Type typeOfSrc, JsonSerializationContext context) { + return new JsonPrimitive(src.incrementAndGet()); + } + + @Override public AtomicInteger deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) + throws JsonParseException { + int intValue = json.getAsInt(); + return new AtomicInteger(--intValue); + } + } + + static abstract class Abstract { + String a; + } + + static class Concrete extends Abstract { + String b; + } } diff --git a/gson/src/test/java/com/google/gson/InnerClassExclusionStrategyTest.java b/gson/src/test/java/com/google/gson/InnerClassExclusionStrategyTest.java index 86f7a62247..b0cf2e6e3f 100644 --- a/gson/src/test/java/com/google/gson/InnerClassExclusionStrategyTest.java +++ b/gson/src/test/java/com/google/gson/InnerClassExclusionStrategyTest.java @@ -50,9 +50,9 @@ public void testIncludeStaticNestedClassField() throws Exception { assertFalse(excluder.excludeField(f, true)); } - class InnerClass { + static class StaticNestedClass { } - static class StaticNestedClass { + class InnerClass { } } diff --git a/gson/src/test/java/com/google/gson/JsonParserParameterizedTest.java b/gson/src/test/java/com/google/gson/JsonParserParameterizedTest.java index 8671fd8369..eda7fe499b 100644 --- a/gson/src/test/java/com/google/gson/JsonParserParameterizedTest.java +++ b/gson/src/test/java/com/google/gson/JsonParserParameterizedTest.java @@ -12,6 +12,10 @@ @RunWith(Parameterized.class) public class JsonParserParameterizedTest { + private final TypeAdapter adapter = new Gson().getAdapter(JsonElement.class); + @Parameter + public String json; + @Parameters public static Iterable data() { return Arrays.asList( @@ -26,10 +30,6 @@ public static Iterable data() { ); } - private final TypeAdapter adapter = new Gson().getAdapter(JsonElement.class); - @Parameter - public String json; - @Test public void testParse() throws IOException { JsonElement deserialized = JsonParser.parseString(json); diff --git a/gson/src/test/java/com/google/gson/JsonParserTest.java b/gson/src/test/java/com/google/gson/JsonParserTest.java index a05aa32296..ff5c98b504 100644 --- a/gson/src/test/java/com/google/gson/JsonParserTest.java +++ b/gson/src/test/java/com/google/gson/JsonParserTest.java @@ -16,16 +16,15 @@ package com.google.gson; +import com.google.gson.common.TestTypes.BagOfPrimitives; +import com.google.gson.internal.Streams; +import com.google.gson.stream.JsonReader; import java.io.CharArrayReader; import java.io.CharArrayWriter; import java.io.IOException; import java.io.StringReader; import junit.framework.TestCase; -import com.google.gson.common.TestTypes.BagOfPrimitives; -import com.google.gson.internal.Streams; -import com.google.gson.stream.JsonReader; - /** * Unit test for {@link JsonParser} * @@ -33,6 +32,14 @@ */ public class JsonParserTest extends TestCase { + private static String repeat(String s, int times) { + StringBuilder stringBuilder = new StringBuilder(s.length() * times); + for (int i = 0; i < times; i++) { + stringBuilder.append(s); + } + return stringBuilder.toString(); + } + public void testParseInvalidJson() { try { JsonParser.parseString("[[]"); @@ -90,14 +97,6 @@ public void testParseMixedArray() { assertEquals("stringValue", array.get(2).getAsString()); } - private static String repeat(String s, int times) { - StringBuilder stringBuilder = new StringBuilder(s.length() * times); - for (int i = 0; i < times; i++) { - stringBuilder.append(s); - } - return stringBuilder.toString(); - } - /** Deeply nested JSON arrays should not cause {@link StackOverflowError} */ public void testParseDeeplyNestedArrays() throws IOException { int times = 10000; diff --git a/gson/src/test/java/com/google/gson/ObjectTypeAdapterParameterizedTest.java b/gson/src/test/java/com/google/gson/ObjectTypeAdapterParameterizedTest.java index 60740ce0f5..98b163a2df 100644 --- a/gson/src/test/java/com/google/gson/ObjectTypeAdapterParameterizedTest.java +++ b/gson/src/test/java/com/google/gson/ObjectTypeAdapterParameterizedTest.java @@ -12,6 +12,10 @@ @RunWith(Parameterized.class) public class ObjectTypeAdapterParameterizedTest { + private final TypeAdapter adapter = new Gson().getAdapter(Object.class); + @Parameter + public String json; + @Parameters public static Iterable data() { return Arrays.asList( @@ -26,10 +30,6 @@ public static Iterable data() { ); } - private final TypeAdapter adapter = new Gson().getAdapter(Object.class); - @Parameter - public String json; - @Test public void testReadWrite() throws IOException { Object deserialized = adapter.fromJson(json); diff --git a/gson/src/test/java/com/google/gson/ObjectTypeAdapterTest.java b/gson/src/test/java/com/google/gson/ObjectTypeAdapterTest.java index 534c398d50..49136d025e 100644 --- a/gson/src/test/java/com/google/gson/ObjectTypeAdapterTest.java +++ b/gson/src/test/java/com/google/gson/ObjectTypeAdapterTest.java @@ -28,6 +28,14 @@ public final class ObjectTypeAdapterTest extends TestCase { private final Gson gson = new GsonBuilder().create(); private final TypeAdapter adapter = gson.getAdapter(Object.class); + private static String repeat(String s, int times) { + StringBuilder stringBuilder = new StringBuilder(s.length() * times); + for (int i = 0; i < times; i++) { + stringBuilder.append(s); + } + return stringBuilder.toString(); + } + public void testDeserialize() throws Exception { Map map = (Map) adapter.fromJson("{\"a\":5,\"b\":[1,2,null],\"c\":{\"x\":\"y\"}}"); assertEquals(5.0, map.get("a")); @@ -57,14 +65,6 @@ public void testSerializeObject() throws Exception { assertEquals("{}", adapter.toJson(new Object())); } - private static String repeat(String s, int times) { - StringBuilder stringBuilder = new StringBuilder(s.length() * times); - for (int i = 0; i < times; i++) { - stringBuilder.append(s); - } - return stringBuilder.toString(); - } - /** Deeply nested JSON arrays should not cause {@link StackOverflowError} */ @SuppressWarnings("unchecked") public void testDeserializeDeeplyNestedArrays() throws IOException { diff --git a/gson/src/test/java/com/google/gson/ParameterizedTypeFixtures.java b/gson/src/test/java/com/google/gson/ParameterizedTypeFixtures.java index 3aca9fb717..6436e671bd 100644 --- a/gson/src/test/java/com/google/gson/ParameterizedTypeFixtures.java +++ b/gson/src/test/java/com/google/gson/ParameterizedTypeFixtures.java @@ -17,7 +17,6 @@ package com.google.gson; import com.google.gson.internal.$Gson$Types; - import com.google.gson.internal.Primitives; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; diff --git a/gson/src/test/java/com/google/gson/ParameterizedTypeTest.java b/gson/src/test/java/com/google/gson/ParameterizedTypeTest.java index 24d78e89d8..47dd4a6de0 100644 --- a/gson/src/test/java/com/google/gson/ParameterizedTypeTest.java +++ b/gson/src/test/java/com/google/gson/ParameterizedTypeTest.java @@ -18,12 +18,10 @@ import com.google.gson.internal.$Gson$Types; import com.google.gson.reflect.TypeToken; - -import junit.framework.TestCase; - import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.util.List; +import junit.framework.TestCase; /** * Unit tests for {@code ParameterizedType}s created by the {@link $Gson$Types} class. diff --git a/gson/src/test/java/com/google/gson/ToNumberPolicyTest.java b/gson/src/test/java/com/google/gson/ToNumberPolicyTest.java index db9898d47d..df083012fe 100644 --- a/gson/src/test/java/com/google/gson/ToNumberPolicyTest.java +++ b/gson/src/test/java/com/google/gson/ToNumberPolicyTest.java @@ -16,15 +16,25 @@ package com.google.gson; -import java.io.IOException; -import java.io.StringReader; -import java.math.BigDecimal; import com.google.gson.internal.LazilyParsedNumber; import com.google.gson.stream.JsonReader; import com.google.gson.stream.MalformedJsonException; +import java.io.IOException; +import java.io.StringReader; +import java.math.BigDecimal; import junit.framework.TestCase; public class ToNumberPolicyTest extends TestCase { + private static JsonReader fromString(String json) { + return new JsonReader(new StringReader(json)); + } + + private static JsonReader fromStringLenient(String json) { + JsonReader jsonReader = fromString(json); + jsonReader.setLenient(true); + return jsonReader; + } + public void testDouble() throws IOException { ToNumberStrategy strategy = ToNumberPolicy.DOUBLE; assertEquals(10.1, strategy.readNumber(fromString("10.1"))); @@ -126,14 +136,4 @@ public void testNullsAreNeverExpected() throws IOException { } catch (IllegalStateException expected) { } } - - private static JsonReader fromString(String json) { - return new JsonReader(new StringReader(json)); - } - - private static JsonReader fromStringLenient(String json) { - JsonReader jsonReader = fromString(json); - jsonReader.setLenient(true); - return jsonReader; - } } diff --git a/gson/src/test/java/com/google/gson/TypeAdapterTest.java b/gson/src/test/java/com/google/gson/TypeAdapterTest.java index 725ecdae93..15ecc7bb3a 100644 --- a/gson/src/test/java/com/google/gson/TypeAdapterTest.java +++ b/gson/src/test/java/com/google/gson/TypeAdapterTest.java @@ -11,6 +11,16 @@ import org.junit.Test; public class TypeAdapterTest { + private static final TypeAdapter adapter = new TypeAdapter() { + @Override public void write(JsonWriter out, String value) throws IOException { + out.value(value); + } + + @Override public String read(JsonReader in) throws IOException { + return in.nextString(); + } + }; + @Test public void testNullSafe() throws IOException { TypeAdapter adapter = new TypeAdapter() { @@ -59,16 +69,6 @@ public void testToJson_ThrowingIOException() { } } - private static final TypeAdapter adapter = new TypeAdapter() { - @Override public void write(JsonWriter out, String value) throws IOException { - out.value(value); - } - - @Override public String read(JsonReader in) throws IOException { - return in.nextString(); - } - }; - // Note: This test just verifies the current behavior; it is a bit questionable // whether that behavior is actually desired @Test diff --git a/gson/src/test/java/com/google/gson/common/TestTypes.java b/gson/src/test/java/com/google/gson/common/TestTypes.java index 13807637e8..8dc36b6873 100644 --- a/gson/src/test/java/com/google/gson/common/TestTypes.java +++ b/gson/src/test/java/com/google/gson/common/TestTypes.java @@ -271,9 +271,9 @@ public void appendFields(StringBuilder sb) { } public static class ClassWithTransientFields { - public transient T transientT; public final transient long transientLongValue; private final long[] longValue; + public transient T transientT; public ClassWithTransientFields() { this(0L); diff --git a/gson/src/test/java/com/google/gson/functional/CircularReferenceTest.java b/gson/src/test/java/com/google/gson/functional/CircularReferenceTest.java index f4419551b3..86b29d0a1c 100644 --- a/gson/src/test/java/com/google/gson/functional/CircularReferenceTest.java +++ b/gson/src/test/java/com/google/gson/functional/CircularReferenceTest.java @@ -15,12 +15,6 @@ */ package com.google.gson.functional; -import java.lang.reflect.Type; -import java.util.ArrayList; -import java.util.Collection; - -import junit.framework.TestCase; - import com.google.gson.Gson; import com.google.gson.GsonBuilder; import com.google.gson.JsonElement; @@ -28,6 +22,10 @@ import com.google.gson.JsonSerializationContext; import com.google.gson.JsonSerializer; import com.google.gson.common.TestTypes.ClassOverridingEquals; +import java.lang.reflect.Type; +import java.util.ArrayList; +import java.util.Collection; +import junit.framework.TestCase; /** * Functional tests related to circular reference detection and error reporting. diff --git a/gson/src/test/java/com/google/gson/functional/CollectionTest.java b/gson/src/test/java/com/google/gson/functional/CollectionTest.java index 44a655c8bc..c14bf39c06 100644 --- a/gson/src/test/java/com/google/gson/functional/CollectionTest.java +++ b/gson/src/test/java/com/google/gson/functional/CollectionTest.java @@ -51,6 +51,20 @@ public class CollectionTest extends TestCase { private Gson gson; + private static int[] toIntArray(Collection collection) { + int[] ints = new int[collection.size()]; + int i = 0; + for (Iterator iterator = collection.iterator(); iterator.hasNext(); ++i) { + Object obj = iterator.next(); + if (obj instanceof Integer) { + ints[i] = ((Integer)obj).intValue(); + } else if (obj instanceof Long) { + ints[i] = ((Long)obj).intValue(); + } + } + return ints; + } + @Override protected void setUp() throws Exception { super.setUp(); @@ -336,42 +350,6 @@ public void testUserCollectionTypeAdapter() { assertEquals("\"ab;cd\"", gson.toJson(Arrays.asList("ab", "cd"), listOfString)); } - static class HasArrayListField { - ArrayList longs = new ArrayList<>(); - } - - private static int[] toIntArray(Collection collection) { - int[] ints = new int[collection.size()]; - int i = 0; - for (Iterator iterator = collection.iterator(); iterator.hasNext(); ++i) { - Object obj = iterator.next(); - if (obj instanceof Integer) { - ints[i] = ((Integer)obj).intValue(); - } else if (obj instanceof Long) { - ints[i] = ((Long)obj).intValue(); - } - } - return ints; - } - - private static class ObjectWithWildcardCollection { - private final Collection collection; - - public ObjectWithWildcardCollection(Collection collection) { - this.collection = collection; - } - - public Collection getCollection() { - return collection; - } - } - - private static class Entry { - int value; - Entry(int value) { - this.value = value; - } - } public void testSetSerialization() { Set set = new HashSet<>(); set.add(new Entry(1)); @@ -380,6 +358,7 @@ public void testSetSerialization() { assertTrue(json.contains("1")); assertTrue(json.contains("2")); } + public void testSetDeserialization() { String json = "[{value:1},{value:2}]"; Type type = new TypeToken>() {}.getType(); @@ -390,10 +369,6 @@ public void testSetDeserialization() { } } - private class BigClass { private Map> inBig; } - - private class SmallClass { private String inSmall; } - public void testIssue1107() { String json = "{\n" + " \"inBig\": {\n" + @@ -408,4 +383,31 @@ public void testIssue1107() { assertEquals("hello", small.inSmall); } + static class HasArrayListField { + ArrayList longs = new ArrayList<>(); + } + + private static class ObjectWithWildcardCollection { + private final Collection collection; + + public ObjectWithWildcardCollection(Collection collection) { + this.collection = collection; + } + + public Collection getCollection() { + return collection; + } + } + + private static class Entry { + int value; + Entry(int value) { + this.value = value; + } + } + + private class BigClass { private Map> inBig; } + + private class SmallClass { private String inSmall; } + } diff --git a/gson/src/test/java/com/google/gson/functional/ConcurrencyTest.java b/gson/src/test/java/com/google/gson/functional/ConcurrencyTest.java index 318c6ac389..af685b5796 100644 --- a/gson/src/test/java/com/google/gson/functional/ConcurrencyTest.java +++ b/gson/src/test/java/com/google/gson/functional/ConcurrencyTest.java @@ -15,15 +15,13 @@ */ package com.google.gson.functional; +import com.google.gson.Gson; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.atomic.AtomicBoolean; - import junit.framework.TestCase; -import com.google.gson.Gson; - /** * Tests for ensuring Gson thread-safety. * diff --git a/gson/src/test/java/com/google/gson/functional/CustomDeserializerTest.java b/gson/src/test/java/com/google/gson/functional/CustomDeserializerTest.java index 8134ae2d19..c49522b566 100644 --- a/gson/src/test/java/com/google/gson/functional/CustomDeserializerTest.java +++ b/gson/src/test/java/com/google/gson/functional/CustomDeserializerTest.java @@ -25,10 +25,8 @@ import com.google.gson.JsonParseException; import com.google.gson.common.TestTypes.Base; import com.google.gson.common.TestTypes.ClassWithBaseField; - -import junit.framework.TestCase; - import java.lang.reflect.Type; +import junit.framework.TestCase; /** * Functional Test exercising custom deserialization only. When test applies to both @@ -64,52 +62,6 @@ public void testDefaultConstructorNotCalledOnField() throws Exception { assertEquals(DEFAULT_VALUE + SUFFIX, actual.getWrappedData().getData()); } - private static class DataHolder { - private final String data; - - // For use by Gson - @SuppressWarnings("unused") - private DataHolder() { - throw new IllegalStateException(); - } - - public DataHolder(String data) { - this.data = data; - } - - public String getData() { - return data; - } - } - - private static class DataHolderWrapper { - private final DataHolder wrappedData; - - // For use by Gson - @SuppressWarnings("unused") - private DataHolderWrapper() { - this(new DataHolder(DEFAULT_VALUE)); - } - - public DataHolderWrapper(DataHolder data) { - this.wrappedData = data; - } - - public DataHolder getWrappedData() { - return wrappedData; - } - } - - private static class DataHolderDeserializer implements JsonDeserializer { - @Override - public DataHolder deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) - throws JsonParseException { - JsonObject jsonObj = json.getAsJsonObject(); - String dataString = jsonObj.get("data").getAsString(); - return new DataHolder(dataString + SUFFIX); - } - } - public void testJsonTypeFieldBasedDeserialization() { String json = "{field1:'abc',field2:'def',__type__:'SUB_TYPE1'}"; Gson gson = new GsonBuilder().registerTypeAdapter(MyBase.class, new JsonDeserializer() { @@ -123,31 +75,6 @@ public void testJsonTypeFieldBasedDeserialization() { assertEquals("abc", target.field1); } - private static class MyBase { - static final String TYPE_ACCESS = "__type__"; - } - - private enum SubTypes { - SUB_TYPE1(SubType1.class), - SUB_TYPE2(SubType2.class); - private final Type subClass; - private SubTypes(Type subClass) { - this.subClass = subClass; - } - public Type getSubclass() { - return subClass; - } - } - - private static class SubType1 extends MyBase { - String field1; - } - - private static class SubType2 extends MyBase { - @SuppressWarnings("unused") - String field2; - } - public void testCustomDeserializerReturnsNullForTopLevelObject() { Gson gson = new GsonBuilder() .registerTypeAdapter(Base.class, new JsonDeserializer() { @@ -206,6 +133,77 @@ public Base deserialize(JsonElement json, Type typeOfT, JsonDeserializationConte assertNull(target.bases[1]); } + private enum SubTypes { + SUB_TYPE1(SubType1.class), + SUB_TYPE2(SubType2.class); + private final Type subClass; + private SubTypes(Type subClass) { + this.subClass = subClass; + } + public Type getSubclass() { + return subClass; + } + } + + private static class DataHolder { + private final String data; + + // For use by Gson + @SuppressWarnings("unused") + private DataHolder() { + throw new IllegalStateException(); + } + + public DataHolder(String data) { + this.data = data; + } + + public String getData() { + return data; + } + } + + private static class DataHolderWrapper { + private final DataHolder wrappedData; + + // For use by Gson + @SuppressWarnings("unused") + private DataHolderWrapper() { + this(new DataHolder(DEFAULT_VALUE)); + } + + public DataHolderWrapper(DataHolder data) { + this.wrappedData = data; + } + + public DataHolder getWrappedData() { + return wrappedData; + } + } + + private static class DataHolderDeserializer implements JsonDeserializer { + @Override + public DataHolder deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) + throws JsonParseException { + JsonObject jsonObj = json.getAsJsonObject(); + String dataString = jsonObj.get("data").getAsString(); + return new DataHolder(dataString + SUFFIX); + } + } + + private static class MyBase { + static final String TYPE_ACCESS = "__type__"; + } + + private static class SubType1 extends MyBase { + String field1; + } + + private static class SubType2 extends MyBase { + @SuppressWarnings("unused") + String field2; + } + private static final class ClassWithBaseArray { Base[] bases; } diff --git a/gson/src/test/java/com/google/gson/functional/CustomSerializerTest.java b/gson/src/test/java/com/google/gson/functional/CustomSerializerTest.java index e04cb67c5f..21018c8e36 100644 --- a/gson/src/test/java/com/google/gson/functional/CustomSerializerTest.java +++ b/gson/src/test/java/com/google/gson/functional/CustomSerializerTest.java @@ -29,10 +29,8 @@ import com.google.gson.common.TestTypes.ClassWithBaseField; import com.google.gson.common.TestTypes.Sub; import com.google.gson.common.TestTypes.SubSerializer; - -import junit.framework.TestCase; - import java.lang.reflect.Type; +import junit.framework.TestCase; /** * Functional Test exercising custom serialization only. When test applies to both diff --git a/gson/src/test/java/com/google/gson/functional/CustomTypeAdaptersTest.java b/gson/src/test/java/com/google/gson/functional/CustomTypeAdaptersTest.java index 1c38e6caea..6f15b4298f 100644 --- a/gson/src/test/java/com/google/gson/functional/CustomTypeAdaptersTest.java +++ b/gson/src/test/java/com/google/gson/functional/CustomTypeAdaptersTest.java @@ -165,47 +165,10 @@ public JsonElement serialize(Base src, Type typeOfSrc, JsonSerializationContext assertFalse(json.contains("derivedValue")); } - private static class Base { - int baseValue = 2; - } - - private static class Derived extends Base { - @SuppressWarnings("unused") - int derivedValue = 3; - } - - private Gson createGsonObjectWithFooTypeAdapter() { return new GsonBuilder().registerTypeAdapter(Foo.class, new FooTypeAdapter()).create(); } - public static class Foo { - private final int key; - private final long value; - - public Foo() { - this(0, 0L); - } - - public Foo(int key, long value) { - this.key = key; - this.value = value; - } - } - - public static final class FooTypeAdapter implements JsonSerializer, JsonDeserializer { - @Override - public Foo deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) - throws JsonParseException { - return context.deserialize(json, typeOfT); - } - - @Override - public JsonElement serialize(Foo src, Type typeOfSrc, JsonSerializationContext context) { - return context.serialize(src, typeOfSrc); - } - } - public void testCustomSerializerInvokedForPrimitives() { Gson gson = new GsonBuilder() .registerTypeAdapter(boolean.class, new JsonSerializer() { @@ -269,41 +232,6 @@ public void testCustomByteArrayDeserializerAndInstanceCreator() { } } - private static final class StringHolder { - String part1; - String part2; - - public StringHolder(String string) { - String[] parts = string.split(":"); - part1 = parts[0]; - part2 = parts[1]; - } - public StringHolder(String part1, String part2) { - this.part1 = part1; - this.part2 = part2; - } - } - - private static class StringHolderTypeAdapter implements JsonSerializer, - JsonDeserializer, InstanceCreator { - - @Override public StringHolder createInstance(Type type) { - //Fill up with objects that will be thrown away - return new StringHolder("unknown:thing"); - } - - @Override public StringHolder deserialize(JsonElement src, Type type, - JsonDeserializationContext context) { - return new StringHolder(src.getAsString()); - } - - @Override public JsonElement serialize(StringHolder src, Type typeOfSrc, - JsonSerializationContext context) { - String contents = src.part1 + ':' + src.part2; - return new JsonPrimitive(contents); - } - } - // Test created from Issue 70 public void testCustomAdapterInvokedForCollectionElementSerializationWithType() { Gson gson = new GsonBuilder() @@ -409,6 +337,77 @@ public void testRegisterHierarchyAdapterForDate() { assertEquals(new java.sql.Date(0), gson.fromJson("0", java.sql.Date.class)); } + private static class Base { + int baseValue = 2; + } + + private static class Derived extends Base { + @SuppressWarnings("unused") + int derivedValue = 3; + } + + public static class Foo { + private final int key; + private final long value; + + public Foo() { + this(0, 0L); + } + + public Foo(int key, long value) { + this.key = key; + this.value = value; + } + } + + public static final class FooTypeAdapter implements JsonSerializer, JsonDeserializer { + @Override + public Foo deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) + throws JsonParseException { + return context.deserialize(json, typeOfT); + } + + @Override + public JsonElement serialize(Foo src, Type typeOfSrc, JsonSerializationContext context) { + return context.serialize(src, typeOfSrc); + } + } + + private static final class StringHolder { + String part1; + String part2; + + public StringHolder(String string) { + String[] parts = string.split(":"); + part1 = parts[0]; + part2 = parts[1]; + } + public StringHolder(String part1, String part2) { + this.part1 = part1; + this.part2 = part2; + } + } + + private static class StringHolderTypeAdapter implements JsonSerializer, + JsonDeserializer, InstanceCreator { + + @Override public StringHolder createInstance(Type type) { + //Fill up with objects that will be thrown away + return new StringHolder("unknown:thing"); + } + + @Override public StringHolder deserialize(JsonElement src, Type type, + JsonDeserializationContext context) { + return new StringHolder(src.getAsString()); + } + + @Override public JsonElement serialize(StringHolder src, Type typeOfSrc, + JsonSerializationContext context) { + String contents = src.part1 + ':' + src.part2; + return new JsonPrimitive(contents); + } + } + private static class DataHolder { final String data; diff --git a/gson/src/test/java/com/google/gson/functional/DefaultTypeAdaptersTest.java b/gson/src/test/java/com/google/gson/functional/DefaultTypeAdaptersTest.java index 218c97ab7d..a210e2d4eb 100644 --- a/gson/src/test/java/com/google/gson/functional/DefaultTypeAdaptersTest.java +++ b/gson/src/test/java/com/google/gson/functional/DefaultTypeAdaptersTest.java @@ -68,6 +68,27 @@ public class DefaultTypeAdaptersTest extends TestCase { private TimeZone oldTimeZone; private Locale oldLocale; + public static void testNullSerializationAndDeserialization(Gson gson, Class c) { + assertEquals("null", gson.toJson(null, c)); + assertEquals(null, gson.fromJson("null", c)); + } + + // Date can not directly be compared with another instance since the deserialization loses the + // millisecond portion. + @SuppressWarnings("deprecation") + public static void assertEqualsDate(Date date, int year, int month, int day) { + assertEquals(year-1900, date.getYear()); + assertEquals(month, date.getMonth()); + assertEquals(day, date.getDate()); + } + + @SuppressWarnings("deprecation") + public static void assertEqualsTime(Date date, int hours, int minutes, int seconds) { + assertEquals(hours, date.getHours()); + assertEquals(minutes, date.getMinutes()); + assertEquals(seconds, date.getSeconds()); + } + @Override protected void setUp() throws Exception { super.setUp(); @@ -134,10 +155,6 @@ public void testUrlNullDeserialization() { assertNull(target.url); } - private static class ClassWithUrlField { - URL url; - } - public void testUriSerialization() throws Exception { String uriValue = "http://google.com/"; URI uri = new URI(uriValue); @@ -186,11 +203,6 @@ private void testNullSerializationAndDeserialization(Class c) { testNullSerializationAndDeserialization(gson, c); } - public static void testNullSerializationAndDeserialization(Gson gson, Class c) { - assertEquals("null", gson.toJson(null, c)); - assertEquals(null, gson.fromJson("null", c)); - } - public void testUuidSerialization() throws Exception { String uuidValue = "c237bec1-19ef-4858-a98e-521cf0aad4c0"; UUID uuid = UUID.fromString(uuidValue); @@ -362,22 +374,6 @@ public void testDefaultDateDeserialization() { assertEqualsTime(extracted, 7, 18, 2); } - // Date can not directly be compared with another instance since the deserialization loses the - // millisecond portion. - @SuppressWarnings("deprecation") - public static void assertEqualsDate(Date date, int year, int month, int day) { - assertEquals(year-1900, date.getYear()); - assertEquals(month, date.getMonth()); - assertEquals(day, date.getDate()); - } - - @SuppressWarnings("deprecation") - public static void assertEqualsTime(Date date, int hours, int minutes, int seconds) { - assertEquals(hours, date.getHours()); - assertEquals(minutes, date.getMinutes()); - assertEquals(seconds, date.getSeconds()); - } - public void testDefaultDateSerializationUsingBuilder() throws Exception { Gson gson = new GsonBuilder().create(); Date now = new Date(1315806903103L); @@ -583,26 +579,6 @@ public void testJsonElementTypeMismatch() { } } - private static class ClassWithBigDecimal { - BigDecimal value; - ClassWithBigDecimal(String value) { - this.value = new BigDecimal(value); - } - String getExpectedJson() { - return "{\"value\":" + value.toEngineeringString() + "}"; - } - } - - private static class ClassWithBigInteger { - BigInteger value; - ClassWithBigInteger(String value) { - this.value = new BigInteger(value); - } - String getExpectedJson() { - return "{\"value\":" + value + "}"; - } - } - public void testPropertiesSerialization() { Properties props = new Properties(); props.setProperty("foo", "bar"); @@ -653,6 +629,30 @@ public void testStringBufferDeserialization() { assertEquals("abc", sb.toString()); } + private static class ClassWithUrlField { + URL url; + } + + private static class ClassWithBigDecimal { + BigDecimal value; + ClassWithBigDecimal(String value) { + this.value = new BigDecimal(value); + } + String getExpectedJson() { + return "{\"value\":" + value.toEngineeringString() + "}"; + } + } + + private static class ClassWithBigInteger { + BigInteger value; + ClassWithBigInteger(String value) { + this.value = new BigInteger(value); + } + String getExpectedJson() { + return "{\"value\":" + value + "}"; + } + } + private static class MyClassTypeAdapter extends TypeAdapter> { @Override public void write(JsonWriter out, Class value) throws IOException { diff --git a/gson/src/test/java/com/google/gson/functional/DelegateTypeAdapterTest.java b/gson/src/test/java/com/google/gson/functional/DelegateTypeAdapterTest.java index 87ee81e57d..7ef100b3d3 100644 --- a/gson/src/test/java/com/google/gson/functional/DelegateTypeAdapterTest.java +++ b/gson/src/test/java/com/google/gson/functional/DelegateTypeAdapterTest.java @@ -15,12 +15,6 @@ */ package com.google.gson.functional; -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; - -import junit.framework.TestCase; - import com.google.gson.Gson; import com.google.gson.GsonBuilder; import com.google.gson.TypeAdapter; @@ -29,6 +23,10 @@ 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.List; +import junit.framework.TestCase; /** * Functional tests for {@link Gson#getDelegateAdapter(TypeAdapterFactory, TypeToken)} method. diff --git a/gson/src/test/java/com/google/gson/functional/EnumTest.java b/gson/src/test/java/com/google/gson/functional/EnumTest.java index b46f80b86b..25f08e25b5 100644 --- a/gson/src/test/java/com/google/gson/functional/EnumTest.java +++ b/gson/src/test/java/com/google/gson/functional/EnumTest.java @@ -96,18 +96,6 @@ public void testClassWithEnumFieldDeserialization() throws Exception { assertEquals(MyEnum.VALUE2,target.value2); } - private static enum MyEnum { - VALUE1, VALUE2 - } - - private static class ClassWithEnumFields { - private final MyEnum value1 = MyEnum.VALUE1; - private final MyEnum value2 = MyEnum.VALUE2; - public String getExpectedJson() { - return "{\"value1\":\"" + value1 + "\",\"value2\":\"" + value2 + "\"}"; - } - } - /** * Test for issue 226. */ @@ -175,6 +163,34 @@ public void testEnumMap() throws Exception { assertEquals(expectedMap, actualMap); } + public void testEnumClassWithFields() { + assertEquals("\"RED\"", gson.toJson(Color.RED)); + assertEquals("red", gson.fromJson("RED", Color.class).value); + assertEquals(2, gson.fromJson("BLUE", Color.class).index); + } + + public void testEnumToStringRead() { + // Should still be able to read constant name + assertEquals(CustomToString.A, gson.fromJson("\"A\"", CustomToString.class)); + // Should be able to read toString() value + assertEquals(CustomToString.A, gson.fromJson("\"test\"", CustomToString.class)); + + assertNull(gson.fromJson("\"other\"", CustomToString.class)); + } + + /** + * Test that enum constant names have higher precedence than {@code toString()} + * result. + */ + public void testEnumToStringReadInterchanged() { + assertEquals(InterchangedToString.A, gson.fromJson("\"A\"", InterchangedToString.class)); + assertEquals(InterchangedToString.B, gson.fromJson("\"B\"", InterchangedToString.class)); + } + + private static enum MyEnum { + VALUE1, VALUE2 + } + private enum Roshambo { ROCK { @Override Roshambo defeats() { @@ -195,18 +211,6 @@ private enum Roshambo { abstract Roshambo defeats(); } - private static class MyEnumTypeAdapter - implements JsonSerializer, JsonDeserializer { - @Override public JsonElement serialize(Roshambo src, Type typeOfSrc, JsonSerializationContext context) { - return new JsonPrimitive("123" + src.name()); - } - - @Override public Roshambo deserialize(JsonElement json, Type classOfT, JsonDeserializationContext context) - throws JsonParseException { - return Roshambo.valueOf(json.getAsString().substring(3)); - } - } - private enum Gender { @SerializedName("boy") MALE, @@ -215,12 +219,6 @@ private enum Gender { FEMALE } - public void testEnumClassWithFields() { - assertEquals("\"RED\"", gson.toJson(Color.RED)); - assertEquals("red", gson.fromJson("RED", Color.class).value); - assertEquals(2, gson.fromJson("BLUE", Color.class).index); - } - private enum Color { RED("red", 1), BLUE("blue", 2), GREEN("green", 3); String value; @@ -231,15 +229,6 @@ private Color(String value, int index) { } } - public void testEnumToStringRead() { - // Should still be able to read constant name - assertEquals(CustomToString.A, gson.fromJson("\"A\"", CustomToString.class)); - // Should be able to read toString() value - assertEquals(CustomToString.A, gson.fromJson("\"test\"", CustomToString.class)); - - assertNull(gson.fromJson("\"other\"", CustomToString.class)); - } - private enum CustomToString { A; @@ -249,15 +238,6 @@ public String toString() { } } - /** - * Test that enum constant names have higher precedence than {@code toString()} - * result. - */ - public void testEnumToStringReadInterchanged() { - assertEquals(InterchangedToString.A, gson.fromJson("\"A\"", InterchangedToString.class)); - assertEquals(InterchangedToString.B, gson.fromJson("\"B\"", InterchangedToString.class)); - } - private enum InterchangedToString { A("B"), B("A"); @@ -273,4 +253,24 @@ public String toString() { return toString; } } + + private static class ClassWithEnumFields { + private final MyEnum value1 = MyEnum.VALUE1; + private final MyEnum value2 = MyEnum.VALUE2; + public String getExpectedJson() { + return "{\"value1\":\"" + value1 + "\",\"value2\":\"" + value2 + "\"}"; + } + } + + private static class MyEnumTypeAdapter + implements JsonSerializer, JsonDeserializer { + @Override public JsonElement serialize(Roshambo src, Type typeOfSrc, JsonSerializationContext context) { + return new JsonPrimitive("123" + src.name()); + } + + @Override public Roshambo deserialize(JsonElement json, Type classOfT, JsonDeserializationContext context) + throws JsonParseException { + return Roshambo.valueOf(json.getAsString().substring(3)); + } + } } diff --git a/gson/src/test/java/com/google/gson/functional/EnumWithObfuscatedTest.java b/gson/src/test/java/com/google/gson/functional/EnumWithObfuscatedTest.java index 080b81fa77..4a26abedf0 100644 --- a/gson/src/test/java/com/google/gson/functional/EnumWithObfuscatedTest.java +++ b/gson/src/test/java/com/google/gson/functional/EnumWithObfuscatedTest.java @@ -18,7 +18,6 @@ import com.google.gson.Gson; import com.google.gson.annotations.SerializedName; - import junit.framework.TestCase; /** @@ -35,14 +34,6 @@ protected void setUp() throws Exception { gson = new Gson(); } - public enum Gender { - @SerializedName("MAIL") - MALE, - - @SerializedName("FEMAIL") - FEMALE - } - public void testEnumClassWithObfuscated() { for (Gender enumConstant: Gender.class.getEnumConstants()) { try { @@ -55,4 +46,12 @@ public void testEnumClassWithObfuscated() { assertEquals(Gender.MALE, gson.fromJson("\"MAIL\"", Gender.class)); assertEquals("\"MAIL\"", gson.toJson(Gender.MALE, Gender.class)); } + + public enum Gender { + @SerializedName("MAIL") + MALE, + + @SerializedName("FEMAIL") + FEMALE + } } diff --git a/gson/src/test/java/com/google/gson/functional/ExclusionStrategyFunctionalTest.java b/gson/src/test/java/com/google/gson/functional/ExclusionStrategyFunctionalTest.java index 2eca8bb4f9..2b39d389d3 100644 --- a/gson/src/test/java/com/google/gson/functional/ExclusionStrategyFunctionalTest.java +++ b/gson/src/test/java/com/google/gson/functional/ExclusionStrategyFunctionalTest.java @@ -47,6 +47,18 @@ public class ExclusionStrategyFunctionalTest extends TestCase { private SampleObjectForTest src; + private static Gson createGson(ExclusionStrategy exclusionStrategy, boolean serialization) { + GsonBuilder gsonBuilder = new GsonBuilder(); + if (serialization) { + gsonBuilder.addSerializationExclusionStrategy(exclusionStrategy); + } else { + gsonBuilder.addDeserializationExclusionStrategy(exclusionStrategy); + } + return gsonBuilder + .serializeNulls() + .create(); + } + @Override protected void setUp() throws Exception { super.setUp(); @@ -149,18 +161,6 @@ public void testExcludeTopLevelClassDeserializationDoesNotImpactSerialization() assertTrue(json.contains("\"longField\"")); } - private static Gson createGson(ExclusionStrategy exclusionStrategy, boolean serialization) { - GsonBuilder gsonBuilder = new GsonBuilder(); - if (serialization) { - gsonBuilder.addSerializationExclusionStrategy(exclusionStrategy); - } else { - gsonBuilder.addDeserializationExclusionStrategy(exclusionStrategy); - } - return gsonBuilder - .serializeNulls() - .create(); - } - @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.FIELD}) private static @interface Foo { diff --git a/gson/src/test/java/com/google/gson/functional/ExposeFieldsTest.java b/gson/src/test/java/com/google/gson/functional/ExposeFieldsTest.java index 0430ba49d1..860d5e320f 100644 --- a/gson/src/test/java/com/google/gson/functional/ExposeFieldsTest.java +++ b/gson/src/test/java/com/google/gson/functional/ExposeFieldsTest.java @@ -16,13 +16,11 @@ package com.google.gson.functional; -import java.lang.reflect.Type; - import com.google.gson.Gson; import com.google.gson.GsonBuilder; import com.google.gson.InstanceCreator; import com.google.gson.annotations.Expose; - +import java.lang.reflect.Type; import junit.framework.TestCase; /** @@ -110,12 +108,16 @@ public void testExposedInterfaceFieldDeserialization() throws Exception { assertNotNull(obj.interfaceField); } + private static interface SomeInterface { + // Empty interface + } + private static class ClassWithExposedFields { - @Expose private final Integer a; - private final Integer b; @Expose(serialize = false) final long c; @Expose(deserialize = false) final double d; @Expose(serialize = false, deserialize = false) final char e; + @Expose private final Integer a; + private final Integer b; public ClassWithExposedFields(Integer a, Integer b) { this(a, b, 1L, 2.0, 'a'); @@ -138,16 +140,12 @@ public String getExpectedJson() { return sb.toString(); } } - + private static class ClassWithNoExposedFields { private final int a = 0; private final int b = 1; } - private static interface SomeInterface { - // Empty interface - } - private static class SomeObject implements SomeInterface { // Do nothing } diff --git a/gson/src/test/java/com/google/gson/functional/FieldExclusionTest.java b/gson/src/test/java/com/google/gson/functional/FieldExclusionTest.java index 080a8234fe..9413dc6870 100644 --- a/gson/src/test/java/com/google/gson/functional/FieldExclusionTest.java +++ b/gson/src/test/java/com/google/gson/functional/FieldExclusionTest.java @@ -18,7 +18,6 @@ import com.google.gson.Gson; import com.google.gson.GsonBuilder; - import junit.framework.TestCase; /** diff --git a/gson/src/test/java/com/google/gson/functional/GsonVersionDiagnosticsTest.java b/gson/src/test/java/com/google/gson/functional/GsonVersionDiagnosticsTest.java index 0bf647e115..841b1e5f76 100644 --- a/gson/src/test/java/com/google/gson/functional/GsonVersionDiagnosticsTest.java +++ b/gson/src/test/java/com/google/gson/functional/GsonVersionDiagnosticsTest.java @@ -15,19 +15,16 @@ */ package com.google.gson.functional; -import java.io.IOException; -import java.util.regex.Pattern; - -import org.junit.Before; -import org.junit.Test; - import com.google.gson.Gson; import com.google.gson.GsonBuilder; import com.google.gson.TypeAdapter; import com.google.gson.stream.JsonReader; import com.google.gson.stream.JsonWriter; - +import java.io.IOException; +import java.util.regex.Pattern; import junit.framework.TestCase; +import org.junit.Before; +import org.junit.Test; /** * Functional tests to validate printing of Gson version on AssertionErrors diff --git a/gson/src/test/java/com/google/gson/functional/InheritanceTest.java b/gson/src/test/java/com/google/gson/functional/InheritanceTest.java index 84de37de80..294ce1a0ac 100644 --- a/gson/src/test/java/com/google/gson/functional/InheritanceTest.java +++ b/gson/src/test/java/com/google/gson/functional/InheritanceTest.java @@ -26,9 +26,6 @@ import com.google.gson.common.TestTypes.ClassWithBaseField; import com.google.gson.common.TestTypes.Nested; import com.google.gson.common.TestTypes.Sub; - -import junit.framework.TestCase; - import java.util.ArrayList; import java.util.Collection; import java.util.LinkedList; @@ -37,6 +34,7 @@ import java.util.Set; import java.util.SortedSet; import java.util.TreeSet; +import junit.framework.TestCase; /** * Functional tests for Json serialization and deserialization of classes with @@ -136,20 +134,6 @@ public void testBaseSerializedAsSubWhenSpecifiedWithExplicitTypeForToJsonMethod( assertTrue(json.contains(Sub.SUB_NAME)); } - private static class SubTypeOfNested extends Nested { - private final long value = 5; - - public SubTypeOfNested(BagOfPrimitives primitive1, BagOfPrimitives primitive2) { - super(primitive1, primitive2); - } - - @Override - public void appendFields(StringBuilder sb) { - sb.append("\"value\":").append(value).append(","); - super.appendFields(sb); - } - } - public void testSubInterfacesOfCollectionSerialization() throws Exception { List list = new LinkedList<>(); list.add(0); @@ -180,13 +164,27 @@ public void testSubInterfacesOfCollectionDeserialization() throws Exception { String json = "{\"list\":[0,1,2,3],\"queue\":[0,1,2,3],\"set\":[0.1,0.2,0.3,0.4]," + "\"sortedSet\":[\"a\",\"b\",\"c\",\"d\"]" + "}"; - ClassWithSubInterfacesOfCollection target = + ClassWithSubInterfacesOfCollection target = gson.fromJson(json, ClassWithSubInterfacesOfCollection.class); assertTrue(target.listContains(0, 1, 2, 3)); assertTrue(target.queueContains(0, 1, 2, 3)); assertTrue(target.setContains(0.1F, 0.2F, 0.3F, 0.4F)); assertTrue(target.sortedSetContains('a', 'b', 'c', 'd')); } + + private static class SubTypeOfNested extends Nested { + private final long value = 5; + + public SubTypeOfNested(BagOfPrimitives primitive1, BagOfPrimitives primitive2) { + super(primitive1, primitive2); + } + + @Override + public void appendFields(StringBuilder sb) { + sb.append("\"value\":").append(value).append(","); + super.appendFields(sb); + } + } private static class ClassWithSubInterfacesOfCollection { private List list; diff --git a/gson/src/test/java/com/google/gson/functional/InterfaceTest.java b/gson/src/test/java/com/google/gson/functional/InterfaceTest.java index 6851f1e99b..e10d80d06d 100644 --- a/gson/src/test/java/com/google/gson/functional/InterfaceTest.java +++ b/gson/src/test/java/com/google/gson/functional/InterfaceTest.java @@ -17,7 +17,6 @@ package com.google.gson.functional; import com.google.gson.Gson; - import junit.framework.TestCase; /** diff --git a/gson/src/test/java/com/google/gson/functional/Java17RecordTest.java b/gson/src/test/java/com/google/gson/functional/Java17RecordTest.java index a172f5a4ef..3f30a470ae 100644 --- a/gson/src/test/java/com/google/gson/functional/Java17RecordTest.java +++ b/gson/src/test/java/com/google/gson/functional/Java17RecordTest.java @@ -75,10 +75,6 @@ public void testMultipleNamesInTheSameString() { gson.fromJson("{'name': 'foo', 'name1':'v1','name2':'v2','name3':'v3'}", RecordWithCustomNames.class).b); } - private record RecordWithCustomNames( - @SerializedName("name") String a, - @SerializedName(value = "name1", alternate = {"name2", "name3"}) String b) {} - @Test public void testSerializedNameOnAccessor() { record LocalRecord(int i) { @@ -256,9 +252,6 @@ public void testPrimitiveAdapterNullValue() { exception.getMessage()); } - private record RecordWithPrimitives( - String aString, byte aByte, short aShort, int anInt, long aLong, float aFloat, double aDouble, char aChar, boolean aBoolean) {} - /** Tests behavior when value of Object component is missing; should default to null */ @Test public void testObjectDefaultValue() { @@ -315,10 +308,6 @@ public void testStaticFieldDeserialization() { } } - private record RecordWithStaticField() { - static String s = "initial"; - } - @Test public void testExposeAnnotation() { record RecordWithExpose( @@ -409,9 +398,6 @@ public void testReflectionFilterBlockInaccessible() { assertEquals(new PublicRecord(2), gson.fromJson("{\"i\":2}", PublicRecord.class)); } - private record PrivateRecord(int i) {} - public record PublicRecord(int i) {} - /** * Tests behavior when {@code java.lang.Record} is used as type for serialization * and deserialization. @@ -427,4 +413,19 @@ record LocalRecord(int i) {} + " this type. Class name: java.lang.Record", exception.getMessage()); } + + private record RecordWithCustomNames( + @SerializedName("name") String a, + @SerializedName(value = "name1", alternate = {"name2", "name3"}) String b) {} + + private record RecordWithPrimitives( + String aString, byte aByte, short aShort, int anInt, long aLong, float aFloat, double aDouble, char aChar, boolean aBoolean) {} + + private record RecordWithStaticField() { + static String s = "initial"; + } + + private record PrivateRecord(int i) {} + + public record PublicRecord(int i) {} } diff --git a/gson/src/test/java/com/google/gson/functional/JavaUtilConcurrentAtomicTest.java b/gson/src/test/java/com/google/gson/functional/JavaUtilConcurrentAtomicTest.java index 464892a3a6..2d0f3757e4 100644 --- a/gson/src/test/java/com/google/gson/functional/JavaUtilConcurrentAtomicTest.java +++ b/gson/src/test/java/com/google/gson/functional/JavaUtilConcurrentAtomicTest.java @@ -16,16 +16,14 @@ package com.google.gson.functional; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.LongSerializationPolicy; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicIntegerArray; import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicLongArray; - -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; -import com.google.gson.LongSerializationPolicy; - import junit.framework.TestCase; /** diff --git a/gson/src/test/java/com/google/gson/functional/JavaUtilTest.java b/gson/src/test/java/com/google/gson/functional/JavaUtilTest.java index 0520965339..85bf4533aa 100644 --- a/gson/src/test/java/com/google/gson/functional/JavaUtilTest.java +++ b/gson/src/test/java/com/google/gson/functional/JavaUtilTest.java @@ -16,11 +16,9 @@ package com.google.gson.functional; +import com.google.gson.Gson; import java.util.Currency; import java.util.Properties; - -import com.google.gson.Gson; - import junit.framework.TestCase; /** @@ -47,10 +45,6 @@ public void testCurrency() throws Exception { assertEquals("{}", gson.toJson(target)); } - private static class CurrencyHolder { - Currency value; - } - public void testProperties() { Properties props = gson.fromJson("{'a':'v1','b':'v2'}", Properties.class); assertEquals("v1", props.getProperty("a")); @@ -59,4 +53,8 @@ public void testProperties() { assertTrue(json.contains("\"a\":\"v1\"")); assertTrue(json.contains("\"b\":\"v2\"")); } + + private static class CurrencyHolder { + Currency value; + } } diff --git a/gson/src/test/java/com/google/gson/functional/JsonAdapterAnnotationOnClassesTest.java b/gson/src/test/java/com/google/gson/functional/JsonAdapterAnnotationOnClassesTest.java index db939dcdc5..0da1b7180a 100644 --- a/gson/src/test/java/com/google/gson/functional/JsonAdapterAnnotationOnClassesTest.java +++ b/gson/src/test/java/com/google/gson/functional/JsonAdapterAnnotationOnClassesTest.java @@ -139,6 +139,16 @@ public void testNullSafeObjectFromJson() { assertNull(fromJson); } + public void testIncorrectJsonAdapterType() { + try { + new Gson().toJson(new D()); + fail(); + } catch (IllegalArgumentException expected) {} + } + + @JsonAdapter(FooJsonAdapter.class) + private static enum Foo { BAR, BAZ } + @JsonAdapter(A.JsonAdapter.class) private static class A { final String value; @@ -183,6 +193,7 @@ private static final class B extends A { super(value); } } + // Note that the type is NOT TypeAdapter so this // should cause error @JsonAdapter(A.JsonAdapter.class) @@ -202,6 +213,7 @@ private static class User { this.lastName = lastName; } } + private static class UserJsonAdapter extends TypeAdapter { @Override public void write(JsonWriter out, User user) throws IOException { // implement write: combine firstName and lastName into name @@ -238,8 +250,6 @@ public NullableClass read(JsonReader in) throws IOException { } } - @JsonAdapter(FooJsonAdapter.class) - private static enum Foo { BAR, BAZ } private static class FooJsonAdapter extends TypeAdapter { @Override public void write(JsonWriter out, Foo value) throws IOException { out.value(value.name().toLowerCase(Locale.US)); @@ -250,12 +260,6 @@ private static class FooJsonAdapter extends TypeAdapter { } } - public void testIncorrectJsonAdapterType() { - try { - new Gson().toJson(new D()); - fail(); - } catch (IllegalArgumentException expected) {} - } @JsonAdapter(Integer.class) private static final class D { @SuppressWarnings("unused") final String value = "a"; diff --git a/gson/src/test/java/com/google/gson/functional/JsonAdapterAnnotationOnFieldsTest.java b/gson/src/test/java/com/google/gson/functional/JsonAdapterAnnotationOnFieldsTest.java index 706fe60f85..61908f7dd6 100644 --- a/gson/src/test/java/com/google/gson/functional/JsonAdapterAnnotationOnFieldsTest.java +++ b/gson/src/test/java/com/google/gson/functional/JsonAdapterAnnotationOnFieldsTest.java @@ -16,10 +16,6 @@ package com.google.gson.functional; -import java.io.IOException; -import java.util.Arrays; -import java.util.List; - import com.google.gson.Gson; import com.google.gson.GsonBuilder; import com.google.gson.TypeAdapter; @@ -28,7 +24,9 @@ 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.Arrays; +import java.util.List; import junit.framework.TestCase; /** @@ -85,6 +83,51 @@ public void testFieldAnnotationTakesPrecedenceOverClassAnnotation() { assertEquals("UserFieldAnnotationAdapter", target.user.name); } + public void testJsonAdapterInvokedOnlyForAnnotatedFields() { + Gson gson = new Gson(); + String json = "{'part1':'name','part2':{'name':'name2'}}"; + GadgetWithTwoParts gadget = gson.fromJson(json, GadgetWithTwoParts.class); + assertEquals("PartJsonFieldAnnotationAdapter", gadget.part1.name); + assertEquals("name2", gadget.part2.name); + } + + public void testJsonAdapterWrappedInNullSafeAsRequested() { + Gson gson = new Gson(); + String fromJson = "{'part':null}"; + + GadgetWithOptionalPart gadget = gson.fromJson(fromJson, GadgetWithOptionalPart.class); + assertNull(gadget.part); + + String toJson = gson.toJson(gadget); + assertFalse(toJson.contains("PartJsonFieldAnnotationAdapter")); + } + + /** Regression test contributed through https://github.com/google/gson/issues/831 */ + public void testNonPrimitiveFieldAnnotationTakesPrecedenceOverDefault() { + Gson gson = new Gson(); + String json = gson.toJson(new GadgetWithOptionalPart(new Part("foo"))); + assertEquals("{\"part\":\"PartJsonFieldAnnotationAdapter\"}", json); + GadgetWithOptionalPart gadget = gson.fromJson("{'part':'foo'}", GadgetWithOptionalPart.class); + assertEquals("PartJsonFieldAnnotationAdapter", gadget.part.name); + } + + /** Regression test contributed through https://github.com/google/gson/issues/831 */ + public void testPrimitiveFieldAnnotationTakesPrecedenceOverDefault() { + Gson gson = new Gson(); + String json = gson.toJson(new GadgetWithPrimitivePart(42)); + assertEquals("{\"part\":\"42\"}", json); + GadgetWithPrimitivePart gadget = gson.fromJson(json, GadgetWithPrimitivePart.class); + assertEquals(42, gadget.part); + } + + public void testFieldAnnotationWorksForParameterizedType() { + Gson gson = new Gson(); + String json = gson.toJson(new Gizmo2(Arrays.asList(new Part("Part")))); + assertEquals("{\"part\":\"GizmoPartTypeAdapterFactory\"}", json); + Gizmo2 computer = gson.fromJson("{'part':'Part'}", Gizmo2.class); + assertEquals("GizmoPartTypeAdapterFactory", computer.part.get(0).name); + } + private static final class Gadget { @JsonAdapter(PartJsonFieldAnnotationAdapter.class) final Part part; @@ -187,14 +230,6 @@ private static final class RegisteredUserAdapter extends TypeAdapter { } } - public void testJsonAdapterInvokedOnlyForAnnotatedFields() { - Gson gson = new Gson(); - String json = "{'part1':'name','part2':{'name':'name2'}}"; - GadgetWithTwoParts gadget = gson.fromJson(json, GadgetWithTwoParts.class); - assertEquals("PartJsonFieldAnnotationAdapter", gadget.part1.name); - assertEquals("name2", gadget.part2.name); - } - private static final class GadgetWithTwoParts { @JsonAdapter(PartJsonFieldAnnotationAdapter.class) final Part part1; final Part part2; // Doesn't have the JsonAdapter annotation @@ -204,17 +239,6 @@ private static final class GadgetWithTwoParts { } } - public void testJsonAdapterWrappedInNullSafeAsRequested() { - Gson gson = new Gson(); - String fromJson = "{'part':null}"; - - GadgetWithOptionalPart gadget = gson.fromJson(fromJson, GadgetWithOptionalPart.class); - assertNull(gadget.part); - - String toJson = gson.toJson(gadget); - assertFalse(toJson.contains("PartJsonFieldAnnotationAdapter")); - } - private static final class GadgetWithOptionalPart { @JsonAdapter(value = PartJsonFieldAnnotationAdapter.class) final Part part; @@ -224,24 +248,6 @@ private GadgetWithOptionalPart(Part part) { } } - /** Regression test contributed through https://github.com/google/gson/issues/831 */ - public void testNonPrimitiveFieldAnnotationTakesPrecedenceOverDefault() { - Gson gson = new Gson(); - String json = gson.toJson(new GadgetWithOptionalPart(new Part("foo"))); - assertEquals("{\"part\":\"PartJsonFieldAnnotationAdapter\"}", json); - GadgetWithOptionalPart gadget = gson.fromJson("{'part':'foo'}", GadgetWithOptionalPart.class); - assertEquals("PartJsonFieldAnnotationAdapter", gadget.part.name); - } - - /** Regression test contributed through https://github.com/google/gson/issues/831 */ - public void testPrimitiveFieldAnnotationTakesPrecedenceOverDefault() { - Gson gson = new Gson(); - String json = gson.toJson(new GadgetWithPrimitivePart(42)); - assertEquals("{\"part\":\"42\"}", json); - GadgetWithPrimitivePart gadget = gson.fromJson(json, GadgetWithPrimitivePart.class); - assertEquals(42, gadget.part); - } - private static final class GadgetWithPrimitivePart { @JsonAdapter(LongToStringTypeAdapterFactory.class) final long part; @@ -273,14 +279,6 @@ private static final class LongToStringTypeAdapterFactory implements TypeAdapter } } - public void testFieldAnnotationWorksForParameterizedType() { - Gson gson = new Gson(); - String json = gson.toJson(new Gizmo2(Arrays.asList(new Part("Part")))); - assertEquals("{\"part\":\"GizmoPartTypeAdapterFactory\"}", json); - Gizmo2 computer = gson.fromJson("{'part':'Part'}", Gizmo2.class); - assertEquals("GizmoPartTypeAdapterFactory", computer.part.get(0).name); - } - private static final class Gizmo2 { @JsonAdapter(Gizmo2PartTypeAdapterFactory.class) List part; 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 f539884366..d0d35c2456 100644 --- a/gson/src/test/java/com/google/gson/functional/JsonAdapterSerializerDeserializerTest.java +++ b/gson/src/test/java/com/google/gson/functional/JsonAdapterSerializerDeserializerTest.java @@ -16,8 +16,6 @@ package com.google.gson.functional; -import java.lang.reflect.Type; - import com.google.gson.Gson; import com.google.gson.JsonDeserializationContext; import com.google.gson.JsonDeserializer; @@ -27,7 +25,7 @@ import com.google.gson.JsonSerializationContext; import com.google.gson.JsonSerializer; import com.google.gson.annotations.JsonAdapter; - +import java.lang.reflect.Type; import junit.framework.TestCase; /** @@ -45,6 +43,31 @@ public void testJsonSerializerDeserializerBasedJsonAdapterOnFields() { assertEquals("UserSerializerDeserializer", computer.user3.name); } + public void testJsonSerializerDeserializerBasedJsonAdapterOnClass() { + Gson gson = new Gson(); + String json = gson.toJson(new Computer2(new User2("Inderjeet Singh"))); + assertEquals("{\"user\":\"UserSerializerDeserializer2\"}", json); + Computer2 computer = gson.fromJson("{'user':'Inderjeet Singh'}", Computer2.class); + assertEquals("UserSerializerDeserializer2", computer.user.name); + } + + public void testDifferentJsonAdaptersForGenericFieldsOfSameRawType() { + Container c = new Container("Foo", 10); + Gson gson = new Gson(); + String json = gson.toJson(c); + assertTrue(json.contains("\"a\":\"BaseStringAdapter\"")); + assertTrue(json.contains("\"b\":\"BaseIntegerAdapter\"")); + } + + public void testJsonAdapterNullSafe() { + Gson gson = new Gson(); + String json = gson.toJson(new Computer3(null, null)); + assertEquals("{\"user1\":\"UserSerializerDeserializer\"}", json); + Computer3 computer3 = gson.fromJson("{\"user1\":null, \"user2\":null}", Computer3.class); + assertEquals("UserSerializerDeserializer", computer3.user1.name); + assertNull(computer3.user2); + } + private static final class Computer { @JsonAdapter(UserSerializer.class) final User user1; @JsonAdapter(UserDeserializer.class) final User user2; @@ -90,14 +113,6 @@ public User deserialize(JsonElement json, Type typeOfT, JsonDeserializationConte } } - public void testJsonSerializerDeserializerBasedJsonAdapterOnClass() { - Gson gson = new Gson(); - String json = gson.toJson(new Computer2(new User2("Inderjeet Singh"))); - assertEquals("{\"user\":\"UserSerializerDeserializer2\"}", json); - Computer2 computer = gson.fromJson("{'user':'Inderjeet Singh'}", Computer2.class); - assertEquals("UserSerializerDeserializer2", computer.user.name); - } - private static final class Computer2 { final User2 user; Computer2(User2 user) { @@ -125,14 +140,6 @@ public User2 deserialize(JsonElement json, Type typeOfT, JsonDeserializationCont } } - public void testDifferentJsonAdaptersForGenericFieldsOfSameRawType() { - Container c = new Container("Foo", 10); - Gson gson = new Gson(); - String json = gson.toJson(c); - assertTrue(json.contains("\"a\":\"BaseStringAdapter\"")); - assertTrue(json.contains("\"b\":\"BaseIntegerAdapter\"")); - } - private static final class Container { @JsonAdapter(BaseStringAdapter.class) Base a; @JsonAdapter(BaseIntegerAdapter.class) Base b; @@ -162,15 +169,6 @@ private static final class BaseIntegerAdapter implements JsonSerializer extends LinkedHashMap { - final int foo; - MyParameterizedMap(int foo) { - this.foo = foo; - } - } - public void testMapSubclassSerialization() { MyMap map = new MyMap(); map.put("a", "b"); @@ -314,13 +306,6 @@ public void testCustomSerializerForSpecificMapType() { assertEquals("[1,2,3]", gson.toJson(src, type)); } - /** - * Created in response to http://code.google.com/p/google-gson/issues/detail?id=99 - */ - private static class ClassWithAMap { - Map map = new TreeMap<>(); - } - /** * Created in response to http://code.google.com/p/google-gson/issues/detail?id=99 */ @@ -363,14 +348,6 @@ public void testMapDeserializationWithWildcardValues() { assertEquals(Long.valueOf(123L), map.get("test")); } - - private static class MyMap extends LinkedHashMap { - private static final long serialVersionUID = 1L; - - @SuppressWarnings("unused") - int foo = 10; - } - /** * From bug report http://code.google.com/p/google-gson/issues/detail?id=95 */ @@ -608,6 +585,28 @@ public void testMapNamePromotionWithJsonElementReader() { assertEquals(map, gson.fromJson(tree, new TypeToken>() {}.getType())); } + @SuppressWarnings({ "unused", "serial" }) + private static class MyParameterizedMap extends LinkedHashMap { + final int foo; + MyParameterizedMap(int foo) { + this.foo = foo; + } + } + + /** + * Created in response to http://code.google.com/p/google-gson/issues/detail?id=99 + */ + private static class ClassWithAMap { + Map map = new TreeMap<>(); + } + + private static class MyMap extends LinkedHashMap { + private static final long serialVersionUID = 1L; + + @SuppressWarnings("unused") + int foo = 10; + } + static class Point { private final int x; private final int y; diff --git a/gson/src/test/java/com/google/gson/functional/MoreSpecificTypeSerializationTest.java b/gson/src/test/java/com/google/gson/functional/MoreSpecificTypeSerializationTest.java index a179fa8e62..4e67da76b8 100644 --- a/gson/src/test/java/com/google/gson/functional/MoreSpecificTypeSerializationTest.java +++ b/gson/src/test/java/com/google/gson/functional/MoreSpecificTypeSerializationTest.java @@ -18,13 +18,11 @@ import com.google.gson.Gson; import com.google.gson.JsonObject; - -import junit.framework.TestCase; - import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.Map; +import junit.framework.TestCase; /** * Tests for Gson serialization of a sub-class object while encountering a base-class type diff --git a/gson/src/test/java/com/google/gson/functional/NullObjectAndFieldTest.java b/gson/src/test/java/com/google/gson/functional/NullObjectAndFieldTest.java index a9b77f1d80..e82dd2b905 100644 --- a/gson/src/test/java/com/google/gson/functional/NullObjectAndFieldTest.java +++ b/gson/src/test/java/com/google/gson/functional/NullObjectAndFieldTest.java @@ -18,20 +18,18 @@ import com.google.gson.Gson; import com.google.gson.GsonBuilder; +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonDeserializer; import com.google.gson.JsonElement; import com.google.gson.JsonNull; import com.google.gson.JsonObject; -import com.google.gson.JsonDeserializationContext; -import com.google.gson.JsonDeserializer; import com.google.gson.JsonSerializationContext; import com.google.gson.JsonSerializer; import com.google.gson.common.TestTypes.BagOfPrimitives; import com.google.gson.common.TestTypes.ClassWithObjects; - -import junit.framework.TestCase; - import java.lang.reflect.Type; import java.util.Collection; +import junit.framework.TestCase; /** * Functional tests for the different cases for serializing (or ignoring) null fields and object. @@ -163,6 +161,40 @@ public void testAbsentJsonElementsAreSetToNull() { assertFalse(target.bool2); // test the default value of a primitive boolean field per JVM spec } + public void testExplicitNullSetsFieldToNullDuringDeserialization() { + Gson gson = new Gson(); + String json = "{value:null}"; + ObjectWithField obj = gson.fromJson(json, ObjectWithField.class); + assertNull(obj.value); + } + + public void testCustomTypeAdapterPassesNullSerialization() { + Gson gson = new GsonBuilder() + .registerTypeAdapter(ObjectWithField.class, new JsonSerializer() { + @Override public JsonElement serialize(ObjectWithField src, Type typeOfSrc, + JsonSerializationContext context) { + return context.serialize(null); + } + }).create(); + ObjectWithField target = new ObjectWithField(); + target.value = "value1"; + String json = gson.toJson(target); + assertFalse(json.contains("value1")); + } + + public void testCustomTypeAdapterPassesNullDesrialization() { + Gson gson = new GsonBuilder() + .registerTypeAdapter(ObjectWithField.class, new JsonDeserializer() { + @Override public ObjectWithField deserialize(JsonElement json, Type type, + JsonDeserializationContext context) { + return context.deserialize(null, type); + } + }).create(); + String json = "{value:'value1'}"; + ObjectWithField target = gson.fromJson(json, ObjectWithField.class); + assertNull(target); + } + public static class ClassWithInitializedMembers { // Using a mix of no-args constructor and field initializers // Also, some fields are intialized and some are not (so initialized per JVM spec) @@ -190,7 +222,7 @@ private static class ClassWithMembers { int[] array; Collection col; } - + private static class ClassWithObjectsSerializer implements JsonSerializer { @Override public JsonElement serialize(ClassWithObjects src, Type typeOfSrc, JsonSerializationContext context) { @@ -200,40 +232,6 @@ private static class ClassWithObjectsSerializer implements JsonSerializer() { - @Override public JsonElement serialize(ObjectWithField src, Type typeOfSrc, - JsonSerializationContext context) { - return context.serialize(null); - } - }).create(); - ObjectWithField target = new ObjectWithField(); - target.value = "value1"; - String json = gson.toJson(target); - assertFalse(json.contains("value1")); - } - - public void testCustomTypeAdapterPassesNullDesrialization() { - Gson gson = new GsonBuilder() - .registerTypeAdapter(ObjectWithField.class, new JsonDeserializer() { - @Override public ObjectWithField deserialize(JsonElement json, Type type, - JsonDeserializationContext context) { - return context.deserialize(null, type); - } - }).create(); - String json = "{value:'value1'}"; - ObjectWithField target = gson.fromJson(json, ObjectWithField.class); - assertNull(target); - } - private static class ObjectWithField { String value = ""; } diff --git a/gson/src/test/java/com/google/gson/functional/ObjectTest.java b/gson/src/test/java/com/google/gson/functional/ObjectTest.java index cf851e060a..6563910520 100644 --- a/gson/src/test/java/com/google/gson/functional/ObjectTest.java +++ b/gson/src/test/java/com/google/gson/functional/ObjectTest.java @@ -145,17 +145,6 @@ public void testClassWithNoFieldsDeserialization() throws Exception { assertEquals(expected, target); } - private static class Subclass extends Superclass1 { - } - private static class Superclass1 extends Superclass2 { - @SuppressWarnings("unused") - String s; - } - private static class Superclass2 { - @SuppressWarnings("unused") - String s; - } - public void testClassWithDuplicateFields() { try { gson.getAdapter(Subclass.class); @@ -183,6 +172,7 @@ public void testNestedDeserialization() throws Exception { Nested target = gson.fromJson(json, Nested.class); assertEquals(json, target.getExpectedJson()); } + public void testNullSerialization() throws Exception { assertEquals("null", gson.toJson(null)); } @@ -281,10 +271,6 @@ public void testEmptyCollectionInAnObjectDeserialization() throws Exception { assertTrue(target.children.isEmpty()); } - private static class ClassWithCollectionField { - Collection children = new ArrayList<>(); - } - public void testPrimitiveArrayInAnObjectDeserialization() throws Exception { String json = "{\"longArray\":[0,1,2,3,4,5,6,7,8,9]}"; PrimitiveArray target = gson.fromJson(json, PrimitiveArray.class); @@ -348,11 +334,6 @@ public void testClassWithObjectFieldSerialization() { assertTrue(json.contains("abc")); } - private static class ClassWithObjectField { - @SuppressWarnings("unused") - Object member; - } - public void testInnerClassSerialization() { Parent p = new Parent(); Parent.Child c = p.new Child(); @@ -374,58 +355,6 @@ public void testInnerClassDeserialization() { assertEquals(3, c.value2); } - private static class Parent { - @SuppressWarnings("unused") - int value1 = 1; - private class Child { - int value2 = 2; - } - } - - private static class ArrayOfArrays { - private final BagOfPrimitives[][] elements; - public ArrayOfArrays() { - elements = new BagOfPrimitives[3][2]; - for (int i = 0; i < elements.length; ++i) { - BagOfPrimitives[] row = elements[i]; - for (int j = 0; j < row.length; ++j) { - row[j] = new BagOfPrimitives(i+j, i*j, false, i+"_"+j); - } - } - } - public String getExpectedJson() { - StringBuilder sb = new StringBuilder("{\"elements\":["); - boolean first = true; - for (BagOfPrimitives[] row : elements) { - if (first) { - first = false; - } else { - sb.append(","); - } - boolean firstOfRow = true; - sb.append("["); - for (BagOfPrimitives element : row) { - if (firstOfRow) { - firstOfRow = false; - } else { - sb.append(","); - } - sb.append(element.getExpectedJson()); - } - sb.append("]"); - } - sb.append("]}"); - return sb.toString(); - } - } - - private static class ClassWithPrivateNoArgsConstructor { - public int a; - private ClassWithPrivateNoArgsConstructor() { - a = 10; - } - } - /** * In response to Issue 41 http://code.google.com/p/google-gson/issues/detail?id=41 */ @@ -474,12 +403,6 @@ public void testStringFieldWithEmptyValueDeserialization() { assertEquals("", target.c); } - private static class ClassWithEmptyStringFields { - String a = ""; - String b = ""; - String c = ""; - } - public void testJsonObjectSerialization() { Gson gson = new GsonBuilder().serializeNulls().create(); JsonObject obj = new JsonObject(); @@ -508,16 +431,6 @@ public void testSingletonLists() { gson.fromJson(gson.toJson(product), Product.class); } - static final class Department { - public String name = "abc"; - public String code = "123"; - } - - static final class Product { - private List attributes = new ArrayList<>(); - private List departments = new ArrayList<>(); - } - // http://code.google.com/p/google-gson/issues/detail?id=270 public void testDateAsMapObjectField() { HasObjectMap a = new HasObjectMap(); @@ -529,10 +442,6 @@ public void testDateAsMapObjectField() { } } - static class HasObjectMap { - Map map = new HashMap<>(); - } - /** * Tests serialization of a class with {@code static} field. * @@ -589,14 +498,6 @@ public void testStaticFieldDeserialization() { } } - static class ClassWithStaticField { - static String s = "initial"; - } - - static class ClassWithStaticFinalField { - static final String s = "initial"; - } - public void testThrowingDefaultConstructor() { try { gson.fromJson("{}", ClassWithThrowingConstructor.class); @@ -610,6 +511,108 @@ public void testThrowingDefaultConstructor() { } } + private static class Subclass extends Superclass1 { + } + + private static class Superclass1 extends Superclass2 { + @SuppressWarnings("unused") + String s; + } + + private static class Superclass2 { + @SuppressWarnings("unused") + String s; + } + + private static class ClassWithCollectionField { + Collection children = new ArrayList<>(); + } + + private static class ClassWithObjectField { + @SuppressWarnings("unused") + Object member; + } + + private static class Parent { + @SuppressWarnings("unused") + int value1 = 1; + private class Child { + int value2 = 2; + } + } + + private static class ArrayOfArrays { + private final BagOfPrimitives[][] elements; + public ArrayOfArrays() { + elements = new BagOfPrimitives[3][2]; + for (int i = 0; i < elements.length; ++i) { + BagOfPrimitives[] row = elements[i]; + for (int j = 0; j < row.length; ++j) { + row[j] = new BagOfPrimitives(i+j, i*j, false, i+"_"+j); + } + } + } + public String getExpectedJson() { + StringBuilder sb = new StringBuilder("{\"elements\":["); + boolean first = true; + for (BagOfPrimitives[] row : elements) { + if (first) { + first = false; + } else { + sb.append(","); + } + boolean firstOfRow = true; + sb.append("["); + for (BagOfPrimitives element : row) { + if (firstOfRow) { + firstOfRow = false; + } else { + sb.append(","); + } + sb.append(element.getExpectedJson()); + } + sb.append("]"); + } + sb.append("]}"); + return sb.toString(); + } + } + + private static class ClassWithPrivateNoArgsConstructor { + public int a; + private ClassWithPrivateNoArgsConstructor() { + a = 10; + } + } + + private static class ClassWithEmptyStringFields { + String a = ""; + String b = ""; + String c = ""; + } + + static final class Department { + public String name = "abc"; + public String code = "123"; + } + + static final class Product { + private List attributes = new ArrayList<>(); + private List departments = new ArrayList<>(); + } + + static class HasObjectMap { + Map map = new HashMap<>(); + } + + static class ClassWithStaticField { + static String s = "initial"; + } + + static class ClassWithStaticFinalField { + static final String s = "initial"; + } + static class ClassWithThrowingConstructor { static final RuntimeException thrownException = new RuntimeException("Custom exception"); diff --git a/gson/src/test/java/com/google/gson/functional/ParameterizedTypesTest.java b/gson/src/test/java/com/google/gson/functional/ParameterizedTypesTest.java index e616874620..d30f58db6b 100644 --- a/gson/src/test/java/com/google/gson/functional/ParameterizedTypesTest.java +++ b/gson/src/test/java/com/google/gson/functional/ParameterizedTypesTest.java @@ -50,6 +50,19 @@ public class ParameterizedTypesTest { private Gson gson; + @SuppressWarnings("varargs") + @SafeVarargs + private static T[] arrayOf(T... args) { + return args; + } + + private static void assertCorrectlyDeserialized(Object object) { + @SuppressWarnings("unchecked") + List list = (List) object; + assertEquals(1, list.size()); + assertEquals(4, list.get(0).q); + } + @Before public void setUp() { gson = new Gson(); @@ -166,12 +179,6 @@ public void testParameterizedTypeWithReaderDeserialization() throws Exception { assertEquals(expected, actual); } - @SuppressWarnings("varargs") - @SafeVarargs - private static T[] arrayOf(T... args) { - return args; - } - @Test public void testVariableTypeFieldsAndGenericArraysSerialization() throws Exception { Integer obj = 0; @@ -276,6 +283,64 @@ public void testParameterizedTypeGenericArraysDeserialization() throws Exception assertEquals(objAfterDeserialization.getExpectedJson(), json); } + @Test + public void testDeepParameterizedTypeSerialization() { + Amount amount = new Amount<>(); + String json = gson.toJson(amount); + assertTrue(json.contains("value")); + assertTrue(json.contains("30")); + } + + @Test + public void testDeepParameterizedTypeDeserialization() { + String json = "{value:30}"; + Type type = new TypeToken>() {}.getType(); + Amount amount = gson.fromJson(json, type); + assertEquals(30, amount.value); + } + + @Test + public void testGsonFromJsonTypeToken() { + TypeToken> typeToken = new TypeToken>() {}; + Type type = typeToken.getType(); + + { + JsonObject jsonObject = new JsonObject(); + jsonObject.addProperty("q", 4); + JsonArray jsonArray = new JsonArray(); + jsonArray.add(jsonObject); + + assertCorrectlyDeserialized(gson.fromJson(jsonArray, typeToken)); + assertCorrectlyDeserialized(gson.fromJson(jsonArray, type)); + } + + String json = "[{\"q\":4}]"; + + { + assertCorrectlyDeserialized(gson.fromJson(json, typeToken)); + assertCorrectlyDeserialized(gson.fromJson(json, type)); + } + + { + assertCorrectlyDeserialized(gson.fromJson(new StringReader(json), typeToken)); + assertCorrectlyDeserialized(gson.fromJson(new StringReader(json), type)); + } + + { + JsonReader reader = new JsonReader(new StringReader(json)); + assertCorrectlyDeserialized(gson.fromJson(reader, typeToken)); + + reader = new JsonReader(new StringReader(json)); + assertCorrectlyDeserialized(gson.fromJson(reader, type)); + } + } + private interface Measurable { + } + private interface Field { + } + private interface Immutable { + } + /** * An test object that has fields that are type variables. * @@ -486,16 +551,12 @@ private static class Quantity { @SuppressWarnings("unused") int q = 10; } + // End: tests to reproduce issue 103 + private static class MyQuantity extends Quantity { @SuppressWarnings("unused") int q2 = 20; } - private interface Measurable { - } - private interface Field { - } - private interface Immutable { - } public static final class Amount implements Measurable, Field>, Serializable, Immutable { @@ -503,64 +564,4 @@ public static final class Amount int value = 30; } - - @Test - public void testDeepParameterizedTypeSerialization() { - Amount amount = new Amount<>(); - String json = gson.toJson(amount); - assertTrue(json.contains("value")); - assertTrue(json.contains("30")); - } - - @Test - public void testDeepParameterizedTypeDeserialization() { - String json = "{value:30}"; - Type type = new TypeToken>() {}.getType(); - Amount amount = gson.fromJson(json, type); - assertEquals(30, amount.value); - } - // End: tests to reproduce issue 103 - - private static void assertCorrectlyDeserialized(Object object) { - @SuppressWarnings("unchecked") - List list = (List) object; - assertEquals(1, list.size()); - assertEquals(4, list.get(0).q); - } - - @Test - public void testGsonFromJsonTypeToken() { - TypeToken> typeToken = new TypeToken>() {}; - Type type = typeToken.getType(); - - { - JsonObject jsonObject = new JsonObject(); - jsonObject.addProperty("q", 4); - JsonArray jsonArray = new JsonArray(); - jsonArray.add(jsonObject); - - assertCorrectlyDeserialized(gson.fromJson(jsonArray, typeToken)); - assertCorrectlyDeserialized(gson.fromJson(jsonArray, type)); - } - - String json = "[{\"q\":4}]"; - - { - assertCorrectlyDeserialized(gson.fromJson(json, typeToken)); - assertCorrectlyDeserialized(gson.fromJson(json, type)); - } - - { - assertCorrectlyDeserialized(gson.fromJson(new StringReader(json), typeToken)); - assertCorrectlyDeserialized(gson.fromJson(new StringReader(json), type)); - } - - { - JsonReader reader = new JsonReader(new StringReader(json)); - assertCorrectlyDeserialized(gson.fromJson(reader, typeToken)); - - reader = new JsonReader(new StringReader(json)); - assertCorrectlyDeserialized(gson.fromJson(reader, type)); - } - } } diff --git a/gson/src/test/java/com/google/gson/functional/PrettyPrintingTest.java b/gson/src/test/java/com/google/gson/functional/PrettyPrintingTest.java index 58827b4168..4b4abb9de7 100644 --- a/gson/src/test/java/com/google/gson/functional/PrettyPrintingTest.java +++ b/gson/src/test/java/com/google/gson/functional/PrettyPrintingTest.java @@ -15,21 +15,19 @@ */ package com.google.gson.functional; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.common.TestTypes.ArrayOfObjects; +import com.google.gson.common.TestTypes.BagOfPrimitives; +import com.google.gson.reflect.TypeToken; import java.lang.reflect.Type; import java.util.Arrays; import java.util.LinkedHashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; - import junit.framework.TestCase; -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; -import com.google.gson.common.TestTypes.ArrayOfObjects; -import com.google.gson.common.TestTypes.BagOfPrimitives; -import com.google.gson.reflect.TypeToken; - /** * Functional tests for pretty printing option. * @@ -103,12 +101,6 @@ public void testEmptyMapField() { assertTrue(json.contains("{\n \"map\": {},\n \"value\": 2\n}")); } - @SuppressWarnings("unused") - private static class ClassWithMap { - Map map; - int value = 2; - } - public void testMultipleArrays() { int[][][] ints = new int[][][] { { { 1 }, { 2 } } }; String json = gson.toJson(ints); @@ -120,4 +112,10 @@ private void print(String msg) { System.out.println(msg); } } + + @SuppressWarnings("unused") + private static class ClassWithMap { + Map map; + int value = 2; + } } diff --git a/gson/src/test/java/com/google/gson/functional/PrimitiveCharacterTest.java b/gson/src/test/java/com/google/gson/functional/PrimitiveCharacterTest.java index 69ff1f3f2a..fdb92c9b32 100644 --- a/gson/src/test/java/com/google/gson/functional/PrimitiveCharacterTest.java +++ b/gson/src/test/java/com/google/gson/functional/PrimitiveCharacterTest.java @@ -16,9 +16,8 @@ package com.google.gson.functional; -import junit.framework.TestCase; - import com.google.gson.Gson; +import junit.framework.TestCase; /** * Functional tests for Java Character values. diff --git a/gson/src/test/java/com/google/gson/functional/PrimitiveTest.java b/gson/src/test/java/com/google/gson/functional/PrimitiveTest.java index c4c25f0072..54c6dc3435 100644 --- a/gson/src/test/java/com/google/gson/functional/PrimitiveTest.java +++ b/gson/src/test/java/com/google/gson/functional/PrimitiveTest.java @@ -714,10 +714,6 @@ public void testDeserializePrimitiveWrapperAsObjectField() { assertEquals(10, target.i.intValue()); } - private static class ClassWithIntegerField { - Integer i; - } - public void testPrimitiveClassLiteral() { assertEquals(1, gson.fromJson("1", int.class).intValue()); assertEquals(1, gson.fromJson(new StringReader("1"), int.class).intValue()); @@ -957,4 +953,8 @@ public void testStringsAsBooleans() { assertEquals(Arrays.asList(true, false, true, false, false), gson.>fromJson(json, new TypeToken>() {}.getType())); } + + private static class ClassWithIntegerField { + Integer i; + } } diff --git a/gson/src/test/java/com/google/gson/functional/PrintFormattingTest.java b/gson/src/test/java/com/google/gson/functional/PrintFormattingTest.java index 6801ba00ca..873ccb2294 100644 --- a/gson/src/test/java/com/google/gson/functional/PrintFormattingTest.java +++ b/gson/src/test/java/com/google/gson/functional/PrintFormattingTest.java @@ -37,6 +37,12 @@ public class PrintFormattingTest extends TestCase { private Gson gson; + private static void assertContainsNoWhiteSpace(String str) { + for (char c : str.toCharArray()) { + assertFalse(Character.isWhitespace(c)); + } + } + @Override protected void setUp() throws Exception { super.setUp(); @@ -72,10 +78,4 @@ public void testJsonObjectWithNullValuesSerialized() { assertTrue(json.contains("field1")); assertTrue(json.contains("field2")); } - - private static void assertContainsNoWhiteSpace(String str) { - for (char c : str.toCharArray()) { - assertFalse(Character.isWhitespace(c)); - } - } } diff --git a/gson/src/test/java/com/google/gson/functional/RawSerializationTest.java b/gson/src/test/java/com/google/gson/functional/RawSerializationTest.java index 7876999d09..117d719f6d 100644 --- a/gson/src/test/java/com/google/gson/functional/RawSerializationTest.java +++ b/gson/src/test/java/com/google/gson/functional/RawSerializationTest.java @@ -15,14 +15,12 @@ */ package com.google.gson.functional; +import com.google.gson.Gson; +import com.google.gson.reflect.TypeToken; import java.util.Arrays; import java.util.Collection; - import junit.framework.TestCase; -import com.google.gson.Gson; -import com.google.gson.reflect.TypeToken; - /** * Unit tests to validate serialization of parameterized types without explicit types * diff --git a/gson/src/test/java/com/google/gson/functional/ReflectionAccessFilterTest.java b/gson/src/test/java/com/google/gson/functional/ReflectionAccessFilterTest.java index 6c9ab44945..09ba3791a8 100644 --- a/gson/src/test/java/com/google/gson/functional/ReflectionAccessFilterTest.java +++ b/gson/src/test/java/com/google/gson/functional/ReflectionAccessFilterTest.java @@ -28,18 +28,6 @@ import org.junit.Test; public class ReflectionAccessFilterTest { - // Reader has protected `lock` field which cannot be accessed - private static class ClassExtendingJdkClass extends Reader { - @Override - public int read(char[] cbuf, int off, int len) throws IOException { - return 0; - } - - @Override - public void close() throws IOException { - } - } - @Test public void testBlockInaccessibleJava() throws ReflectiveOperationException { Gson gson = new GsonBuilder() @@ -132,11 +120,6 @@ public void testBlockAllJavaExtendingJdkClass() { } } - private static class ClassWithStaticField { - @SuppressWarnings("unused") - private static int i = 1; - } - @Test public void testBlockInaccessibleStaticField() { Gson gson = new GsonBuilder() @@ -163,17 +146,6 @@ public void testBlockInaccessibleStaticField() { } } - private static class SuperTestClass { - } - private static class SubTestClass extends SuperTestClass { - @SuppressWarnings("unused") - public int i = 1; - } - private static class OtherClass { - @SuppressWarnings("unused") - public int i = 2; - } - @Test public void testDelegation() { Gson gson = new GsonBuilder() @@ -213,13 +185,6 @@ public void testDelegation() { assertEquals("{\"i\":2}", json); } - private static class ClassWithPrivateField { - @SuppressWarnings("unused") - private int i = 1; - } - private static class ExtendingClassWithPrivateField extends ClassWithPrivateField { - } - @Test public void testAllowForSupertype() { Gson gson = new GsonBuilder() @@ -258,11 +223,6 @@ public void testAllowForSupertype() { assertEquals("{\"i\":1}", json); } - private static class ClassWithPrivateNoArgsConstructor { - private ClassWithPrivateNoArgsConstructor() { - } - } - @Test public void testInaccessibleNoArgsConstructor() { Gson gson = new GsonBuilder() @@ -286,14 +246,6 @@ public void testInaccessibleNoArgsConstructor() { } } - private static class ClassWithoutNoArgsConstructor { - public String s; - - public ClassWithoutNoArgsConstructor(String s) { - this.s = s; - } - } - @Test public void testClassWithoutNoArgsConstructor() { GsonBuilder gsonBuilder = new GsonBuilder() @@ -438,4 +390,55 @@ public void testBlockInaccessibleInterface() { ); } } + + // Reader has protected `lock` field which cannot be accessed + private static class ClassExtendingJdkClass extends Reader { + @Override + public int read(char[] cbuf, int off, int len) throws IOException { + return 0; + } + + @Override + public void close() throws IOException { + } + } + + private static class ClassWithStaticField { + @SuppressWarnings("unused") + private static int i = 1; + } + + private static class SuperTestClass { + } + + private static class SubTestClass extends SuperTestClass { + @SuppressWarnings("unused") + public int i = 1; + } + + private static class OtherClass { + @SuppressWarnings("unused") + public int i = 2; + } + + private static class ClassWithPrivateField { + @SuppressWarnings("unused") + private int i = 1; + } + + private static class ExtendingClassWithPrivateField extends ClassWithPrivateField { + } + + private static class ClassWithPrivateNoArgsConstructor { + private ClassWithPrivateNoArgsConstructor() { + } + } + + private static class ClassWithoutNoArgsConstructor { + public String s; + + public ClassWithoutNoArgsConstructor(String s) { + this.s = s; + } + } } diff --git a/gson/src/test/java/com/google/gson/functional/ReflectionAccessTest.java b/gson/src/test/java/com/google/gson/functional/ReflectionAccessTest.java index 02649c5f87..a1725cb17d 100644 --- a/gson/src/test/java/com/google/gson/functional/ReflectionAccessTest.java +++ b/gson/src/test/java/com/google/gson/functional/ReflectionAccessTest.java @@ -22,14 +22,6 @@ import org.junit.Test; public class ReflectionAccessTest { - @SuppressWarnings("unused") - private static class ClassWithPrivateMembers { - private String s; - - private ClassWithPrivateMembers() { - } - } - private static Class loadClassWithDifferentClassLoader(Class c) throws Exception { URL url = c.getProtectionDomain().getCodeSource().getLocation(); URLClassLoader classLoader = new URLClassLoader(new URL[] { url }, null); @@ -124,4 +116,12 @@ public void testSerializeInternalImplementationObject() { )); } } + + @SuppressWarnings("unused") + private static class ClassWithPrivateMembers { + private String s; + + private ClassWithPrivateMembers() { + } + } } diff --git a/gson/src/test/java/com/google/gson/functional/ReusedTypeVariablesFullyResolveTest.java b/gson/src/test/java/com/google/gson/functional/ReusedTypeVariablesFullyResolveTest.java index 10c7c6db1d..f652bb7695 100644 --- a/gson/src/test/java/com/google/gson/functional/ReusedTypeVariablesFullyResolveTest.java +++ b/gson/src/test/java/com/google/gson/functional/ReusedTypeVariablesFullyResolveTest.java @@ -6,11 +6,11 @@ import com.google.gson.Gson; import com.google.gson.GsonBuilder; -import org.junit.Before; -import org.junit.Test; import java.util.Collection; import java.util.Iterator; import java.util.Set; +import org.junit.Before; +import org.junit.Test; /** * This test covers the scenario described in #1390 where a type variable needs to be used diff --git a/gson/src/test/java/com/google/gson/functional/RuntimeTypeAdapterFactoryFunctionalTest.java b/gson/src/test/java/com/google/gson/functional/RuntimeTypeAdapterFactoryFunctionalTest.java index 0c6bd07a5a..4f1d7a87a4 100644 --- a/gson/src/test/java/com/google/gson/functional/RuntimeTypeAdapterFactoryFunctionalTest.java +++ b/gson/src/test/java/com/google/gson/functional/RuntimeTypeAdapterFactoryFunctionalTest.java @@ -15,12 +15,6 @@ */ package com.google.gson.functional; -import java.io.IOException; -import java.util.LinkedHashMap; -import java.util.Map; - -import junit.framework.TestCase; - import com.google.gson.Gson; import com.google.gson.JsonElement; import com.google.gson.JsonObject; @@ -33,6 +27,10 @@ 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.LinkedHashMap; +import java.util.Map; +import junit.framework.TestCase; /** * Functional tests for the RuntimeTypeAdapterFactory feature in extras. @@ -58,6 +56,10 @@ public void testSubclassesAutomaticallySerialized() throws Exception { assertEquals(ShapeType.SQUARE, shape.type); } + public enum ShapeType { + SQUARE, CIRCLE + } + @JsonAdapter(Shape.JsonAdapterFactory.class) static class Shape { final ShapeType type; @@ -71,10 +73,6 @@ public JsonAdapterFactory() { } } - public enum ShapeType { - SQUARE, CIRCLE - } - private static final class Circle extends Shape { final int radius; Circle(int radius) { super(ShapeType.CIRCLE); this.radius = radius; } diff --git a/gson/src/test/java/com/google/gson/functional/SecurityTest.java b/gson/src/test/java/com/google/gson/functional/SecurityTest.java index aa1c2d4517..060e35759b 100644 --- a/gson/src/test/java/com/google/gson/functional/SecurityTest.java +++ b/gson/src/test/java/com/google/gson/functional/SecurityTest.java @@ -19,7 +19,6 @@ import com.google.gson.Gson; import com.google.gson.GsonBuilder; import com.google.gson.common.TestTypes.BagOfPrimitives; - import junit.framework.TestCase; /** diff --git a/gson/src/test/java/com/google/gson/functional/SerializedNameTest.java b/gson/src/test/java/com/google/gson/functional/SerializedNameTest.java index 0314ae0af7..d76a5ad9ff 100644 --- a/gson/src/test/java/com/google/gson/functional/SerializedNameTest.java +++ b/gson/src/test/java/com/google/gson/functional/SerializedNameTest.java @@ -17,7 +17,6 @@ import com.google.gson.Gson; import com.google.gson.annotations.SerializedName; - import junit.framework.TestCase; public final class SerializedNameTest extends TestCase { diff --git a/gson/src/test/java/com/google/gson/functional/StringTest.java b/gson/src/test/java/com/google/gson/functional/StringTest.java index 7dcf6f0f5d..2e582711ec 100644 --- a/gson/src/test/java/com/google/gson/functional/StringTest.java +++ b/gson/src/test/java/com/google/gson/functional/StringTest.java @@ -1,7 +1,6 @@ package com.google.gson.functional; import com.google.gson.Gson; - import junit.framework.TestCase; /** diff --git a/gson/src/test/java/com/google/gson/functional/ToNumberPolicyFunctionalTest.java b/gson/src/test/java/com/google/gson/functional/ToNumberPolicyFunctionalTest.java index 3e261d5643..d56c5b5a23 100644 --- a/gson/src/test/java/com/google/gson/functional/ToNumberPolicyFunctionalTest.java +++ b/gson/src/test/java/com/google/gson/functional/ToNumberPolicyFunctionalTest.java @@ -16,12 +16,6 @@ package com.google.gson.functional; -import java.lang.reflect.Type; -import java.math.BigDecimal; -import java.util.Arrays; -import java.util.Collection; -import java.util.LinkedList; -import java.util.List; import com.google.gson.Gson; import com.google.gson.GsonBuilder; import com.google.gson.ToNumberPolicy; @@ -29,6 +23,12 @@ import com.google.gson.internal.LazilyParsedNumber; import com.google.gson.reflect.TypeToken; import com.google.gson.stream.JsonReader; +import java.lang.reflect.Type; +import java.math.BigDecimal; +import java.util.Arrays; +import java.util.Collection; +import java.util.LinkedList; +import java.util.List; import junit.framework.TestCase; public class ToNumberPolicyFunctionalTest extends TestCase { diff --git a/gson/src/test/java/com/google/gson/functional/TypeAdapterPrecedenceTest.java b/gson/src/test/java/com/google/gson/functional/TypeAdapterPrecedenceTest.java index 5f881530dc..ec3732e531 100644 --- a/gson/src/test/java/com/google/gson/functional/TypeAdapterPrecedenceTest.java +++ b/gson/src/test/java/com/google/gson/functional/TypeAdapterPrecedenceTest.java @@ -113,13 +113,6 @@ public void testNonstreamingHierarchicalFollowedByNonstreaming() { assertEquals("foo via non hierarchical", gson.fromJson("foo", Foo.class).name); } - private static class Foo { - final String name; - private Foo(String name) { - this.name = name; - } - } - private JsonSerializer newSerializer(final String name) { return new JsonSerializer() { @Override @@ -148,4 +141,11 @@ private TypeAdapter newTypeAdapter(final String name) { } }; } + + private static class Foo { + final String name; + private Foo(String name) { + this.name = name; + } + } } diff --git a/gson/src/test/java/com/google/gson/functional/TypeAdapterRuntimeTypeWrapperTest.java b/gson/src/test/java/com/google/gson/functional/TypeAdapterRuntimeTypeWrapperTest.java index 73a0101243..b03f07a93a 100644 --- a/gson/src/test/java/com/google/gson/functional/TypeAdapterRuntimeTypeWrapperTest.java +++ b/gson/src/test/java/com/google/gson/functional/TypeAdapterRuntimeTypeWrapperTest.java @@ -18,23 +18,6 @@ import org.junit.Test; public class TypeAdapterRuntimeTypeWrapperTest { - private static class Base { - } - private static class Subclass extends Base { - @SuppressWarnings("unused") - String f = "test"; - } - private static class Container { - @SuppressWarnings("unused") - Base b = new Subclass(); - } - private static class Deserializer implements JsonDeserializer { - @Override - public Base deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) { - throw new AssertionError("not needed for this test"); - } - } - /** * When custom {@link JsonSerializer} is registered for Base should * prefer that over reflective adapter for Subclass for serialization. @@ -163,20 +146,6 @@ public JsonElement serialize(Base src, Type typeOfSrc, JsonSerializationContext assertEquals("{\"b\":{\"f\":\"test\"}}", json); } - private static class CyclicBase { - @SuppressWarnings("unused") - CyclicBase f; - } - - private static class CyclicSub extends CyclicBase { - @SuppressWarnings("unused") - int i; - - public CyclicSub(int i) { - this.i = i; - } - } - /** * Tests behavior when the type of a field refers to a type whose adapter is * currently in the process of being created. For these cases {@link Gson} @@ -190,4 +159,38 @@ public void testGsonFutureAdapter() { String json = new Gson().toJson(b); assertEquals("{\"f\":{\"i\":2}}", json); } + + private static class Base { + } + + private static class Subclass extends Base { + @SuppressWarnings("unused") + String f = "test"; + } + + private static class Container { + @SuppressWarnings("unused") + Base b = new Subclass(); + } + + private static class Deserializer implements JsonDeserializer { + @Override + public Base deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) { + throw new AssertionError("not needed for this test"); + } + } + + private static class CyclicBase { + @SuppressWarnings("unused") + CyclicBase f; + } + + private static class CyclicSub extends CyclicBase { + @SuppressWarnings("unused") + int i; + + public CyclicSub(int i) { + this.i = i; + } + } } diff --git a/gson/src/test/java/com/google/gson/functional/TypeVariableTest.java b/gson/src/test/java/com/google/gson/functional/TypeVariableTest.java index f9ef46b3d3..a2d06e93ac 100644 --- a/gson/src/test/java/com/google/gson/functional/TypeVariableTest.java +++ b/gson/src/test/java/com/google/gson/functional/TypeVariableTest.java @@ -100,9 +100,9 @@ public Red(S redField) { @SuppressWarnings("overrides") // for missing hashCode() override public static class Foo extends Red { + public final Map> map = new HashMap<>(); private S someSField; private T someTField; - public final Map> map = new HashMap<>(); public Foo() {} diff --git a/gson/src/test/java/com/google/gson/functional/UncategorizedTest.java b/gson/src/test/java/com/google/gson/functional/UncategorizedTest.java index 0ac0380357..1afad02f57 100644 --- a/gson/src/test/java/com/google/gson/functional/UncategorizedTest.java +++ b/gson/src/test/java/com/google/gson/functional/UncategorizedTest.java @@ -23,14 +23,12 @@ import com.google.gson.JsonParseException; import com.google.gson.common.TestTypes.BagOfPrimitives; import com.google.gson.common.TestTypes.ClassOverridingEquals; - import com.google.gson.reflect.TypeToken; +import java.lang.reflect.Type; import java.util.Arrays; import java.util.List; import junit.framework.TestCase; -import java.lang.reflect.Type; - /** * Functional tests that do not fall neatly into any of the existing classification. * diff --git a/gson/src/test/java/com/google/gson/internal/ConstructorConstructorTest.java b/gson/src/test/java/com/google/gson/internal/ConstructorConstructorTest.java index ba3c339aa1..392d30e7b4 100644 --- a/gson/src/test/java/com/google/gson/internal/ConstructorConstructorTest.java +++ b/gson/src/test/java/com/google/gson/internal/ConstructorConstructorTest.java @@ -16,12 +16,6 @@ public class ConstructorConstructorTest { Collections.emptyList() ); - private abstract static class AbstractClass { - @SuppressWarnings("unused") - public AbstractClass() { } - } - private interface Interface { } - /** * Verify that ConstructorConstructor does not try to invoke no-arg constructor * of abstract class. @@ -55,4 +49,11 @@ public void testGet_Interface() { ); } } + + private interface Interface { } + + private abstract static class AbstractClass { + @SuppressWarnings("unused") + public AbstractClass() { } + } } diff --git a/gson/src/test/java/com/google/gson/internal/GsonTypesTest.java b/gson/src/test/java/com/google/gson/internal/GsonTypesTest.java index c80700bde4..003357ab46 100644 --- a/gson/src/test/java/com/google/gson/internal/GsonTypesTest.java +++ b/gson/src/test/java/com/google/gson/internal/GsonTypesTest.java @@ -19,11 +19,22 @@ import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.util.List; - import junit.framework.TestCase; public final class GsonTypesTest extends TestCase { + /** + * Given a parameterized type A<B,C>, returns B. If the specified type is not + * a generic type, returns null. + */ + public static Type getFirstTypeArgument(Type type) throws Exception { + if (!(type instanceof ParameterizedType)) return null; + ParameterizedType ptype = (ParameterizedType) type; + Type[] actualTypeArguments = ptype.getActualTypeArguments(); + if (actualTypeArguments.length == 0) return null; + return $Gson$Types.canonicalize(actualTypeArguments[0]); + } + public void testNewParameterizedTypeWithoutOwner() throws Exception { // List. List is a top-level class Type type = $Gson$Types.newParameterizedTypeWithOwner(null, List.class, A.class); @@ -55,20 +66,10 @@ public void testGetFirstTypeArgument() throws Exception { private static final class A { } + private static final class B { } - private static final class C { - } - /** - * Given a parameterized type A<B,C>, returns B. If the specified type is not - * a generic type, returns null. - */ - public static Type getFirstTypeArgument(Type type) throws Exception { - if (!(type instanceof ParameterizedType)) return null; - ParameterizedType ptype = (ParameterizedType) type; - Type[] actualTypeArguments = ptype.getActualTypeArguments(); - if (actualTypeArguments.length == 0) return null; - return $Gson$Types.canonicalize(actualTypeArguments[0]); + private static final class C { } } diff --git a/gson/src/test/java/com/google/gson/internal/LazilyParsedNumberTest.java b/gson/src/test/java/com/google/gson/internal/LazilyParsedNumberTest.java index 75e77bb55f..038604dd23 100644 --- a/gson/src/test/java/com/google/gson/internal/LazilyParsedNumberTest.java +++ b/gson/src/test/java/com/google/gson/internal/LazilyParsedNumberTest.java @@ -21,7 +21,6 @@ import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.math.BigDecimal; - import junit.framework.TestCase; public class LazilyParsedNumberTest extends TestCase { diff --git a/gson/src/test/java/com/google/gson/internal/UnsafeAllocatorInstantiationTest.java b/gson/src/test/java/com/google/gson/internal/UnsafeAllocatorInstantiationTest.java index 54d0a5064b..1aa2f7e3d8 100644 --- a/gson/src/test/java/com/google/gson/internal/UnsafeAllocatorInstantiationTest.java +++ b/gson/src/test/java/com/google/gson/internal/UnsafeAllocatorInstantiationTest.java @@ -23,15 +23,6 @@ */ public final class UnsafeAllocatorInstantiationTest extends TestCase { - public interface Interface { - } - - public static abstract class AbstractClass { - } - - public static class ConcreteClass { - } - /** * Ensure that an {@link AssertionError} is thrown when trying * to instantiate an interface @@ -65,4 +56,13 @@ public void testConcreteClassInstantiation() throws Exception { ConcreteClass instance = UnsafeAllocator.INSTANCE.newInstance(ConcreteClass.class); assertNotNull(instance); } + + public interface Interface { + } + + public static abstract class AbstractClass { + } + + public static class ConcreteClass { + } } diff --git a/gson/src/test/java/com/google/gson/internal/bind/DefaultDateTypeAdapterTest.java b/gson/src/test/java/com/google/gson/internal/bind/DefaultDateTypeAdapterTest.java index 5cc2229ee8..5f46c36240 100644 --- a/gson/src/test/java/com/google/gson/internal/bind/DefaultDateTypeAdapterTest.java +++ b/gson/src/test/java/com/google/gson/internal/bind/DefaultDateTypeAdapterTest.java @@ -16,18 +16,18 @@ package com.google.gson.internal.bind; -import java.io.IOException; -import java.text.DateFormat; -import java.text.SimpleDateFormat; -import java.util.Date; -import java.util.Locale; -import java.util.TimeZone; import com.google.gson.Gson; import com.google.gson.TypeAdapter; import com.google.gson.TypeAdapterFactory; import com.google.gson.internal.JavaVersion; import com.google.gson.internal.bind.DefaultDateTypeAdapter.DateType; import com.google.gson.reflect.TypeToken; +import java.io.IOException; +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.Locale; +import java.util.TimeZone; import junit.framework.TestCase; /** @@ -37,6 +37,27 @@ */ public class DefaultDateTypeAdapterTest extends TestCase { + private static TypeAdapter dateAdapter(TypeAdapterFactory adapterFactory) { + TypeAdapter adapter = adapterFactory.create(new Gson(), TypeToken.get(Date.class)); + assertNotNull(adapter); + return adapter; + } + + private static void assertFormatted(String formatted, TypeAdapterFactory adapterFactory) { + TypeAdapter adapter = dateAdapter(adapterFactory); + assertEquals(toLiteral(formatted), adapter.toJson(new Date(0))); + } + + private static void assertParsed(String date, TypeAdapterFactory adapterFactory) throws IOException { + TypeAdapter adapter = dateAdapter(adapterFactory); + assertEquals(date, new Date(0), adapter.fromJson(toLiteral(date))); + assertEquals("ISO 8601", new Date(0), adapter.fromJson(toLiteral("1970-01-01T00:00:00Z"))); + } + + private static String toLiteral(String s) { + return '"' + s + '"'; + } + public void testFormattingInEnUs() { assertFormattingAlwaysEmitsUsLocale(Locale.US); } @@ -199,25 +220,4 @@ public void testUnexpectedToken() throws Exception { fail("Unexpected token should fail."); } catch (IllegalStateException expected) { } } - - private static TypeAdapter dateAdapter(TypeAdapterFactory adapterFactory) { - TypeAdapter adapter = adapterFactory.create(new Gson(), TypeToken.get(Date.class)); - assertNotNull(adapter); - return adapter; - } - - private static void assertFormatted(String formatted, TypeAdapterFactory adapterFactory) { - TypeAdapter adapter = dateAdapter(adapterFactory); - assertEquals(toLiteral(formatted), adapter.toJson(new Date(0))); - } - - private static void assertParsed(String date, TypeAdapterFactory adapterFactory) throws IOException { - TypeAdapter adapter = dateAdapter(adapterFactory); - assertEquals(date, new Date(0), adapter.fromJson(toLiteral(date))); - assertEquals("ISO 8601", new Date(0), adapter.fromJson(toLiteral("1970-01-01T00:00:00Z"))); - } - - private static String toLiteral(String s) { - return '"' + s + '"'; - } } diff --git a/gson/src/test/java/com/google/gson/internal/bind/Java17ReflectiveTypeAdapterFactoryTest.java b/gson/src/test/java/com/google/gson/internal/bind/Java17ReflectiveTypeAdapterFactoryTest.java index 18984c7b6d..793dd33d43 100644 --- a/gson/src/test/java/com/google/gson/internal/bind/Java17ReflectiveTypeAdapterFactoryTest.java +++ b/gson/src/test/java/com/google/gson/internal/bind/Java17ReflectiveTypeAdapterFactoryTest.java @@ -28,12 +28,6 @@ public void setUp() throws Exception { unixDomainPrincipalClass = Class.forName("jdk.net.UnixDomainPrincipal"); } - // Class for which the normal reflection based adapter is used - private static class DummyClass { - @SuppressWarnings("unused") - public String s; - } - @Test public void testCustomAdapterForRecords() { Gson gson = new Gson(); @@ -63,6 +57,12 @@ public void testSerializeRecords() throws ReflectiveOperationException { assertEquals("{\"user\":\"user\",\"group\":\"group\"}", serialized); } + // Class for which the normal reflection based adapter is used + private static class DummyClass { + @SuppressWarnings("unused") + public String s; + } + private static class PrincipalTypeAdapter extends TypeAdapter { @Override public void write(JsonWriter out, T principal) throws IOException { diff --git a/gson/src/test/java/com/google/gson/internal/bind/RecursiveTypesResolveTest.java b/gson/src/test/java/com/google/gson/internal/bind/RecursiveTypesResolveTest.java index ca068df313..bbd4d3555c 100644 --- a/gson/src/test/java/com/google/gson/internal/bind/RecursiveTypesResolveTest.java +++ b/gson/src/test/java/com/google/gson/internal/bind/RecursiveTypesResolveTest.java @@ -31,15 +31,6 @@ */ public class RecursiveTypesResolveTest extends TestCase { - @SuppressWarnings("unused") - private static class Foo1 { - public Foo2 foo2; - } - @SuppressWarnings("unused") - private static class Foo2 { - public Foo1 foo1; - } - /** * Test simplest case of recursion. */ @@ -74,6 +65,28 @@ public void testSubSupertype() { $Gson$Types.subtypeOf($Gson$Types.supertypeOf(Number.class))); } + public void testRecursiveTypeVariablesResolve1() throws Exception { + @SuppressWarnings("rawtypes") + TypeAdapter adapter = new Gson().getAdapter(TestType.class); + assertNotNull(adapter); + } + + public void testRecursiveTypeVariablesResolve12() throws Exception { + @SuppressWarnings("rawtypes") + TypeAdapter adapter = new Gson().getAdapter(TestType2.class); + assertNotNull(adapter); + } + + @SuppressWarnings("unused") + private static class Foo1 { + public Foo2 foo2; + } + + @SuppressWarnings("unused") + private static class Foo2 { + public Foo1 foo1; + } + /** * Tests for recursion while resolving type variables. */ @@ -87,16 +100,4 @@ private static class TestType { private static class TestType2 { TestType2 superReversedType; } - - public void testRecursiveTypeVariablesResolve1() throws Exception { - @SuppressWarnings("rawtypes") - TypeAdapter adapter = new Gson().getAdapter(TestType.class); - assertNotNull(adapter); - } - - public void testRecursiveTypeVariablesResolve12() throws Exception { - @SuppressWarnings("rawtypes") - TypeAdapter adapter = new Gson().getAdapter(TestType2.class); - assertNotNull(adapter); - } } diff --git a/gson/src/test/java/com/google/gson/internal/sql/SqlTypesGsonTest.java b/gson/src/test/java/com/google/gson/internal/sql/SqlTypesGsonTest.java index 03e2185541..70d55b6787 100644 --- a/gson/src/test/java/com/google/gson/internal/sql/SqlTypesGsonTest.java +++ b/gson/src/test/java/com/google/gson/internal/sql/SqlTypesGsonTest.java @@ -1,16 +1,14 @@ package com.google.gson.internal.sql; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.functional.DefaultTypeAdaptersTest; +import com.google.gson.internal.JavaVersion; import java.sql.Date; import java.sql.Time; import java.sql.Timestamp; import java.util.Locale; import java.util.TimeZone; - -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; -import com.google.gson.functional.DefaultTypeAdaptersTest; -import com.google.gson.internal.JavaVersion; - import junit.framework.TestCase; public class SqlTypesGsonTest extends TestCase { diff --git a/gson/src/test/java/com/google/gson/metrics/PerformanceTest.java b/gson/src/test/java/com/google/gson/metrics/PerformanceTest.java index e0ff065314..33e97e8228 100644 --- a/gson/src/test/java/com/google/gson/metrics/PerformanceTest.java +++ b/gson/src/test/java/com/google/gson/metrics/PerformanceTest.java @@ -20,15 +20,13 @@ import com.google.gson.JsonParseException; import com.google.gson.annotations.Expose; import com.google.gson.reflect.TypeToken; - -import junit.framework.TestCase; - import java.io.StringWriter; import java.lang.reflect.Type; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; +import junit.framework.TestCase; /** * Tests to measure performance for Gson. All tests in this file will be disabled in code. To run @@ -77,37 +75,6 @@ private void parseLongJson(String json) throws JsonParseException { assertTrue(target.stackTrace.contains("Yippie")); } - private static class ExceptionHolder { - public final String message; - public final String stackTrace; - - // For use by Gson - @SuppressWarnings("unused") - private ExceptionHolder() { - this("", ""); - } - public ExceptionHolder(String message, String stackTrace) { - this.message = message; - this.stackTrace = stackTrace; - } - } - - @SuppressWarnings("unused") - private static class CollectionEntry { - final String name; - final String value; - - // For use by Gson - private CollectionEntry() { - this(null, null); - } - - CollectionEntry(String name, String value) { - this.name = name; - this.value = value; - } - } - /** * Created in response to http://code.google.com/p/google-gson/issues/detail?id=96 */ @@ -116,10 +83,10 @@ public void disabled_testLargeCollectionSerialization() { List list = new ArrayList<>(count); for (int i = 0; i < count; ++i) { list.add(new CollectionEntry("name"+i,"value"+i)); - } + } gson.toJson(list); } - + /** * Created in response to http://code.google.com/p/google-gson/issues/detail?id=96 */ @@ -135,14 +102,14 @@ public void disabled_testLargeCollectionDeserialization() { sb.append(','); } sb.append("{name:'name").append(i).append("',value:'value").append(i).append("'}"); - } + } sb.append(']'); String json = sb.toString(); Type collectionType = new TypeToken>(){}.getType(); List list = gson.fromJson(json, collectionType); assertEquals(count, list.size()); } - + /** * Created in response to http://code.google.com/p/google-gson/issues/detail?id=96 */ @@ -182,84 +149,84 @@ public void disabled_testByteArrayDeserialization() { } } -// The tests to measure serialization and deserialization performance of Gson -// Based on the discussion at -// http://groups.google.com/group/google-gson/browse_thread/thread/7a50b17a390dfaeb -// Test results: 10/19/2009 -// Serialize classes avg time: 60 ms -// Deserialized classes avg time: 70 ms -// Serialize exposed classes avg time: 159 ms -// Deserialized exposed classes avg time: 173 ms - public void disabled_testSerializeClasses() { - ClassWithList c = new ClassWithList("str"); - for (int i = 0; i < COLLECTION_SIZE; ++i) { - c.list.add(new ClassWithField("element-" + i)); + ClassWithList c = new ClassWithList("str"); + for (int i = 0; i < COLLECTION_SIZE; ++i) { + c.list.add(new ClassWithField("element-" + i)); } - StringWriter w = new StringWriter(); - long t1 = System.currentTimeMillis(); - for (int i = 0; i < NUM_ITERATIONS; ++i) { - gson.toJson(c, w); - } - long t2 = System.currentTimeMillis(); + StringWriter w = new StringWriter(); + long t1 = System.currentTimeMillis(); + for (int i = 0; i < NUM_ITERATIONS; ++i) { + gson.toJson(c, w); + } + long t2 = System.currentTimeMillis(); long avg = (t2 - t1) / NUM_ITERATIONS; System.out.printf("Serialize classes avg time: %d ms\n", avg); } - + public void disabled_testDeserializeClasses() { String json = buildJsonForClassWithList(); ClassWithList[] target = new ClassWithList[NUM_ITERATIONS]; - long t1 = System.currentTimeMillis(); + long t1 = System.currentTimeMillis(); for (int i = 0; i < NUM_ITERATIONS; ++i) { target[i] = gson.fromJson(json, ClassWithList.class); } - long t2 = System.currentTimeMillis(); + long t2 = System.currentTimeMillis(); long avg = (t2 - t1) / NUM_ITERATIONS; System.out.printf("Deserialize classes avg time: %d ms\n", avg); } + +// The tests to measure serialization and deserialization performance of Gson +// Based on the discussion at +// http://groups.google.com/group/google-gson/browse_thread/thread/7a50b17a390dfaeb +// Test results: 10/19/2009 +// Serialize classes avg time: 60 ms +// Deserialized classes avg time: 70 ms +// Serialize exposed classes avg time: 159 ms +// Deserialized exposed classes avg time: 173 ms public void disabled_testLargeObjectSerializationAndDeserialization() { Map largeObject = new HashMap<>(); for (long l = 0; l < 100000; l++) { largeObject.put("field" + l, l); } - - long t1 = System.currentTimeMillis(); + + long t1 = System.currentTimeMillis(); String json = gson.toJson(largeObject); long t2 = System.currentTimeMillis(); System.out.printf("Large object serialized in: %d ms\n", (t2 - t1)); - t1 = System.currentTimeMillis(); + t1 = System.currentTimeMillis(); gson.fromJson(json, new TypeToken>() {}.getType()); t2 = System.currentTimeMillis(); System.out.printf("Large object deserialized in: %d ms\n", (t2 - t1)); - + } public void disabled_testSerializeExposedClasses() { - ClassWithListOfObjects c1 = new ClassWithListOfObjects("str"); - for (int i1 = 0; i1 < COLLECTION_SIZE; ++i1) { - c1.list.add(new ClassWithExposedField("element-" + i1)); + ClassWithListOfObjects c1 = new ClassWithListOfObjects("str"); + for (int i1 = 0; i1 < COLLECTION_SIZE; ++i1) { + c1.list.add(new ClassWithExposedField("element-" + i1)); } - ClassWithListOfObjects c = c1; - StringWriter w = new StringWriter(); - long t1 = System.currentTimeMillis(); - for (int i = 0; i < NUM_ITERATIONS; ++i) { - gson.toJson(c, w); - } - long t2 = System.currentTimeMillis(); + ClassWithListOfObjects c = c1; + StringWriter w = new StringWriter(); + long t1 = System.currentTimeMillis(); + for (int i = 0; i < NUM_ITERATIONS; ++i) { + gson.toJson(c, w); + } + long t2 = System.currentTimeMillis(); long avg = (t2 - t1) / NUM_ITERATIONS; System.out.printf("Serialize exposed classes avg time: %d ms\n", avg); } - + public void disabled_testDeserializeExposedClasses() { String json = buildJsonForClassWithList(); ClassWithListOfObjects[] target = new ClassWithListOfObjects[NUM_ITERATIONS]; - long t1 = System.currentTimeMillis(); + long t1 = System.currentTimeMillis(); for (int i = 0; i < NUM_ITERATIONS; ++i) { target[i] = gson.fromJson(json, ClassWithListOfObjects.class); } - long t2 = System.currentTimeMillis(); + long t2 = System.currentTimeMillis(); long avg = (t2 - t1) / NUM_ITERATIONS; System.out.printf("Deserialize exposed classes avg time: %d ms\n", avg); } @@ -295,6 +262,37 @@ private String buildJsonForClassWithList() { return json; } + private static class ExceptionHolder { + public final String message; + public final String stackTrace; + + // For use by Gson + @SuppressWarnings("unused") + private ExceptionHolder() { + this("", ""); + } + public ExceptionHolder(String message, String stackTrace) { + this.message = message; + this.stackTrace = stackTrace; + } + } + + @SuppressWarnings("unused") + private static class CollectionEntry { + final String name; + final String value; + + // For use by Gson + private CollectionEntry() { + this(null, null); + } + + CollectionEntry(String name, String value) { + this.name = name; + this.value = value; + } + } + @SuppressWarnings("unused") private static final class ClassWithList { final String field; diff --git a/gson/src/test/java/com/google/gson/reflect/TypeTokenTest.java b/gson/src/test/java/com/google/gson/reflect/TypeTokenTest.java index 55c2e82357..f5b78df074 100644 --- a/gson/src/test/java/com/google/gson/reflect/TypeTokenTest.java +++ b/gson/src/test/java/com/google/gson/reflect/TypeTokenTest.java @@ -204,9 +204,6 @@ public void testParameterizedFactory_Invalid() { } } - private static class CustomTypeToken extends TypeToken { - } - public void testTypeTokenNonAnonymousSubclass() { TypeToken typeToken = new CustomTypeToken(); assertEquals(String.class, typeToken.getRawType()); @@ -255,6 +252,9 @@ public void testTypeTokenRaw() { expected.getMessage()); } } + + private static class CustomTypeToken extends TypeToken { + } } // Have to declare these classes here as top-level classes because otherwise tests for diff --git a/gson/src/test/java/com/google/gson/regression/JsonAdapterNullSafeTest.java b/gson/src/test/java/com/google/gson/regression/JsonAdapterNullSafeTest.java index e327895c43..ed145d1a49 100644 --- a/gson/src/test/java/com/google/gson/regression/JsonAdapterNullSafeTest.java +++ b/gson/src/test/java/com/google/gson/regression/JsonAdapterNullSafeTest.java @@ -20,7 +20,6 @@ import com.google.gson.TypeAdapterFactory; import com.google.gson.annotations.JsonAdapter; import com.google.gson.reflect.TypeToken; - import junit.framework.TestCase; public class JsonAdapterNullSafeTest extends TestCase { diff --git a/gson/src/test/java/com/google/gson/regression/OSGiTest.java b/gson/src/test/java/com/google/gson/regression/OSGiTest.java index c41b12f465..22581011ad 100644 --- a/gson/src/test/java/com/google/gson/regression/OSGiTest.java +++ b/gson/src/test/java/com/google/gson/regression/OSGiTest.java @@ -15,17 +15,23 @@ */ package com.google.gson.regression; -import java.io.InputStream; import java.io.IOException; +import java.io.InputStream; import java.net.URL; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.jar.Manifest; - import junit.framework.TestCase; public class OSGiTest extends TestCase { + private static void assertSubstring(String msg, String wholeText, String subString) { + if (wholeText.contains(subString)) { + return; + } + fail(msg + ". Expecting " + subString + " but was: " + wholeText); + } + public void testComGoogleGsonAnnotationsPackage() throws Exception { Manifest mf = findManifest("com.google.gson"); String importPkg = mf.getMainAttributes().getValue("Import-Package"); @@ -60,11 +66,4 @@ private Manifest findManifest(String pkg) throws IOException { fail("Cannot find " + pkg + " OSGi bundle manifest among: " + urls); return null; } - - private static void assertSubstring(String msg, String wholeText, String subString) { - if (wholeText.contains(subString)) { - return; - } - fail(msg + ". Expecting " + subString + " but was: " + wholeText); - } } diff --git a/gson/src/test/java/com/google/gson/stream/JsonReaderPathTest.java b/gson/src/test/java/com/google/gson/stream/JsonReaderPathTest.java index a755bd8349..f666433bc5 100644 --- a/gson/src/test/java/com/google/gson/stream/JsonReaderPathTest.java +++ b/gson/src/test/java/com/google/gson/stream/JsonReaderPathTest.java @@ -33,6 +33,9 @@ @SuppressWarnings("resource") @RunWith(Parameterized.class) public class JsonReaderPathTest { + @Parameterized.Parameter + public Factory factory; + @Parameterized.Parameters(name = "{0}") public static List parameters() { return Arrays.asList( @@ -41,9 +44,6 @@ public static List parameters() { ); } - @Parameterized.Parameter - public Factory factory; - @Test public void path() throws IOException { JsonReader reader = factory.create("{\"a\":[2,true,false,null,\"b\",{\"c\":\"d\"},[3]]}"); assertEquals("$", reader.getPreviousPath()); diff --git a/metrics/src/main/java/com/google/gson/metrics/ParseBenchmark.java b/metrics/src/main/java/com/google/gson/metrics/ParseBenchmark.java index e6a4530239..f3c4069c30 100644 --- a/metrics/src/main/java/com/google/gson/metrics/ParseBenchmark.java +++ b/metrics/src/main/java/com/google/gson/metrics/ParseBenchmark.java @@ -38,7 +38,6 @@ import java.io.InputStreamReader; import java.io.Reader; import java.io.StringWriter; -import java.lang.reflect.Type; import java.net.URL; import java.text.SimpleDateFormat; import java.util.Date; @@ -57,6 +56,55 @@ public final class ParseBenchmark { @Param Document document; @Param Api api; + private char[] text; + private Parser parser; + + private static File getResourceFile(String path) throws Exception { + URL url = ParseBenchmark.class.getResource(path); + if (url == null) { + throw new IllegalArgumentException("Resource " + path + " does not exist"); + } + File file = new File(url.toURI()); + if (!file.isFile()) { + throw new IllegalArgumentException("Resource " + path + " is not a file"); + } + return file; + } + + private static String resourceToString(String fileName) throws Exception { + ZipFile zipFile = new ZipFile(getResourceFile("/ParseBenchmarkData.zip")); + try { + ZipEntry zipEntry = zipFile.getEntry(fileName); + Reader reader = new InputStreamReader(zipFile.getInputStream(zipEntry)); + char[] buffer = new char[8192]; + StringWriter writer = new StringWriter(); + int count; + while ((count = reader.read(buffer)) != -1) { + writer.write(buffer, 0, count); + } + reader.close(); + return writer.toString(); + + } finally { + zipFile.close(); + } + } + + public static void main(String[] args) throws Exception { + NonUploadingCaliperRunner.run(ParseBenchmark.class, args); + } + + @BeforeExperiment + void setUp() throws Exception { + text = resourceToString(document.name() + ".json").toCharArray(); + parser = api.newParser(); + } + + public void timeParse(int reps) throws Exception { + for (int i = 0; i < reps; i++) { + parser.parse(text, document); + } + } private enum Document { TWEETS(new TypeToken>() {}, new TypeReference>() {}), @@ -106,56 +154,6 @@ private enum Api { abstract Parser newParser(); } - private char[] text; - private Parser parser; - - @BeforeExperiment - void setUp() throws Exception { - text = resourceToString(document.name() + ".json").toCharArray(); - parser = api.newParser(); - } - - public void timeParse(int reps) throws Exception { - for (int i = 0; i < reps; i++) { - parser.parse(text, document); - } - } - - private static File getResourceFile(String path) throws Exception { - URL url = ParseBenchmark.class.getResource(path); - if (url == null) { - throw new IllegalArgumentException("Resource " + path + " does not exist"); - } - File file = new File(url.toURI()); - if (!file.isFile()) { - throw new IllegalArgumentException("Resource " + path + " is not a file"); - } - return file; - } - - private static String resourceToString(String fileName) throws Exception { - ZipFile zipFile = new ZipFile(getResourceFile("/ParseBenchmarkData.zip")); - try { - ZipEntry zipEntry = zipFile.getEntry(fileName); - Reader reader = new InputStreamReader(zipFile.getInputStream(zipEntry)); - char[] buffer = new char[8192]; - StringWriter writer = new StringWriter(); - int count; - while ((count = reader.read(buffer)) != -1) { - writer.write(buffer, 0, count); - } - reader.close(); - return writer.toString(); - - } finally { - zipFile.close(); - } - } - - public static void main(String[] args) throws Exception { - NonUploadingCaliperRunner.run(ParseBenchmark.class, args); - } - interface Parser { void parse(char[] data, Document document) throws Exception; } diff --git a/proto/src/main/java/com/google/gson/protobuf/ProtoTypeAdapter.java b/proto/src/main/java/com/google/gson/protobuf/ProtoTypeAdapter.java index 9aa166fc68..d404637392 100644 --- a/proto/src/main/java/com/google/gson/protobuf/ProtoTypeAdapter.java +++ b/proto/src/main/java/com/google/gson/protobuf/ProtoTypeAdapter.java @@ -71,134 +71,15 @@ */ public class ProtoTypeAdapter implements JsonSerializer, JsonDeserializer { - /** - * Determines how enum values should be serialized. - */ - public enum EnumSerialization { - /** - * Serializes and deserializes enum values using their number. When this is used, custom - * value names set on enums are ignored. - */ - NUMBER, - /** Serializes and deserializes enum values using their name. */ - NAME; - } - - /** - * Builder for {@link ProtoTypeAdapter}s. - */ - public static class Builder { - private final Set> serializedNameExtensions; - private final Set> serializedEnumValueExtensions; - private EnumSerialization enumSerialization; - private CaseFormat protoFormat; - private CaseFormat jsonFormat; - - private Builder(EnumSerialization enumSerialization, CaseFormat fromFieldNameFormat, - CaseFormat toFieldNameFormat) { - this.serializedNameExtensions = new HashSet<>(); - this.serializedEnumValueExtensions = new HashSet<>(); - setEnumSerialization(enumSerialization); - setFieldNameSerializationFormat(fromFieldNameFormat, toFieldNameFormat); - } - - public Builder setEnumSerialization(EnumSerialization enumSerialization) { - this.enumSerialization = requireNonNull(enumSerialization); - return this; - } - - /** - * Sets the field names serialization format. The first parameter defines how to read the format - * of the proto field names you are converting to JSON. The second parameter defines which - * format to use when serializing them. - *

- * For example, if you use the following parameters: {@link CaseFormat#LOWER_UNDERSCORE}, - * {@link CaseFormat#LOWER_CAMEL}, the following conversion will occur: - * - *

{@code
-     * PROTO     <->  JSON
-     * my_field       myField
-     * foo            foo
-     * n__id_ct       nIdCt
-     * }
- */ - public Builder setFieldNameSerializationFormat(CaseFormat fromFieldNameFormat, - CaseFormat toFieldNameFormat) { - this.protoFormat = fromFieldNameFormat; - this.jsonFormat = toFieldNameFormat; - return this; - } - - /** - * Adds a field proto annotation that, when set, overrides the default field name - * serialization/deserialization. For example, if you add the '{@code serialized_name}' - * annotation and you define a field in your proto like the one below: - * - *
-     * string client_app_id = 1 [(serialized_name) = "appId"];
-     * 
- * - * ...the adapter will serialize the field using '{@code appId}' instead of the default ' - * {@code clientAppId}'. This lets you customize the name serialization of any proto field. - */ - public Builder addSerializedNameExtension( - Extension serializedNameExtension) { - serializedNameExtensions.add(requireNonNull(serializedNameExtension)); - return this; - } - - /** - * Adds an enum value proto annotation that, when set, overrides the default enum value - * serialization/deserialization of this adapter. For example, if you add the ' - * {@code serialized_value}' annotation and you define an enum in your proto like the one below: - * - *
-     * enum MyEnum {
-     *   UNKNOWN = 0;
-     *   CLIENT_APP_ID = 1 [(serialized_value) = "APP_ID"];
-     *   TWO = 2 [(serialized_value) = "2"];
-     * }
-     * 
- * - * ...the adapter will serialize the value {@code CLIENT_APP_ID} as "{@code APP_ID}" and the - * value {@code TWO} as "{@code 2}". This works for both serialization and deserialization. - *

- * Note that you need to set the enum serialization of this adapter to - * {@link EnumSerialization#NAME}, otherwise these annotations will be ignored. - */ - public Builder addSerializedEnumValueExtension( - Extension serializedEnumValueExtension) { - serializedEnumValueExtensions.add(requireNonNull(serializedEnumValueExtension)); - return this; - } - - public ProtoTypeAdapter build() { - return new ProtoTypeAdapter(enumSerialization, protoFormat, jsonFormat, - serializedNameExtensions, serializedEnumValueExtensions); - } - } - - /** - * Creates a new {@link ProtoTypeAdapter} builder, defaulting enum serialization to - * {@link EnumSerialization#NAME} and converting field serialization from - * {@link CaseFormat#LOWER_UNDERSCORE} to {@link CaseFormat#LOWER_CAMEL}. - */ - public static Builder newBuilder() { - return new Builder(EnumSerialization.NAME, CaseFormat.LOWER_UNDERSCORE, CaseFormat.LOWER_CAMEL); - } - private static final com.google.protobuf.Descriptors.FieldDescriptor.Type ENUM_TYPE = com.google.protobuf.Descriptors.FieldDescriptor.Type.ENUM; - private static final ConcurrentMap, Method>> mapOfMapOfMethods = new MapMaker().makeMap(); - private final EnumSerialization enumSerialization; private final CaseFormat protoFormat; private final CaseFormat jsonFormat; private final Set> serializedNameExtensions; private final Set> serializedEnumValueExtensions; - private ProtoTypeAdapter(EnumSerialization enumSerialization, CaseFormat protoFormat, CaseFormat jsonFormat, @@ -211,6 +92,34 @@ private ProtoTypeAdapter(EnumSerialization enumSerialization, this.serializedEnumValueExtensions = serializedEnumValueExtensions; } + /** + * Creates a new {@link ProtoTypeAdapter} builder, defaulting enum serialization to + * {@link EnumSerialization#NAME} and converting field serialization from + * {@link CaseFormat#LOWER_UNDERSCORE} to {@link CaseFormat#LOWER_CAMEL}. + */ + public static Builder newBuilder() { + return new Builder(EnumSerialization.NAME, CaseFormat.LOWER_UNDERSCORE, CaseFormat.LOWER_CAMEL); + } + + private static Method getCachedMethod(Class clazz, String methodName, + Class... methodParamTypes) throws NoSuchMethodException { + ConcurrentMap, Method> mapOfMethods = mapOfMapOfMethods.get(methodName); + if (mapOfMethods == null) { + mapOfMethods = new MapMaker().makeMap(); + ConcurrentMap, Method> previous = + mapOfMapOfMethods.putIfAbsent(methodName, mapOfMethods); + mapOfMethods = previous == null ? mapOfMethods : previous; + } + + Method method = mapOfMethods.get(clazz); + if (method == null) { + method = clazz.getMethod(methodName, methodParamTypes); + mapOfMethods.putIfAbsent(clazz, method); + // NB: it doesn't matter which method we return in the event of a race. + } + return method; + } + @Override public JsonElement serialize(Message src, Type typeOfSrc, JsonSerializationContext context) { @@ -393,23 +302,111 @@ private EnumValueDescriptor findValueByNameAndExtension(EnumDescriptor desc, } } - private static Method getCachedMethod(Class clazz, String methodName, - Class... methodParamTypes) throws NoSuchMethodException { - ConcurrentMap, Method> mapOfMethods = mapOfMapOfMethods.get(methodName); - if (mapOfMethods == null) { - mapOfMethods = new MapMaker().makeMap(); - ConcurrentMap, Method> previous = - mapOfMapOfMethods.putIfAbsent(methodName, mapOfMethods); - mapOfMethods = previous == null ? mapOfMethods : previous; + /** + * Determines how enum values should be serialized. + */ + public enum EnumSerialization { + /** + * Serializes and deserializes enum values using their number. When this is used, custom + * value names set on enums are ignored. + */ + NUMBER, + /** Serializes and deserializes enum values using their name. */ + NAME; + } + + /** + * Builder for {@link ProtoTypeAdapter}s. + */ + public static class Builder { + private final Set> serializedNameExtensions; + private final Set> serializedEnumValueExtensions; + private EnumSerialization enumSerialization; + private CaseFormat protoFormat; + private CaseFormat jsonFormat; + + private Builder(EnumSerialization enumSerialization, CaseFormat fromFieldNameFormat, + CaseFormat toFieldNameFormat) { + this.serializedNameExtensions = new HashSet<>(); + this.serializedEnumValueExtensions = new HashSet<>(); + setEnumSerialization(enumSerialization); + setFieldNameSerializationFormat(fromFieldNameFormat, toFieldNameFormat); } - Method method = mapOfMethods.get(clazz); - if (method == null) { - method = clazz.getMethod(methodName, methodParamTypes); - mapOfMethods.putIfAbsent(clazz, method); - // NB: it doesn't matter which method we return in the event of a race. + public Builder setEnumSerialization(EnumSerialization enumSerialization) { + this.enumSerialization = requireNonNull(enumSerialization); + return this; + } + + /** + * Sets the field names serialization format. The first parameter defines how to read the format + * of the proto field names you are converting to JSON. The second parameter defines which + * format to use when serializing them. + *

+ * For example, if you use the following parameters: {@link CaseFormat#LOWER_UNDERSCORE}, + * {@link CaseFormat#LOWER_CAMEL}, the following conversion will occur: + * + *

{@code
+     * PROTO     <->  JSON
+     * my_field       myField
+     * foo            foo
+     * n__id_ct       nIdCt
+     * }
+ */ + public Builder setFieldNameSerializationFormat(CaseFormat fromFieldNameFormat, + CaseFormat toFieldNameFormat) { + this.protoFormat = fromFieldNameFormat; + this.jsonFormat = toFieldNameFormat; + return this; + } + + /** + * Adds a field proto annotation that, when set, overrides the default field name + * serialization/deserialization. For example, if you add the '{@code serialized_name}' + * annotation and you define a field in your proto like the one below: + * + *
+     * string client_app_id = 1 [(serialized_name) = "appId"];
+     * 
+ * + * ...the adapter will serialize the field using '{@code appId}' instead of the default ' + * {@code clientAppId}'. This lets you customize the name serialization of any proto field. + */ + public Builder addSerializedNameExtension( + Extension serializedNameExtension) { + serializedNameExtensions.add(requireNonNull(serializedNameExtension)); + return this; + } + + /** + * Adds an enum value proto annotation that, when set, overrides the default enum value + * serialization/deserialization of this adapter. For example, if you add the ' + * {@code serialized_value}' annotation and you define an enum in your proto like the one below: + * + *
+     * enum MyEnum {
+     *   UNKNOWN = 0;
+     *   CLIENT_APP_ID = 1 [(serialized_value) = "APP_ID"];
+     *   TWO = 2 [(serialized_value) = "2"];
+     * }
+     * 
+ * + * ...the adapter will serialize the value {@code CLIENT_APP_ID} as "{@code APP_ID}" and the + * value {@code TWO} as "{@code 2}". This works for both serialization and deserialization. + *

+ * Note that you need to set the enum serialization of this adapter to + * {@link EnumSerialization#NAME}, otherwise these annotations will be ignored. + */ + public Builder addSerializedEnumValueExtension( + Extension serializedEnumValueExtension) { + serializedEnumValueExtensions.add(requireNonNull(serializedEnumValueExtension)); + return this; + } + + public ProtoTypeAdapter build() { + return new ProtoTypeAdapter(enumSerialization, protoFormat, jsonFormat, + serializedNameExtensions, serializedEnumValueExtensions); } - return method; } }