diff --git a/.gitignore b/.gitignore index b1bdb7f2..5bda0b93 100644 --- a/.gitignore +++ b/.gitignore @@ -13,4 +13,8 @@ release.properties *.iws classes +.gradle +local.properties +build + .DS_Store diff --git a/CHANGELOG.md b/CHANGELOG.md index 12949841..ef37e520 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,17 @@ Change Log ========== +## Version 2.8.4 +_2018-05-01_ [GitHub Diff](https://github.com/google/gson/compare/gson-parent-2.8.3...gson-parent-2.8.4) + * Added a new FieldNamingPolicy, `LOWER_CASE_WITH_DOTS` that mapps JSON name `someFieldName` to `some.field.name` + * Fixed issue https://github.com/google/gson/issues/1305 by removing compile/runtime dependency on `sun.misc.Unsafe` + +## Version 2.8.3 +_2018-04-27_ [GitHub Diff](https://github.com/google/gson/compare/gson-parent-2.8.2...gson-parent-2.8.3) + * Added a new API, `GsonBuilder.newBuilder()` that clones the current builder + * Preserving DateFormatter behavior on JDK 9 + * Numerous other bugfixes + ## Version 2.8.2 _2017-09-19_ [GitHub Diff](https://github.com/google/gson/compare/gson-parent-2.8.1...gson-parent-2.8.2) * Introduced a new API, `JsonElement.deepCopy()` diff --git a/README.md b/README.md index 76278447..c85ba309 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ There are a few open-source projects that can convert Java objects to JSON. Howe * To use Gson in Android ```gradle dependencies { - implementation 'com.google.code.gson:gson:2.8.2' + implementation 'com.google.code.gson:gson:2.8.4' } ``` @@ -30,7 +30,7 @@ dependencies { com.google.code.gson gson - 2.8.2 + 2.8.4 ``` diff --git a/ReleaseProcess.md b/ReleaseProcess.md index f34f47ec..4b7fbb2d 100644 --- a/ReleaseProcess.md +++ b/ReleaseProcess.md @@ -1,51 +1,19 @@ -#Gson Release Process +# Gson Release Process The following is a step-by-step procedure for releasing a new version of Google-Gson. 1. Go through all open bugs and identify which will be fixed in this release. Mark all others with an appropriate release tag. Identify duplicates, and close the bugs that will never be fixed. Fix all bugs for the release, and mark them fixed. 1. (obsolete step) Edit [`pom.xml`](pom.xml) and update the versions listed for Export-Package to the target version. Also add any new Java packages that have been introduced in Gson. 1. Ensure all changelists are code-reviewed and have +1 -1. (obsolete step) Ensure that your `~/.m2/settings.xml` is configured properly (see steps below) 1. `cd gson` to the parent directory; ensure there are no open files and all changes are committed. 1. Run `mvn release:clean` 1. Do a dry run: `mvn release:prepare -DdryRun=true` 1. Start the release: `mvn release:prepare` - * Answer questions: usually the defaults are fine. - * This will do a full build, change version from `-SNAPSHOT` to the released version, commit and create the tags. It will then change the version to `-SNAPSHOT` for the next release. -1. Ensure you have defined `sonatype-nexus-staging` in your Maven `settings.xml` and run: - - ```bash - mvn -s /home//.m2/settings.xml release:perform - ``` - -1. [Log in to Nexus repository manager](https://oss.sonatype.org/index.html#welcome) at Sonatype and close the staging repository for Gson. If you run into an error regarding missing signatures, you need to manually upload the artifacts using `mvn gpg:sign-and-deploy-file` for Gson binary, source and Javadoc jars. - - ```bash - cp -r ~/.m2/repository/com/google/code/gson/gson/1.7.2 /tmp - cd /tmp/1.7.2 - - mvn gpg:sign-and-deploy-file \ - -Durl=https://oss.sonatype.org/service/local/staging/deploy/maven2/ \ - -DrepositoryId=sonatype-nexus-staging \ - -DpomFile=gson-1.7.2.pom \ - -Dfile=gson-1.7.2-javadoc.jar \ - -Dclassifier=javadoc - - mvn gpg:sign-and-deploy-file \ - -Durl=https://oss.sonatype.org/service/local/staging/deploy/maven2/ \ - -DrepositoryId=sonatype-nexus-staging \ - -DpomFile=gson-1.7.2.pom \ - -Dfile=gson-1.7.2-sources.jar \ - -Dclassifier=sources - - mvn gpg:sign-and-deploy-file \ - -Durl=https://oss.sonatype.org/service/local/staging/deploy/maven2/ \ - -DrepositoryId=sonatype-nexus-staging \ - -DpomFile=gson-1.7.2.pom \ - -Dfile=gson-1.7.2.jar - ``` - -1. Close the Gson repository. Download and sanity check all downloads. Do not skip this step! Once you release the staging repository, there is no going back. It will get synced with Maven central and you will not be able to update or delete anything. Your only recourse will be to release a new version of Gson and hope that no one uses the old one. + * Answer questions: usually the defaults are fine. + * This will do a full build, change version from `-SNAPSHOT` to the released version, commit and create the tags. It will then change the version to `-SNAPSHOT` for the next release. +1. Complete the release: `mvn release:perform` +1. [Log in to Nexus repository manager](https://oss.sonatype.org/index.html#welcome) at Sonatype and close the staging repository for Gson. +1. Download and sanity check all downloads. Do not skip this step! Once you release the staging repository, there is no going back. It will get synced with Maven central and you will not be able to update or delete anything. Your only recourse will be to release a new version of Gson and hope that no one uses the old one. 1. Release the staging repository for Gson. Gson will now get synced to Maven central with-in the next hour. For issues consult [Sonatype Guide](https://docs.sonatype.org/display/Repository/Sonatype+OSS+Maven+Repository+Usage+Guide#SonatypeOSSMavenRepositoryUsageGuide-8.ReleaseIt). 1. Update the version in the [Using Gson with Maven2 page](https://github.com/google/gson/blob/master/UserGuide.md#TOC-Gson-With-Maven) @@ -58,16 +26,16 @@ The following is a step-by-step procedure for releasing a new version of Google- This section was borrowed heavily from [Doclava release process](http://code.google.com/p/doclava/wiki/ProcessRelease). 1. Install/Configure GPG following this [guide](http://www.sonatype.com/people/2010/01/how-to-generate-pgp-signatures-with-maven/). -2. [Create encrypted passwords](http://maven.apache.org/guides/mini/guide-encryption.html). -3. Create `~/.m2/settings.xml` similar to as described in [Doclava release process](https://code.google.com/p/doclava/wiki/ProcessRelease). -4. Now for deploying a snapshot repository, use `mvn deploy`. +1. [Create encrypted passwords](http://maven.apache.org/guides/mini/guide-encryption.html). +1. Create `~/.m2/settings.xml` similar to as described in [Doclava release process](https://code.google.com/p/doclava/wiki/ProcessRelease). +1. Now for deploying a snapshot repository, use `mvn deploy`. ## Getting Maven Publishing Privileges Based on [Gson group thread](https://groups.google.com/d/topic/google-gson/DHWJHVFpIBg/discussion): 1. [Sign up for a Sonatype account](https://docs.sonatype.org/display/Repository/Sonatype+OSS+Maven+Repository+Usage+Guide) following instructions under (2) on that page -2. Ask one of the existing members of the repository to create a JIRA ticket (Step 3 of above document) to add you to the publisher list. +1. Ask one of the existing members of the repository to create a JIRA ticket (Step 3 of above document) to add you to the publisher list. ## Running Benchmarks or Tests on Android diff --git a/UserGuide.md b/UserGuide.md index 4feaf11c..4ce9b367 100644 --- a/UserGuide.md +++ b/UserGuide.md @@ -74,7 +74,7 @@ The Gson instance does not maintain any state while invoking Json operations. So ## Using Gson with Gradle/Android ``` dependencies { - compile 'com.google.code.gson:gson:2.8.2' + compile 'com.google.code.gson:gson:2.8.4' } ``` ## Using Gson with Maven @@ -86,7 +86,7 @@ To use Gson with Maven2/3, you can use the Gson version available in Maven Centr com.google.code.gson gson - 2.8.2 + 2.8.4 compile diff --git a/build.gradle b/build.gradle new file mode 100644 index 00000000..28586b5e --- /dev/null +++ b/build.gradle @@ -0,0 +1,12 @@ +buildscript { + repositories { + mavenCentral() + } +} + +allprojects { + repositories { + mavenCentral() + } +} + diff --git a/examples/android-proguard-example/AndroidManifest.xml b/examples/android-proguard-example/AndroidManifest.xml old mode 100755 new mode 100644 diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 00000000..7a3265ee Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 00000000..4c5f8820 --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Fri Apr 27 17:41:01 PDT 2018 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-4.4-all.zip diff --git a/gradlew b/gradlew new file mode 100755 index 00000000..cccdd3d5 --- /dev/null +++ b/gradlew @@ -0,0 +1,172 @@ +#!/usr/bin/env sh + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=$(save "$@") + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong +if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then + cd "$(dirname "$0")" +fi + +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100755 index 00000000..f9553162 --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,84 @@ +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windows variants + +if not "%OS%" == "Windows_NT" goto win9xME_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/gson/build.gradle b/gson/build.gradle new file mode 100644 index 00000000..04706bb9 --- /dev/null +++ b/gson/build.gradle @@ -0,0 +1,13 @@ +apply plugin: 'java' +apply plugin: 'maven' + +group = 'com.google.code.gson' +version = '2.8.4-SNAPSHOT' + +sourceCompatibility = 1.6 +targetCompatibility = 1.6 + +sourceSets.main.java.exclude("**/module-info.java") +dependencies { + testCompile "junit:junit:4.12" +} diff --git a/gson/pom.xml b/gson/pom.xml index cf059463..13c2515a 100644 --- a/gson/pom.xml +++ b/gson/pom.xml @@ -4,7 +4,7 @@ com.google.code.gson gson-parent - 2.8.3-SNAPSHOT + 2.8.5-SNAPSHOT gson diff --git a/gson/src/main/java/com/google/gson/DefaultDateTypeAdapter.java b/gson/src/main/java/com/google/gson/DefaultDateTypeAdapter.java index 0cbf77ce..52296379 100644 --- a/gson/src/main/java/com/google/gson/DefaultDateTypeAdapter.java +++ b/gson/src/main/java/com/google/gson/DefaultDateTypeAdapter.java @@ -27,12 +27,12 @@ import java.util.Date; import java.util.List; import java.util.Locale; +import com.google.gson.internal.JavaVersion; import com.google.gson.internal.PreJava9DateFormatProvider; import com.google.gson.internal.bind.util.ISO8601Utils; import com.google.gson.stream.JsonReader; import com.google.gson.stream.JsonToken; import com.google.gson.stream.JsonWriter; -import com.google.gson.util.VersionUtils; /** * This type adapter supports three subclasses of date: Date, Timestamp, and @@ -59,7 +59,7 @@ final class DefaultDateTypeAdapter extends TypeAdapter { if (!Locale.getDefault().equals(Locale.US)) { dateFormats.add(DateFormat.getDateTimeInstance(DateFormat.DEFAULT, DateFormat.DEFAULT)); } - if (VersionUtils.isJava9OrLater()) { + if (JavaVersion.isJava9OrLater()) { dateFormats.add(PreJava9DateFormatProvider.getUSDateTimeFormat(DateFormat.DEFAULT, DateFormat.DEFAULT)); } } @@ -78,7 +78,7 @@ final class DefaultDateTypeAdapter extends TypeAdapter { if (!Locale.getDefault().equals(Locale.US)) { dateFormats.add(DateFormat.getDateInstance(style)); } - if (VersionUtils.isJava9OrLater()) { + if (JavaVersion.isJava9OrLater()) { dateFormats.add(PreJava9DateFormatProvider.getUSDateFormat(style)); } } @@ -93,7 +93,7 @@ final class DefaultDateTypeAdapter extends TypeAdapter { if (!Locale.getDefault().equals(Locale.US)) { dateFormats.add(DateFormat.getDateTimeInstance(dateStyle, timeStyle)); } - if (VersionUtils.isJava9OrLater()) { + if (JavaVersion.isJava9OrLater()) { dateFormats.add(PreJava9DateFormatProvider.getUSDateTimeFormat(dateStyle, timeStyle)); } } diff --git a/gson/src/main/java/com/google/gson/FieldNamingPolicy.java b/gson/src/main/java/com/google/gson/FieldNamingPolicy.java index ad7bb6c8..1f1eb8f1 100644 --- a/gson/src/main/java/com/google/gson/FieldNamingPolicy.java +++ b/gson/src/main/java/com/google/gson/FieldNamingPolicy.java @@ -114,6 +114,29 @@ public enum FieldNamingPolicy implements FieldNamingStrategy { @Override public String translateName(Field f) { return separateCamelCase(f.getName(), "-").toLowerCase(Locale.ENGLISH); } + }, + + /** + * Using this naming policy with Gson will modify the Java Field name from its camel cased + * form to a lower case field name where each word is separated by a dot (.). + * + *

