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/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/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/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/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/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 + } +}