Merge branch 'master' into optional-sql
This commit is contained in:
commit
69173b02ea
6
.github/dependabot.yml
vendored
Normal file
6
.github/dependabot.yml
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: "maven"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "daily"
|
16
CHANGELOG.md
16
CHANGELOG.md
@ -1,6 +1,22 @@
|
||||
Change Log
|
||||
==========
|
||||
|
||||
## Version 2.8.8
|
||||
|
||||
* Fixed issue with recursive types (#1390).
|
||||
* Better behaviour with Java 9+ and `Unsafe` if there is a security manager (#1712).
|
||||
* `EnumTypeAdapter` now works better when ProGuard has obfuscated enum fields (#1495).
|
||||
|
||||
## Version 2.8.7
|
||||
|
||||
* Fixed `ISO8601UtilsTest` failing on systems with UTC+X.
|
||||
* Improved javadoc for `JsonStreamParser`.
|
||||
* Updated proguard.cfg (#1693).
|
||||
* Fixed `IllegalStateException` in `JsonTreeWriter` (#1592).
|
||||
* Added `JsonArray.isEmpty()` (#1640).
|
||||
* Added new test cases (#1638).
|
||||
* Fixed OSGi metadata generation to work on JavaSE < 9 (#1603).
|
||||
|
||||
## Version 2.8.6
|
||||
_2019-10-04_ [GitHub Diff](https://github.com/google/gson/compare/gson-parent-2.8.5...gson-parent-2.8.6)
|
||||
* Added static methods `JsonParser.parseString` and `JsonParser.parseReader` and deprecated instance method `JsonParser.parse`
|
||||
|
@ -17,7 +17,7 @@ There are a few open-source projects that can convert Java objects to JSON. Howe
|
||||
Gradle:
|
||||
```gradle
|
||||
dependencies {
|
||||
implementation 'com.google.code.gson:gson:2.8.6'
|
||||
implementation 'com.google.code.gson:gson:2.8.8'
|
||||
}
|
||||
```
|
||||
|
||||
@ -26,7 +26,7 @@ Maven:
|
||||
<dependency>
|
||||
<groupId>com.google.code.gson</groupId>
|
||||
<artifactId>gson</artifactId>
|
||||
<version>2.8.6</version>
|
||||
<version>2.8.8</version>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
|
@ -74,7 +74,7 @@ The Gson instance does not maintain any state while invoking Json operations. So
|
||||
## <a name="TOC-Gson-With-Gradle"></a>Using Gson with Gradle/Android
|
||||
```
|
||||
dependencies {
|
||||
implementation 'com.google.code.gson:gson:2.8.6'
|
||||
implementation 'com.google.code.gson:gson:2.8.8'
|
||||
}
|
||||
```
|
||||
## <a name="TOC-Gson-With-Maven"></a>Using Gson with Maven
|
||||
@ -86,7 +86,7 @@ To use Gson with Maven2/3, you can use the Gson version available in Maven Centr
|
||||
<dependency>
|
||||
<groupId>com.google.code.gson</groupId>
|
||||
<artifactId>gson</artifactId>
|
||||
<version>2.8.6</version>
|
||||
<version>2.8.8</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
@ -40,7 +40,7 @@
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<version>3.8.2</version>
|
||||
<version>4.13.1</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
@ -15,7 +15,7 @@
|
||||
|
||||
# Prevent proguard from stripping interface information from TypeAdapter, TypeAdapterFactory,
|
||||
# JsonSerializer, JsonDeserializer instances (so they can be used in @JsonAdapter)
|
||||
-keep class * implements com.google.gson.TypeAdapter
|
||||
-keep class * extends com.google.gson.TypeAdapter
|
||||
-keep class * implements com.google.gson.TypeAdapterFactory
|
||||
-keep class * implements com.google.gson.JsonSerializer
|
||||
-keep class * implements com.google.gson.JsonDeserializer
|
||||
@ -25,4 +25,8 @@
|
||||
@com.google.gson.annotations.SerializedName <fields>;
|
||||
}
|
||||
|
||||
# Retain generic signatures of TypeToken and its subclasses with R8 version 3.0 and higher.
|
||||
-keep,allowobfuscation,allowshrinking class com.google.gson.reflect.TypeToken
|
||||
-keep,allowobfuscation,allowshrinking class * extends com.google.gson.reflect.TypeToken
|
||||
|
||||
##---------------End: proguard configuration for Gson ----------
|
||||
|
@ -51,7 +51,7 @@
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<version>3.8.2</version>
|
||||
<version>4.13.1</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
91
gson/pom.xml
91
gson/pom.xml
@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>com.google.code.gson</groupId>
|
||||
<artifactId>gson-parent</artifactId>
|
||||
<version>2.8.7-SNAPSHOT</version>
|
||||
<version>2.8.9-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>gson</artifactId>
|
||||
@ -16,6 +16,12 @@
|
||||
<artifactId>junit</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.github.wvengen</groupId>
|
||||
<artifactId>proguard-maven-plugin</artifactId>
|
||||
<version>2.4.0</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
@ -34,7 +40,7 @@
|
||||
<plugin>
|
||||
<groupId>biz.aQute.bnd</groupId>
|
||||
<artifactId>bnd-maven-plugin</artifactId>
|
||||
<version>4.0.0</version>
|
||||
<version>5.3.0</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
@ -69,6 +75,87 @@
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>com.coderplus.maven.plugins</groupId>
|
||||
<artifactId>copy-rename-maven-plugin</artifactId>
|
||||
<version>1.0.1</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>pre-obfuscate-class</id>
|
||||
<phase>process-test-classes</phase>
|
||||
<goals>
|
||||
<goal>rename</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<fileSets>
|
||||
<fileSet>
|
||||
<sourceFile>${project.build.directory}/test-classes/com/google/gson/functional/EnumWithObfuscatedTest.class</sourceFile>
|
||||
<destinationFile>${project.build.directory}/test-classes-obfuscated-injar/com/google/gson/functional/EnumWithObfuscatedTest.class</destinationFile>
|
||||
</fileSet>
|
||||
<fileSet>
|
||||
<sourceFile>${project.build.directory}/test-classes/com/google/gson/functional/EnumWithObfuscatedTest$Gender.class</sourceFile>
|
||||
<destinationFile>${project.build.directory}/test-classes-obfuscated-injar/com/google/gson/functional/EnumWithObfuscatedTest$Gender.class</destinationFile>
|
||||
</fileSet>
|
||||
</fileSets>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>com.github.wvengen</groupId>
|
||||
<artifactId>proguard-maven-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>process-test-classes</phase>
|
||||
<goals><goal>proguard</goal></goals>
|
||||
</execution>
|
||||
</executions>
|
||||
<configuration>
|
||||
<proguardVersion>6.2.2</proguardVersion>
|
||||
<obfuscate>true</obfuscate>
|
||||
<injar>test-classes-obfuscated-injar</injar>
|
||||
<outjar>test-classes-obfuscated-outjar</outjar>
|
||||
<inFilter>**/*.class</inFilter>
|
||||
<proguardInclude>${basedir}/src/test/resources/testcases-proguard.conf</proguardInclude>
|
||||
<libs>
|
||||
<lib>${project.build.directory}/classes</lib>
|
||||
<lib>${java.home}/jmods/java.base.jmod</lib>
|
||||
</libs>
|
||||
</configuration>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>net.sf.proguard</groupId>
|
||||
<artifactId>proguard-base</artifactId>
|
||||
<version>6.2.2</version>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-resources-plugin</artifactId>
|
||||
<version>3.2.0</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>post-obfuscate-class</id>
|
||||
<phase>process-test-classes</phase>
|
||||
<goals>
|
||||
<goal>copy-resources</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<outputDirectory>${project.build.directory}/test-classes/com/google/gson/functional</outputDirectory>
|
||||
<resources>
|
||||
<resource>
|
||||
<directory>${project.build.directory}/test-classes-obfuscated-outjar/com/google/gson/functional</directory>
|
||||
<includes>
|
||||
<include>EnumWithObfuscatedTest.class</include>
|
||||
<include>EnumWithObfuscatedTest$Gender.class</include>
|
||||
</includes>
|
||||
</resource>
|
||||
</resources>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
|
@ -29,7 +29,8 @@ import com.google.gson.stream.MalformedJsonException;
|
||||
|
||||
/**
|
||||
* A streaming parser that allows reading of multiple {@link JsonElement}s from the specified reader
|
||||
* asynchronously.
|
||||
* asynchronously. The JSON data is parsed in lenient mode, see also
|
||||
* {@link JsonReader#setLenient(boolean)}.
|
||||
*
|
||||
* <p>This class is conditionally thread-safe (see Item 70, Effective Java second edition). To
|
||||
* properly use this class across multiple threads, you will need to add some external
|
||||
@ -72,10 +73,12 @@ public final class JsonStreamParser implements Iterator<JsonElement> {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the next available {@link JsonElement} on the reader. Null if none available.
|
||||
* Returns the next available {@link JsonElement} on the reader. Throws a
|
||||
* {@link NoSuchElementException} if no element is available.
|
||||
*
|
||||
* @return the next available {@link JsonElement} on the reader. Null if none available.
|
||||
* @throws JsonParseException if the incoming stream is malformed JSON.
|
||||
* @return the next available {@code JsonElement} on the reader.
|
||||
* @throws JsonSyntaxException if the incoming stream is malformed JSON.
|
||||
* @throws NoSuchElementException if no {@code JsonElement} is available.
|
||||
* @since 1.4
|
||||
*/
|
||||
public JsonElement next() throws JsonParseException {
|
||||
@ -97,6 +100,7 @@ public final class JsonStreamParser implements Iterator<JsonElement> {
|
||||
/**
|
||||
* Returns true if a {@link JsonElement} is available on the input for consumption
|
||||
* @return true if a {@link JsonElement} is available on the input, false otherwise
|
||||
* @throws JsonSyntaxException if the incoming stream is malformed JSON.
|
||||
* @since 1.4
|
||||
*/
|
||||
public boolean hasNext() {
|
||||
|
@ -25,7 +25,12 @@ import java.lang.reflect.ParameterizedType;
|
||||
import java.lang.reflect.Type;
|
||||
import java.lang.reflect.TypeVariable;
|
||||
import java.lang.reflect.WildcardType;
|
||||
import java.util.*;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.Properties;
|
||||
|
||||
import static com.google.gson.internal.$Gson$Preconditions.checkArgument;
|
||||
import static com.google.gson.internal.$Gson$Preconditions.checkNotNull;
|
||||
@ -334,52 +339,62 @@ public final class $Gson$Types {
|
||||
}
|
||||
|
||||
public static Type resolve(Type context, Class<?> contextRawType, Type toResolve) {
|
||||
return resolve(context, contextRawType, toResolve, new HashSet<TypeVariable<?>>());
|
||||
|
||||
return resolve(context, contextRawType, toResolve, new HashMap<TypeVariable<?>, Type>());
|
||||
}
|
||||
|
||||
private static Type resolve(Type context, Class<?> contextRawType, Type toResolve,
|
||||
Collection<TypeVariable<?>> visitedTypeVariables) {
|
||||
Map<TypeVariable<?>, Type> visitedTypeVariables) {
|
||||
// this implementation is made a little more complicated in an attempt to avoid object-creation
|
||||
TypeVariable<?> resolving = null;
|
||||
while (true) {
|
||||
if (toResolve instanceof TypeVariable) {
|
||||
TypeVariable<?> typeVariable = (TypeVariable<?>) toResolve;
|
||||
if (visitedTypeVariables.contains(typeVariable)) {
|
||||
Type previouslyResolved = visitedTypeVariables.get(typeVariable);
|
||||
if (previouslyResolved != null) {
|
||||
// cannot reduce due to infinite recursion
|
||||
return toResolve;
|
||||
} else {
|
||||
visitedTypeVariables.add(typeVariable);
|
||||
return (previouslyResolved == Void.TYPE) ? toResolve : previouslyResolved;
|
||||
}
|
||||
|
||||
// Insert a placeholder to mark the fact that we are in the process of resolving this type
|
||||
visitedTypeVariables.put(typeVariable, Void.TYPE);
|
||||
if (resolving == null) {
|
||||
resolving = typeVariable;
|
||||
}
|
||||
|
||||
toResolve = resolveTypeVariable(context, contextRawType, typeVariable);
|
||||
if (toResolve == typeVariable) {
|
||||
return toResolve;
|
||||
break;
|
||||
}
|
||||
|
||||
} else if (toResolve instanceof Class && ((Class<?>) toResolve).isArray()) {
|
||||
Class<?> original = (Class<?>) toResolve;
|
||||
Type componentType = original.getComponentType();
|
||||
Type newComponentType = resolve(context, contextRawType, componentType, visitedTypeVariables);
|
||||
return componentType == newComponentType
|
||||
toResolve = equal(componentType, newComponentType)
|
||||
? original
|
||||
: arrayOf(newComponentType);
|
||||
break;
|
||||
|
||||
} else if (toResolve instanceof GenericArrayType) {
|
||||
GenericArrayType original = (GenericArrayType) toResolve;
|
||||
Type componentType = original.getGenericComponentType();
|
||||
Type newComponentType = resolve(context, contextRawType, componentType, visitedTypeVariables);
|
||||
return componentType == newComponentType
|
||||
toResolve = equal(componentType, newComponentType)
|
||||
? original
|
||||
: arrayOf(newComponentType);
|
||||
break;
|
||||
|
||||
} else if (toResolve instanceof ParameterizedType) {
|
||||
ParameterizedType original = (ParameterizedType) toResolve;
|
||||
Type ownerType = original.getOwnerType();
|
||||
Type newOwnerType = resolve(context, contextRawType, ownerType, visitedTypeVariables);
|
||||
boolean changed = newOwnerType != ownerType;
|
||||
boolean changed = !equal(newOwnerType, ownerType);
|
||||
|
||||
Type[] args = original.getActualTypeArguments();
|
||||
for (int t = 0, length = args.length; t < length; t++) {
|
||||
Type resolvedTypeArgument = resolve(context, contextRawType, args[t], visitedTypeVariables);
|
||||
if (resolvedTypeArgument != args[t]) {
|
||||
if (!equal(resolvedTypeArgument, args[t])) {
|
||||
if (!changed) {
|
||||
args = args.clone();
|
||||
changed = true;
|
||||
@ -388,9 +403,10 @@ public final class $Gson$Types {
|
||||
}
|
||||
}
|
||||
|
||||
return changed
|
||||
toResolve = changed
|
||||
? newParameterizedTypeWithOwner(newOwnerType, original.getRawType(), args)
|
||||
: original;
|
||||
break;
|
||||
|
||||
} else if (toResolve instanceof WildcardType) {
|
||||
WildcardType original = (WildcardType) toResolve;
|
||||
@ -400,20 +416,28 @@ public final class $Gson$Types {
|
||||
if (originalLowerBound.length == 1) {
|
||||
Type lowerBound = resolve(context, contextRawType, originalLowerBound[0], visitedTypeVariables);
|
||||
if (lowerBound != originalLowerBound[0]) {
|
||||
return supertypeOf(lowerBound);
|
||||
toResolve = supertypeOf(lowerBound);
|
||||
break;
|
||||
}
|
||||
} else if (originalUpperBound.length == 1) {
|
||||
Type upperBound = resolve(context, contextRawType, originalUpperBound[0], visitedTypeVariables);
|
||||
if (upperBound != originalUpperBound[0]) {
|
||||
return subtypeOf(upperBound);
|
||||
toResolve = subtypeOf(upperBound);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return original;
|
||||
toResolve = original;
|
||||
break;
|
||||
|
||||
} else {
|
||||
return toResolve;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// ensure that any in-process resolution gets updated with the final result
|
||||
if (resolving != null) {
|
||||
visitedTypeVariables.put(resolving, toResolve);
|
||||
}
|
||||
return toResolve;
|
||||
}
|
||||
|
||||
static Type resolveTypeVariable(Type context, Class<?> contextRawType, TypeVariable<?> unknown) {
|
||||
|
@ -17,12 +17,15 @@
|
||||
package com.google.gson.internal.bind;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Field;
|
||||
import java.math.BigDecimal;
|
||||
import java.math.BigInteger;
|
||||
import java.net.InetAddress;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.net.URL;
|
||||
import java.security.AccessController;
|
||||
import java.security.PrivilegedAction;
|
||||
import java.util.ArrayList;
|
||||
import java.util.BitSet;
|
||||
import java.util.Calendar;
|
||||
@ -753,9 +756,20 @@ public final class TypeAdapters {
|
||||
|
||||
public EnumTypeAdapter(Class<T> classOfT) {
|
||||
try {
|
||||
for (T constant : classOfT.getEnumConstants()) {
|
||||
for (final Field field : classOfT.getDeclaredFields()) {
|
||||
if (!field.isEnumConstant()) {
|
||||
continue;
|
||||
}
|
||||
AccessController.doPrivileged(new PrivilegedAction<Void>() {
|
||||
@Override public Void run() {
|
||||
field.setAccessible(true);
|
||||
return null;
|
||||
}
|
||||
});
|
||||
@SuppressWarnings("unchecked")
|
||||
T constant = (T)(field.get(null));
|
||||
String name = constant.name();
|
||||
SerializedName annotation = classOfT.getField(name).getAnnotation(SerializedName.class);
|
||||
SerializedName annotation = field.getAnnotation(SerializedName.class);
|
||||
if (annotation != null) {
|
||||
name = annotation.value();
|
||||
for (String alternate : annotation.alternate()) {
|
||||
@ -765,7 +779,7 @@ public final class TypeAdapters {
|
||||
nameToConstant.put(name, constant);
|
||||
constantToName.put(constant, name);
|
||||
}
|
||||
} catch (NoSuchFieldException e) {
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
}
|
||||
|
@ -79,7 +79,7 @@ final class UnsafeReflectionAccessor extends ReflectionAccessor {
|
||||
private static Field getOverrideField() {
|
||||
try {
|
||||
return AccessibleObject.class.getDeclaredField("override");
|
||||
} catch (NoSuchFieldException e) {
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
@ -16,9 +16,8 @@
|
||||
|
||||
package com.google.gson;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import com.google.gson.common.MoreAsserts;
|
||||
import junit.framework.TestCase;
|
||||
|
||||
/**
|
||||
* @author Jesse Wilson
|
||||
@ -99,4 +98,68 @@ public final class JsonArrayTest extends TestCase {
|
||||
assertEquals(1, original.get(0).getAsJsonArray().size());
|
||||
assertEquals(0, copy.get(0).getAsJsonArray().size());
|
||||
}
|
||||
|
||||
public void testFailedGetArrayValues() {
|
||||
JsonArray jsonArray = new JsonArray();
|
||||
jsonArray.add(JsonParser.parseString("{" + "\"key1\":\"value1\"," + "\"key2\":\"value2\"," + "\"key3\":\"value3\"," + "\"key4\":\"value4\"" + "}"));
|
||||
try {
|
||||
jsonArray.getAsBoolean();
|
||||
fail("expected getBoolean to fail");
|
||||
} catch (UnsupportedOperationException e) {
|
||||
assertEquals("Expected an exception message",
|
||||
"JsonObject", e.getMessage());
|
||||
}
|
||||
try {
|
||||
jsonArray.get(-1);
|
||||
fail("expected get to fail");
|
||||
} catch (IndexOutOfBoundsException e) {
|
||||
assertEquals("Expected an exception message",
|
||||
"Index -1 out of bounds for length 1", e.getMessage());
|
||||
}
|
||||
try {
|
||||
jsonArray.getAsString();
|
||||
fail("expected getString to fail");
|
||||
} catch (UnsupportedOperationException e) {
|
||||
assertEquals("Expected an exception message",
|
||||
"JsonObject", e.getMessage());
|
||||
}
|
||||
|
||||
jsonArray.remove(0);
|
||||
jsonArray.add("hello");
|
||||
try {
|
||||
jsonArray.getAsDouble();
|
||||
fail("expected getDouble to fail");
|
||||
} catch (NumberFormatException e) {
|
||||
assertEquals("Expected an exception message",
|
||||
"For input string: \"hello\"", e.getMessage());
|
||||
}
|
||||
try {
|
||||
jsonArray.getAsInt();
|
||||
fail("expected getInt to fail");
|
||||
} catch (NumberFormatException e) {
|
||||
assertEquals("Expected an exception message",
|
||||
"For input string: \"hello\"", e.getMessage());
|
||||
}
|
||||
try {
|
||||
jsonArray.get(0).getAsJsonArray();
|
||||
fail("expected getJSONArray to fail");
|
||||
} catch (IllegalStateException e) {
|
||||
assertEquals("Expected an exception message",
|
||||
"Not a JSON Array: \"hello\"", e.getMessage());
|
||||
}
|
||||
try {
|
||||
jsonArray.getAsJsonObject();
|
||||
fail("expected getJSONObject to fail");
|
||||
} catch (IllegalStateException e) {
|
||||
assertEquals("Expected an exception message",
|
||||
"Not a JSON Object: [\"hello\"]", e.getMessage());
|
||||
}
|
||||
try {
|
||||
jsonArray.getAsLong();
|
||||
fail("expected getLong to fail");
|
||||
} catch (NumberFormatException e) {
|
||||
assertEquals("Expected an exception message",
|
||||
"For input string: \"hello\"", e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright (C) 2008 Google Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.gson.functional;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
/**
|
||||
* Functional tests for enums with Proguard.
|
||||
*
|
||||
* @author Young Cha
|
||||
*/
|
||||
public class EnumWithObfuscatedTest extends TestCase {
|
||||
private Gson gson;
|
||||
|
||||
@Override
|
||||
protected void setUp() throws Exception {
|
||||
super.setUp();
|
||||
gson = new Gson();
|
||||
}
|
||||
|
||||
public enum Gender {
|
||||
@SerializedName("MAIL")
|
||||
MALE,
|
||||
|
||||
@SerializedName("FEMAIL")
|
||||
FEMALE
|
||||
}
|
||||
|
||||
public void testEnumClassWithObfuscated() {
|
||||
for (Gender enumConstant: Gender.class.getEnumConstants()) {
|
||||
try {
|
||||
Gender.class.getField(enumConstant.name());
|
||||
fail("Enum is not obfuscated");
|
||||
} catch (NoSuchFieldException ignore) {
|
||||
}
|
||||
}
|
||||
|
||||
assertEquals(Gender.MALE, gson.fromJson("\"MAIL\"", Gender.class));
|
||||
assertEquals("\"MAIL\"", gson.toJson(Gender.MALE, Gender.class));
|
||||
}
|
||||
}
|
@ -0,0 +1,55 @@
|
||||
package com.google.gson.functional;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
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;
|
||||
|
||||
/**
|
||||
* This test covers the scenario described in #1390 where a type variable needs to be used
|
||||
* by a type definition multiple times. Both type variable references should resolve to the
|
||||
* same underlying concrete type.
|
||||
*/
|
||||
public class ReusedTypeVariablesFullyResolveTest {
|
||||
|
||||
private Gson gson;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
gson = new GsonBuilder().create();
|
||||
}
|
||||
|
||||
@SuppressWarnings("ConstantConditions") // The instances were being unmarshaled as Strings instead of TestEnums
|
||||
@Test
|
||||
public void testGenericsPreservation() {
|
||||
TestEnumSetCollection withSet = gson.fromJson("{\"collection\":[\"ONE\",\"THREE\"]}", TestEnumSetCollection.class);
|
||||
Iterator<TestEnum> iterator = withSet.collection.iterator();
|
||||
assertNotNull(withSet);
|
||||
assertNotNull(withSet.collection);
|
||||
assertEquals(2, withSet.collection.size());
|
||||
TestEnum first = iterator.next();
|
||||
TestEnum second = iterator.next();
|
||||
|
||||
assertTrue(first instanceof TestEnum);
|
||||
assertTrue(second instanceof TestEnum);
|
||||
}
|
||||
|
||||
enum TestEnum { ONE, TWO, THREE }
|
||||
|
||||
private static class TestEnumSetCollection extends SetCollection<TestEnum> {}
|
||||
|
||||
private static class SetCollection<T> extends BaseCollection<T, Set<T>> {}
|
||||
|
||||
private static class BaseCollection<U, C extends Collection<U>>
|
||||
{
|
||||
public C collection;
|
||||
}
|
||||
|
||||
}
|
@ -15,9 +15,25 @@ public class ISO8601UtilsTest {
|
||||
@Rule
|
||||
public final ExpectedException exception = ExpectedException.none();
|
||||
|
||||
private static TimeZone utcTimeZone() {
|
||||
return TimeZone.getTimeZone("UTC");
|
||||
}
|
||||
|
||||
private static GregorianCalendar createUtcCalendar() {
|
||||
TimeZone utc = utcTimeZone();
|
||||
GregorianCalendar calendar = new GregorianCalendar(utc);
|
||||
// Calendar was created with current time, must clear it
|
||||
calendar.clear();
|
||||
return calendar;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDateFormatString() {
|
||||
Date date = new GregorianCalendar(2018, Calendar.JUNE, 25).getTime();
|
||||
GregorianCalendar calendar = new GregorianCalendar(utcTimeZone(), Locale.US);
|
||||
// Calendar was created with current time, must clear it
|
||||
calendar.clear();
|
||||
calendar.set(2018, Calendar.JUNE, 25);
|
||||
Date date = calendar.getTime();
|
||||
String dateStr = ISO8601Utils.format(date);
|
||||
String expectedDate = "2018-06-25";
|
||||
assertEquals(expectedDate, dateStr.substring(0, expectedDate.length()));
|
||||
@ -51,51 +67,28 @@ public class ISO8601UtilsTest {
|
||||
|
||||
@Test
|
||||
public void testDateParseWithTimezone() throws ParseException {
|
||||
TimeZone defaultTimeZone = TimeZone.getDefault();
|
||||
TimeZone.setDefault(TimeZone.getTimeZone("UTC"));
|
||||
Locale defaultLocale = Locale.getDefault();
|
||||
Locale.setDefault(Locale.US);
|
||||
try {
|
||||
String dateStr = "2018-06-25T00:00:00-03:00";
|
||||
Date date = ISO8601Utils.parse(dateStr, new ParsePosition(0));
|
||||
Date expectedDate = new GregorianCalendar(2018, Calendar.JUNE, 25, 3, 0).getTime();
|
||||
assertEquals(expectedDate, date);
|
||||
} finally {
|
||||
TimeZone.setDefault(defaultTimeZone);
|
||||
Locale.setDefault(defaultLocale);
|
||||
}
|
||||
String dateStr = "2018-06-25T00:00:00-03:00";
|
||||
Date date = ISO8601Utils.parse(dateStr, new ParsePosition(0));
|
||||
GregorianCalendar calendar = createUtcCalendar();
|
||||
calendar.set(2018, Calendar.JUNE, 25, 3, 0);
|
||||
Date expectedDate = calendar.getTime();
|
||||
assertEquals(expectedDate, date);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDateParseSpecialTimezone() throws ParseException {
|
||||
TimeZone defaultTimeZone = TimeZone.getDefault();
|
||||
TimeZone.setDefault(TimeZone.getTimeZone("UTC"));
|
||||
Locale defaultLocale = Locale.getDefault();
|
||||
Locale.setDefault(Locale.US);
|
||||
try {
|
||||
String dateStr = "2018-06-25T00:02:00-02:58";
|
||||
Date date = ISO8601Utils.parse(dateStr, new ParsePosition(0));
|
||||
Date expectedDate = new GregorianCalendar(2018, Calendar.JUNE, 25, 3, 0).getTime();
|
||||
assertEquals(expectedDate, date);
|
||||
} finally {
|
||||
TimeZone.setDefault(defaultTimeZone);
|
||||
Locale.setDefault(defaultLocale);
|
||||
}
|
||||
String dateStr = "2018-06-25T00:02:00-02:58";
|
||||
Date date = ISO8601Utils.parse(dateStr, new ParsePosition(0));
|
||||
GregorianCalendar calendar = createUtcCalendar();
|
||||
calendar.set(2018, Calendar.JUNE, 25, 3, 0);
|
||||
Date expectedDate = calendar.getTime();
|
||||
assertEquals(expectedDate, date);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDateParseInvalidTime() throws ParseException {
|
||||
TimeZone defaultTimeZone = TimeZone.getDefault();
|
||||
TimeZone.setDefault(TimeZone.getTimeZone("UTC"));
|
||||
Locale defaultLocale = Locale.getDefault();
|
||||
Locale.setDefault(Locale.US);
|
||||
try {
|
||||
String dateStr = "2018-06-25T61:60:62-03:00";
|
||||
exception.expect(ParseException.class);
|
||||
ISO8601Utils.parse(dateStr, new ParsePosition(0));
|
||||
} finally {
|
||||
TimeZone.setDefault(defaultTimeZone);
|
||||
Locale.setDefault(defaultLocale);
|
||||
}
|
||||
String dateStr = "2018-06-25T61:60:62-03:00";
|
||||
exception.expect(ParseException.class);
|
||||
ISO8601Utils.parse(dateStr, new ParsePosition(0));
|
||||
}
|
||||
}
|
||||
|
@ -15,10 +15,12 @@
|
||||
*/
|
||||
package com.google.gson.internal.reflect;
|
||||
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.security.Permission;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
@ -41,6 +43,30 @@ public class UnsafeReflectionAccessorTest {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMakeAccessibleWithRestrictiveSecurityManager() throws Exception {
|
||||
final Permission accessDeclaredMembers = new RuntimePermission("accessDeclaredMembers");
|
||||
final SecurityManager original = System.getSecurityManager();
|
||||
SecurityManager restrictiveManager = new SecurityManager() {
|
||||
@Override
|
||||
public void checkPermission(Permission perm) {
|
||||
if (accessDeclaredMembers.equals(perm)) {
|
||||
throw new SecurityException("nope");
|
||||
}
|
||||
}
|
||||
};
|
||||
System.setSecurityManager(restrictiveManager);
|
||||
|
||||
try {
|
||||
UnsafeReflectionAccessor accessor = new UnsafeReflectionAccessor();
|
||||
Field field = ClassWithPrivateFinalFields.class.getDeclaredField("a");
|
||||
assertFalse("override field should have been inaccessible", accessor.makeAccessibleWithUnsafe(field));
|
||||
accessor.makeAccessible(field);
|
||||
} finally {
|
||||
System.setSecurityManager(original);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private static final class ClassWithPrivateFinalFields {
|
||||
private final String a;
|
||||
|
20
gson/src/test/resources/testcases-proguard.conf
Normal file
20
gson/src/test/resources/testcases-proguard.conf
Normal file
@ -0,0 +1,20 @@
|
||||
# Options from Android Gradle Plugins
|
||||
# https://android.googlesource.com/platform/tools/base/+/refs/heads/studio-master-dev/build-system/gradle-core/src/main/resources/com/android/build/gradle
|
||||
-optimizations !code/simplification/arithmetic,!code/simplification/cast,!field/*,!class/merging/*
|
||||
-optimizationpasses 5
|
||||
-allowaccessmodification
|
||||
-keepattributes *Annotation*,Signature,InnerClasses,EnclosingMethod
|
||||
-keepclassmembers enum * {
|
||||
public static **[] values();
|
||||
public static ** valueOf(java.lang.String);
|
||||
}
|
||||
|
||||
-keep enum com.google.gson.functional.EnumWithObfuscatedTest$Gender
|
||||
-keep class com.google.gson.functional.EnumWithObfuscatedTest {
|
||||
public void test*();
|
||||
protected void setUp();
|
||||
}
|
||||
|
||||
-dontwarn com.google.gson.functional.EnumWithObfuscatedTest
|
||||
-dontwarn junit.framework.TestCase
|
||||
|
@ -51,7 +51,7 @@
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<version>3.8.2</version>
|
||||
<version>4.13.1</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
15
pom.xml
15
pom.xml
@ -11,7 +11,7 @@
|
||||
|
||||
<groupId>com.google.code.gson</groupId>
|
||||
<artifactId>gson-parent</artifactId>
|
||||
<version>2.8.7-SNAPSHOT</version>
|
||||
<version>2.8.9-SNAPSHOT</version>
|
||||
<packaging>pom</packaging>
|
||||
|
||||
<name>Gson Parent</name>
|
||||
@ -51,7 +51,7 @@
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<version>4.12</version>
|
||||
<version>4.13.2</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
@ -97,7 +97,7 @@
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-javadoc-plugin</artifactId>
|
||||
<version>2.10.4</version>
|
||||
<version>3.3.0</version>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
@ -106,7 +106,7 @@
|
||||
<plugin>
|
||||
<groupId>org.apache.felix</groupId>
|
||||
<artifactId>maven-bundle-plugin</artifactId>
|
||||
<version>3.3.0</version>
|
||||
<version>5.1.2</version>
|
||||
<inherited>true</inherited>
|
||||
</plugin>
|
||||
</plugins>
|
||||
@ -117,10 +117,15 @@
|
||||
<artifactId>maven-release-plugin</artifactId>
|
||||
<version>2.5.3</version>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.apache.maven.scm</groupId>
|
||||
<artifactId>maven-scm-api</artifactId>
|
||||
<version>1.11.3</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.maven.scm</groupId>
|
||||
<artifactId>maven-scm-provider-gitexe</artifactId>
|
||||
<version>1.9.5</version>
|
||||
<version>1.11.3</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<configuration>
|
||||
|
@ -76,7 +76,7 @@
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<version>4.12</version>
|
||||
<version>4.13.1</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user