Here's a few examples of the form "Java Field Name" ---> "JSON Field Name":

+ *
    + *
  • someFieldName ---> some.field.name
  • + *
  • _someFieldName ---> _some.field.name
  • + *
  • aStringField ---> a.string.field
  • + *
  • aURL ---> a.u.r.l
  • + *
+ * Using dots in JavaScript is not recommended since dot is also used for a member sign in + * expressions. This requires that a field named with dots is always accessed as a quoted + * property like {@code myobject['my.field']}. Accessing it as an object field + * {@code myobject.my.field} will result in an unintended javascript expression. + * @since 2.8 + */ + LOWER_CASE_WITH_DOTS() { + @Override public String translateName(Field f) { + return separateCamelCase(f.getName(), ".").toLowerCase(Locale.ENGLISH); + } }; /** diff --git a/gson/src/main/java/com/google/gson/JsonNull.java b/gson/src/main/java/com/google/gson/JsonNull.java old mode 100755 new mode 100644 diff --git a/gson/src/main/java/com/google/gson/JsonParser.java b/gson/src/main/java/com/google/gson/JsonParser.java old mode 100755 new mode 100644 diff --git a/gson/src/main/java/com/google/gson/internal/JavaVersion.java b/gson/src/main/java/com/google/gson/internal/JavaVersion.java new file mode 100644 index 00000000..3887e7da --- /dev/null +++ b/gson/src/main/java/com/google/gson/internal/JavaVersion.java @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2017 The Gson authors + * + * 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.internal; + +/** + * Utility to check the major Java version of the current JVM. + */ +public final class JavaVersion { + // Oracle defines naming conventions at http://www.oracle.com/technetwork/java/javase/versioning-naming-139433.html + // However, many alternate implementations differ. For example, Debian used 9-debian as the version string + + private static final int majorJavaVersion = determineMajorJavaVersion(); + + private static int determineMajorJavaVersion() { + String javaVersion = System.getProperty("java.version"); + return getMajorJavaVersion(javaVersion); + } + + // Visible for testing only + static int getMajorJavaVersion(String javaVersion) { + int version = parseDotted(javaVersion); + if (version == -1) { + version = extractBeginningInt(javaVersion); + } + if (version == -1) { + return 6; // Choose minimum supported JDK version as default + } + return version; + } + + // Parses both legacy 1.8 style and newer 9.0.4 style + private static int parseDotted(String javaVersion) { + try { + String[] parts = javaVersion.split("[._]"); + int firstVer = Integer.parseInt(parts[0]); + if (firstVer == 1 && parts.length > 1) { + return Integer.parseInt(parts[1]); + } else { + return firstVer; + } + } catch (NumberFormatException e) { + return -1; + } + } + + private static int extractBeginningInt(String javaVersion) { + try { + StringBuilder num = new StringBuilder(); + for (int i = 0; i < javaVersion.length(); ++i) { + char c = javaVersion.charAt(i); + if (Character.isDigit(c)) { + num.append(c); + } else { + break; + } + } + return Integer.parseInt(num.toString()); + } catch (NumberFormatException e) { + return -1; + } + } + + /** + * @return the major Java version, i.e. '8' for Java 1.8, '9' for Java 9 etc. + */ + public static int getMajorJavaVersion() { + return majorJavaVersion; + } + + /** + * @return {@code true} if the application is running on Java 9 or later; and {@code false} otherwise. + */ + public static boolean isJava9OrLater() { + return majorJavaVersion >= 9; + } +} 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 c3a3de1b..6e849690 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 @@ -20,13 +20,13 @@ import com.google.gson.Gson; import com.google.gson.JsonSyntaxException; import com.google.gson.TypeAdapter; import com.google.gson.TypeAdapterFactory; +import com.google.gson.internal.JavaVersion; import com.google.gson.internal.PreJava9DateFormatProvider; import com.google.gson.internal.bind.util.ISO8601Utils; 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 com.google.gson.util.VersionUtils; import java.io.IOException; import java.text.DateFormat; @@ -62,7 +62,7 @@ public final class DateTypeAdapter extends TypeAdapter { if (!Locale.getDefault().equals(Locale.US)) { dateFormats.add(DateFormat.getDateTimeInstance(DateFormat.DEFAULT, DateFormat.DEFAULT)); } - if (VersionUtils.isJava9OrLater()) { + if (JavaVersion.isJava9OrLater()) { dateFormats.add(PreJava9DateFormatProvider.getUSDateTimeFormat(DateFormat.DEFAULT, DateFormat.DEFAULT)); } } diff --git a/gson/src/main/java/com/google/gson/internal/reflect/PreJava9ReflectionAccessor.java b/gson/src/main/java/com/google/gson/internal/reflect/PreJava9ReflectionAccessor.java index 2f006517..325274e2 100644 --- a/gson/src/main/java/com/google/gson/internal/reflect/PreJava9ReflectionAccessor.java +++ b/gson/src/main/java/com/google/gson/internal/reflect/PreJava9ReflectionAccessor.java @@ -25,12 +25,9 @@ import java.lang.reflect.AccessibleObject; */ final class PreJava9ReflectionAccessor extends ReflectionAccessor { - /** - * {@inheritDoc} - */ + /** {@inheritDoc} */ @Override public void makeAccessible(AccessibleObject ao) { ao.setAccessible(true); } - } diff --git a/gson/src/main/java/com/google/gson/internal/reflect/ReflectionAccessor.java b/gson/src/main/java/com/google/gson/internal/reflect/ReflectionAccessor.java index 42230d25..6816feaf 100644 --- a/gson/src/main/java/com/google/gson/internal/reflect/ReflectionAccessor.java +++ b/gson/src/main/java/com/google/gson/internal/reflect/ReflectionAccessor.java @@ -15,10 +15,10 @@ */ package com.google.gson.internal.reflect; -import com.google.gson.util.VersionUtils; - import java.lang.reflect.AccessibleObject; +import com.google.gson.internal.JavaVersion; + /** * Provides a replacement for {@link AccessibleObject#setAccessible(boolean)}, which may be used to * avoid reflective access issues appeared in Java 9, like {@link java.lang.reflect.InaccessibleObjectException} @@ -33,7 +33,7 @@ import java.lang.reflect.AccessibleObject; public abstract class ReflectionAccessor { // the singleton instance, use getInstance() to obtain - private static final ReflectionAccessor instance = VersionUtils.getMajorJavaVersion() < 9 ? new PreJava9ReflectionAccessor() : new UnsafeReflectionAccessor(); + private static final ReflectionAccessor instance = JavaVersion.getMajorJavaVersion() < 9 ? new PreJava9ReflectionAccessor() : new UnsafeReflectionAccessor(); /** * Does the same as {@code ao.setAccessible(true)}, but never throws diff --git a/gson/src/main/java/com/google/gson/internal/reflect/UnsafeReflectionAccessor.java b/gson/src/main/java/com/google/gson/internal/reflect/UnsafeReflectionAccessor.java index 5bc59bd8..749335b7 100644 --- a/gson/src/main/java/com/google/gson/internal/reflect/UnsafeReflectionAccessor.java +++ b/gson/src/main/java/com/google/gson/internal/reflect/UnsafeReflectionAccessor.java @@ -15,10 +15,11 @@ */ package com.google.gson.internal.reflect; -import sun.misc.Unsafe; - import java.lang.reflect.AccessibleObject; import java.lang.reflect.Field; +import java.lang.reflect.Method; + +import com.google.gson.JsonIOException; /** * An implementation of {@link ReflectionAccessor} based on {@link Unsafe}. @@ -26,29 +27,51 @@ import java.lang.reflect.Field; * NOTE: This implementation is designed for Java 9. Although it should work with earlier Java releases, it is better to * use {@link PreJava9ReflectionAccessor} for them. */ +@SuppressWarnings({"unchecked", "rawtypes"}) final class UnsafeReflectionAccessor extends ReflectionAccessor { - private final Unsafe theUnsafe = getUnsafeInstance(); + private static Class unsafeClass; + private final Object theUnsafe = getUnsafeInstance(); private final Field overrideField = getOverrideField(); - /** - * {@inheritDoc} - */ + /** {@inheritDoc} */ @Override public void makeAccessible(AccessibleObject ao) { - if (theUnsafe != null && overrideField != null) { - long overrideOffset = theUnsafe.objectFieldOffset(overrideField); - theUnsafe.putBoolean(ao, overrideOffset, true); + boolean success = makeAccessibleWithUnsafe(ao); + if (!success) { + try { + // unsafe couldn't be found, so try using accessible anyway + ao.setAccessible(true); + } catch (SecurityException e) { + throw new JsonIOException("Gson couldn't modify fields for " + ao + + "\nand sun.misc.Unsafe not found.\nEither write a custom type adapter," + + " or make fields accessible, or include sun.misc.Unsafe.", e); + } } } - private static Unsafe getUnsafeInstance() { + // Visible for testing only + boolean makeAccessibleWithUnsafe(AccessibleObject ao) { + if (theUnsafe != null && overrideField != null) { + try { + Method method = unsafeClass.getMethod("objectFieldOffset", Field.class); + long overrideOffset = (Long) method.invoke(theUnsafe, overrideField); // long overrideOffset = theUnsafe.objectFieldOffset(overrideField); + Method putBooleanMethod = unsafeClass.getMethod("putBoolean", Object.class, long.class, boolean.class); + putBooleanMethod.invoke(theUnsafe, ao, overrideOffset, true); // theUnsafe.putBoolean(ao, overrideOffset, true); + return true; + } catch (Exception ignored) { // do nothing + } + } + return false; + } + + private static Object getUnsafeInstance() { try { - Field unsafeField = Unsafe.class.getDeclaredField("theUnsafe"); + unsafeClass = Class.forName("sun.misc.Unsafe"); + Field unsafeField = unsafeClass.getDeclaredField("theUnsafe"); unsafeField.setAccessible(true); - return (Unsafe) unsafeField.get(null); + return unsafeField.get(null); } catch (Exception e) { - e.printStackTrace(); return null; } } @@ -57,7 +80,6 @@ final class UnsafeReflectionAccessor extends ReflectionAccessor { try { return AccessibleObject.class.getDeclaredField("override"); } catch (NoSuchFieldException e) { - e.printStackTrace(); return null; } } diff --git a/gson/src/main/java/com/google/gson/util/VersionUtils.java b/gson/src/main/java/com/google/gson/util/VersionUtils.java deleted file mode 100644 index d81e43c0..00000000 --- a/gson/src/main/java/com/google/gson/util/VersionUtils.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (C) 2017 The Gson authors - * - * 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.util; - -/** - * Utility to check the major Java version of the current JVM. - */ -public class VersionUtils { - - private static final int majorJavaVersion = determineMajorJavaVersion(); - - private static int determineMajorJavaVersion() { - String[] parts = System.getProperty("java.version").split("[._]"); - int firstVer = Integer.parseInt(parts[0]); - if (firstVer == 1 && parts.length > 1) { - return Integer.parseInt(parts[1]); - } else { - return firstVer; - } - } - - /** - * @return the major Java version, i.e. '8' for Java 1.8, '9' for Java 9 etc. - */ - public static int getMajorJavaVersion() { - return majorJavaVersion; - } - - /** - * @return {@code true} if the application is running on Java 9 or later; and {@code false} otherwise. - */ - public static boolean isJava9OrLater() { - return majorJavaVersion >= 9; - } -} diff --git a/gson/src/test/java/com/google/gson/DefaultDateTypeAdapterTest.java b/gson/src/test/java/com/google/gson/DefaultDateTypeAdapterTest.java index a074bea0..679feb83 100644 --- a/gson/src/test/java/com/google/gson/DefaultDateTypeAdapterTest.java +++ b/gson/src/test/java/com/google/gson/DefaultDateTypeAdapterTest.java @@ -23,7 +23,8 @@ import java.util.Date; import java.util.Locale; import java.util.TimeZone; -import com.google.gson.util.VersionUtils; +import com.google.gson.internal.JavaVersion; + import junit.framework.TestCase; /** @@ -47,9 +48,9 @@ public class DefaultDateTypeAdapterTest extends TestCase { Locale defaultLocale = Locale.getDefault(); Locale.setDefault(locale); try { - String afterYearSep = VersionUtils.isJava9OrLater() ? ", " : " "; - String afterYearLongSep = VersionUtils.isJava9OrLater() ? " at " : " "; - String utcFull = VersionUtils.isJava9OrLater() ? "Coordinated Universal Time" : "UTC"; + String afterYearSep = JavaVersion.isJava9OrLater() ? ", " : " "; + String afterYearLongSep = JavaVersion.isJava9OrLater() ? " at " : " "; + String utcFull = JavaVersion.isJava9OrLater() ? "Coordinated Universal Time" : "UTC"; assertFormatted(String.format("Jan 1, 1970%s12:00:00 AM", afterYearSep), new DefaultDateTypeAdapter(Date.class)); assertFormatted("1/1/70", new DefaultDateTypeAdapter(Date.class, DateFormat.SHORT)); @@ -75,7 +76,7 @@ public class DefaultDateTypeAdapterTest extends TestCase { Locale defaultLocale = Locale.getDefault(); Locale.setDefault(Locale.FRANCE); try { - String afterYearSep = VersionUtils.isJava9OrLater() ? " à " : " "; + String afterYearSep = JavaVersion.isJava9OrLater() ? " à " : " "; assertParsed(String.format("1 janv. 1970%s00:00:00", afterYearSep), new DefaultDateTypeAdapter(Date.class)); assertParsed("01/01/70", new DefaultDateTypeAdapter(Date.class, DateFormat.SHORT)); @@ -87,7 +88,7 @@ public class DefaultDateTypeAdapterTest extends TestCase { new DefaultDateTypeAdapter(DateFormat.MEDIUM, DateFormat.MEDIUM)); assertParsed(String.format("1 janvier 1970%s00:00:00 UTC", afterYearSep), new DefaultDateTypeAdapter(DateFormat.LONG, DateFormat.LONG)); - assertParsed(VersionUtils.isJava9OrLater() ? + assertParsed(JavaVersion.isJava9OrLater() ? "jeudi 1 janvier 1970 à 00:00:00 Coordinated Universal Time" : "jeudi 1 janvier 1970 00 h 00 UTC", new DefaultDateTypeAdapter(DateFormat.FULL, DateFormat.FULL)); @@ -127,7 +128,7 @@ public class DefaultDateTypeAdapterTest extends TestCase { Locale defaultLocale = Locale.getDefault(); Locale.setDefault(Locale.US); try { - String afterYearSep = VersionUtils.isJava9OrLater() ? ", " : " "; + String afterYearSep = JavaVersion.isJava9OrLater() ? ", " : " "; assertFormatted(String.format("Dec 31, 1969%s4:00:00 PM", afterYearSep), new DefaultDateTypeAdapter(Date.class)); assertParsed("Dec 31, 1969 4:00:00 PM", new DefaultDateTypeAdapter(Date.class)); diff --git a/gson/src/test/java/com/google/gson/GsonBuilderTest.java b/gson/src/test/java/com/google/gson/GsonBuilderTest.java old mode 100755 new mode 100644 diff --git a/gson/src/test/java/com/google/gson/functional/ConcurrencyTest.java b/gson/src/test/java/com/google/gson/functional/ConcurrencyTest.java old mode 100755 new mode 100644 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 635c2088..f76dd18c 100644 --- a/gson/src/test/java/com/google/gson/functional/DefaultTypeAdaptersTest.java +++ b/gson/src/test/java/com/google/gson/functional/DefaultTypeAdaptersTest.java @@ -27,6 +27,7 @@ import com.google.gson.JsonParseException; import com.google.gson.JsonPrimitive; import com.google.gson.JsonSyntaxException; import com.google.gson.TypeAdapter; +import com.google.gson.internal.JavaVersion; import com.google.gson.reflect.TypeToken; import com.google.gson.stream.JsonReader; import com.google.gson.stream.JsonWriter; @@ -56,7 +57,6 @@ import java.util.TimeZone; import java.util.TreeSet; import java.util.UUID; -import com.google.gson.util.VersionUtils; import junit.framework.TestCase; /** @@ -330,7 +330,7 @@ public class DefaultTypeAdaptersTest extends TestCase { public void testDefaultDateSerialization() { Date now = new Date(1315806903103L); String json = gson.toJson(now); - if (VersionUtils.isJava9OrLater()) { + if (JavaVersion.isJava9OrLater()) { assertEquals("\"Sep 11, 2011, 10:55:03 PM\"", json); } else { assertEquals("\"Sep 11, 2011 10:55:03 PM\"", json); @@ -375,7 +375,7 @@ public class DefaultTypeAdaptersTest extends TestCase { public void testDefaultJavaSqlTimestampSerialization() { Timestamp now = new java.sql.Timestamp(1259875082000L); String json = gson.toJson(now); - if (VersionUtils.isJava9OrLater()) { + if (JavaVersion.isJava9OrLater()) { assertEquals("\"Dec 3, 2009, 1:18:02 PM\"", json); } else { assertEquals("\"Dec 3, 2009 1:18:02 PM\"", json); @@ -405,7 +405,7 @@ public class DefaultTypeAdaptersTest extends TestCase { Gson gson = new GsonBuilder().create(); Date now = new Date(1315806903103L); String json = gson.toJson(now); - if (VersionUtils.isJava9OrLater()) { + if (JavaVersion.isJava9OrLater()) { assertEquals("\"Sep 11, 2011, 10:55:03 PM\"", json); } else { assertEquals("\"Sep 11, 2011 10:55:03 PM\"", json); diff --git a/gson/src/test/java/com/google/gson/functional/MapTest.java b/gson/src/test/java/com/google/gson/functional/MapTest.java old mode 100755 new mode 100644 diff --git a/gson/src/test/java/com/google/gson/functional/NamingPolicyTest.java b/gson/src/test/java/com/google/gson/functional/NamingPolicyTest.java index c3c0be1d..5b1bba5b 100644 --- a/gson/src/test/java/com/google/gson/functional/NamingPolicyTest.java +++ b/gson/src/test/java/com/google/gson/functional/NamingPolicyTest.java @@ -63,6 +63,20 @@ public class NamingPolicyTest extends TestCase { + target.someConstantStringInstanceField + "\"}", gson.toJson(target)); } + public void testGsonWithLowerCaseDotPolicySerialization() { + Gson gson = builder.setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_DOTS).create(); + StringWrapper target = new StringWrapper("blah"); + assertEquals("{\"some.constant.string.instance.field\":\"" + + target.someConstantStringInstanceField + "\"}", gson.toJson(target)); + } + + public void testGsonWithLowerCaseDotPolicyDeserialiation() { + Gson gson = builder.setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_DOTS).create(); + String target = "{\"some.constant.string.instance.field\":\"someValue\"}"; + StringWrapper deserializedObject = gson.fromJson(target, StringWrapper.class); + assertEquals("someValue", deserializedObject.someConstantStringInstanceField); + } + public void testGsonWithLowerCaseDashPolicyDeserialiation() { Gson gson = builder.setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_DASHES).create(); String target = "{\"some-constant-string-instance-field\":\"someValue\"}"; diff --git a/gson/src/test/java/com/google/gson/functional/NullObjectAndFieldTest.java b/gson/src/test/java/com/google/gson/functional/NullObjectAndFieldTest.java old mode 100755 new mode 100644 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 cf82457a..48508f83 100644 --- a/gson/src/test/java/com/google/gson/functional/ObjectTest.java +++ b/gson/src/test/java/com/google/gson/functional/ObjectTest.java @@ -33,6 +33,7 @@ import com.google.gson.common.TestTypes.ClassWithObjects; import com.google.gson.common.TestTypes.ClassWithTransientFields; import com.google.gson.common.TestTypes.Nested; import com.google.gson.common.TestTypes.PrimitiveArray; +import com.google.gson.internal.JavaVersion; import com.google.gson.reflect.TypeToken; import java.lang.reflect.Type; import java.util.ArrayList; @@ -44,7 +45,6 @@ import java.util.Locale; import java.util.Map; import java.util.TimeZone; -import com.google.gson.util.VersionUtils; import junit.framework.TestCase; /** @@ -484,7 +484,7 @@ public class ObjectTest extends TestCase { public void testDateAsMapObjectField() { HasObjectMap a = new HasObjectMap(); a.map.put("date", new Date(0)); - if (VersionUtils.isJava9OrLater()) { + if (JavaVersion.isJava9OrLater()) { assertEquals("{\"map\":{\"date\":\"Dec 31, 1969, 4:00:00 PM\"}}", gson.toJson(a)); } else { assertEquals("{\"map\":{\"date\":\"Dec 31, 1969 4:00:00 PM\"}}", gson.toJson(a)); diff --git a/gson/src/test/java/com/google/gson/internal/JavaVersionTest.java b/gson/src/test/java/com/google/gson/internal/JavaVersionTest.java new file mode 100644 index 00000000..4768c2db --- /dev/null +++ b/gson/src/test/java/com/google/gson/internal/JavaVersionTest.java @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2017 The Gson authors + * + * 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.internal; + +import static org.junit.Assert.*; + +import org.junit.Test; + +import com.google.gson.internal.JavaVersion; + +/** + * Unit and functional tests for {@link JavaVersion} + * + * @author Inderjeet Singh + */ +public class JavaVersionTest { + // Borrowed some of test strings from https://github.com/prestodb/presto/blob/master/presto-main/src/test/java/com/facebook/presto/server/TestJavaVersion.java + + @Test + public void testGetMajorJavaVersion() { + JavaVersion.getMajorJavaVersion(); + } + + @Test + public void testJava6() { + assertEquals(6, JavaVersion.getMajorJavaVersion("1.6.0")); // http://www.oracle.com/technetwork/java/javase/version-6-141920.html + } + + @Test + public void testJava7() { + assertEquals(7, JavaVersion.getMajorJavaVersion("1.7.0")); // http://www.oracle.com/technetwork/java/javase/jdk7-naming-418744.html + } + + @Test + public void testJava8() { + assertEquals(8, JavaVersion.getMajorJavaVersion("1.8")); + assertEquals(8, JavaVersion.getMajorJavaVersion("1.8.0")); + assertEquals(8, JavaVersion.getMajorJavaVersion("1.8.0_131")); + assertEquals(8, JavaVersion.getMajorJavaVersion("1.8.0_60-ea")); + assertEquals(8, JavaVersion.getMajorJavaVersion("1.8.0_111-internal")); + + // openjdk8 per https://github.com/AdoptOpenJDK/openjdk-build/issues/93 + assertEquals(8, JavaVersion.getMajorJavaVersion("1.8.0-internal")); + assertEquals(8, JavaVersion.getMajorJavaVersion("1.8.0_131-adoptopenjdk")); + } + + @Test + public void testJava9() { + // Legacy style + assertEquals(9, JavaVersion.getMajorJavaVersion("9.0.4")); // Oracle JDK 9 + assertEquals(9, JavaVersion.getMajorJavaVersion("9-Debian")); // Debian as reported in https://github.com/google/gson/issues/1310 + // New style + assertEquals(9, JavaVersion.getMajorJavaVersion("9-ea+19")); + assertEquals(9, JavaVersion.getMajorJavaVersion("9+100")); + assertEquals(9, JavaVersion.getMajorJavaVersion("9.0.1+20")); + assertEquals(9, JavaVersion.getMajorJavaVersion("9.1.1+20")); + } + + @Test + public void testJava10() { + assertEquals(10, JavaVersion.getMajorJavaVersion("10.0.1")); // Oracle JDK 10.0.1 + } + + @Test + public void testUnknownVersionFormat() { + assertEquals(6, JavaVersion.getMajorJavaVersion("Java9")); // unknown format + } +} diff --git a/gson/src/test/java/com/google/gson/internal/reflect/UnsafeReflectionAccessorTest.java b/gson/src/test/java/com/google/gson/internal/reflect/UnsafeReflectionAccessorTest.java new file mode 100644 index 00000000..d5caaf53 --- /dev/null +++ b/gson/src/test/java/com/google/gson/internal/reflect/UnsafeReflectionAccessorTest.java @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2018 The Gson authors + * + * 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.internal.reflect; + +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import java.lang.reflect.Field; + +import org.junit.Test; + +/** + * Unit tests for {@link UnsafeReflectionAccessor} + * + * @author Inderjeet Singh + */ +public class UnsafeReflectionAccessorTest { + + @Test + public void testMakeAccessibleWithUnsafe() throws Exception { + UnsafeReflectionAccessor accessor = new UnsafeReflectionAccessor(); + Field field = ClassWithPrivateFinalFields.class.getDeclaredField("a"); + try { + boolean success = accessor.makeAccessibleWithUnsafe(field); + assertTrue(success); + } catch (Exception e) { + fail("Unsafe didn't work on the JDK"); + } + } + + @SuppressWarnings("unused") + private static final class ClassWithPrivateFinalFields { + private final String a; + public ClassWithPrivateFinalFields(String a) { + this.a = a; + } + } +} diff --git a/pom.xml b/pom.xml index c6b454b5..e6c90c2f 100644 --- a/pom.xml +++ b/pom.xml @@ -11,7 +11,7 @@ com.google.code.gson gson-parent - 2.8.3-SNAPSHOT + 2.8.5-SNAPSHOT pom Gson Parent @@ -65,7 +65,9 @@ maven-compiler-plugin 3.7.0 - **/module-info.java + + **/module-info.java + ${java.version} ${java.version} diff --git a/settings.gradle b/settings.gradle new file mode 100644 index 00000000..975ec9b3 --- /dev/null +++ b/settings.gradle @@ -0,0 +1 @@ +include ':gson'