diff --git a/gson/src/main/java/com/google/gson/DefaultDateTypeAdapter.java b/gson/src/main/java/com/google/gson/DefaultDateTypeAdapter.java index 52296379..81b809be 100644 --- a/gson/src/main/java/com/google/gson/DefaultDateTypeAdapter.java +++ b/gson/src/main/java/com/google/gson/DefaultDateTypeAdapter.java @@ -41,11 +41,52 @@ import com.google.gson.stream.JsonWriter; * @author Inderjeet Singh * @author Joel Leitch */ -final class DefaultDateTypeAdapter extends TypeAdapter { - +final class DefaultDateTypeAdapter extends TypeAdapter { private static final String SIMPLE_NAME = "DefaultDateTypeAdapter"; - private final Class dateType; + static abstract class DateType { + private DateType() { + } + + public static final DateType DATE = new DateType() { + @Override + protected Date deserialize(Date date) { + return date; + } + }; + public static final DateType SQL_DATE = new DateType() { + @Override + protected java.sql.Date deserialize(Date date) { + return new java.sql.Date(date.getTime()); + } + }; + public static final DateType SQL_TIMESTAMP = new DateType() { + @Override + protected Timestamp deserialize(Date date) { + return new Timestamp(date.getTime()); + } + }; + + protected abstract T deserialize(Date date); + + public DefaultDateTypeAdapter createAdapter(String datePattern) { + return new DefaultDateTypeAdapter(this, datePattern); + } + + public DefaultDateTypeAdapter createAdapter(int style) { + return new DefaultDateTypeAdapter(this, style); + } + + public DefaultDateTypeAdapter createAdapter(int dateStyle, int timeStyle) { + return new DefaultDateTypeAdapter(this, dateStyle, timeStyle); + } + + public DefaultDateTypeAdapter createDefaultsAdapter() { + return new DefaultDateTypeAdapter(this, DateFormat.DEFAULT, DateFormat.DEFAULT); + } + } + + private final DateType dateType; /** * List of 1 or more different date formats used for de-serialization attempts. @@ -53,18 +94,7 @@ final class DefaultDateTypeAdapter extends TypeAdapter { */ private final List dateFormats = new ArrayList(); - DefaultDateTypeAdapter(Class dateType) { - this.dateType = verifyDateType(dateType); - dateFormats.add(DateFormat.getDateTimeInstance(DateFormat.DEFAULT, DateFormat.DEFAULT, Locale.US)); - if (!Locale.getDefault().equals(Locale.US)) { - dateFormats.add(DateFormat.getDateTimeInstance(DateFormat.DEFAULT, DateFormat.DEFAULT)); - } - if (JavaVersion.isJava9OrLater()) { - dateFormats.add(PreJava9DateFormatProvider.getUSDateTimeFormat(DateFormat.DEFAULT, DateFormat.DEFAULT)); - } - } - - DefaultDateTypeAdapter(Class dateType, String datePattern) { + private DefaultDateTypeAdapter(DateType dateType, String datePattern) { this.dateType = verifyDateType(dateType); dateFormats.add(new SimpleDateFormat(datePattern, Locale.US)); if (!Locale.getDefault().equals(Locale.US)) { @@ -72,7 +102,7 @@ final class DefaultDateTypeAdapter extends TypeAdapter { } } - DefaultDateTypeAdapter(Class dateType, int style) { + private DefaultDateTypeAdapter(DateType dateType, int style) { this.dateType = verifyDateType(dateType); dateFormats.add(DateFormat.getDateInstance(style, Locale.US)); if (!Locale.getDefault().equals(Locale.US)) { @@ -83,11 +113,7 @@ final class DefaultDateTypeAdapter extends TypeAdapter { } } - public DefaultDateTypeAdapter(int dateStyle, int timeStyle) { - this(Date.class, dateStyle, timeStyle); - } - - public DefaultDateTypeAdapter(Class dateType, int dateStyle, int timeStyle) { + private DefaultDateTypeAdapter(DateType dateType, int dateStyle, int timeStyle) { this.dateType = verifyDateType(dateType); dateFormats.add(DateFormat.getDateTimeInstance(dateStyle, timeStyle, Locale.US)); if (!Locale.getDefault().equals(Locale.US)) { @@ -98,9 +124,9 @@ final class DefaultDateTypeAdapter extends TypeAdapter { } } - private static Class verifyDateType(Class dateType) { - if ( dateType != Date.class && dateType != java.sql.Date.class && dateType != Timestamp.class ) { - throw new IllegalArgumentException("Date type must be one of " + Date.class + ", " + Timestamp.class + ", or " + java.sql.Date.class + " but was " + dateType); + private static DateType verifyDateType(DateType dateType) { + if (dateType == null) { + throw new NullPointerException("dateType == null"); } return dateType; } @@ -120,22 +146,13 @@ final class DefaultDateTypeAdapter extends TypeAdapter { } @Override - public Date read(JsonReader in) throws IOException { + public T read(JsonReader in) throws IOException { if (in.peek() == JsonToken.NULL) { in.nextNull(); return null; } Date date = deserializeToDate(in.nextString()); - if (dateType == Date.class) { - return date; - } else if (dateType == Timestamp.class) { - return new Timestamp(date.getTime()); - } else if (dateType == java.sql.Date.class) { - return new java.sql.Date(date.getTime()); - } else { - // This must never happen: dateType is guarded in the primary constructor - throw new AssertionError(); - } + return dateType.deserialize(date); } private Date deserializeToDate(String s) { diff --git a/gson/src/main/java/com/google/gson/GsonBuilder.java b/gson/src/main/java/com/google/gson/GsonBuilder.java index b97be452..2c8dbe03 100644 --- a/gson/src/main/java/com/google/gson/GsonBuilder.java +++ b/gson/src/main/java/com/google/gson/GsonBuilder.java @@ -602,20 +602,19 @@ public final class GsonBuilder { this.factories, this.hierarchyFactories, factories); } - @SuppressWarnings("unchecked") private void addTypeAdaptersForDate(String datePattern, int dateStyle, int timeStyle, List factories) { - DefaultDateTypeAdapter dateTypeAdapter; + DefaultDateTypeAdapter dateTypeAdapter; TypeAdapter timestampTypeAdapter; TypeAdapter javaSqlDateTypeAdapter; if (datePattern != null && !"".equals(datePattern.trim())) { - dateTypeAdapter = new DefaultDateTypeAdapter(Date.class, datePattern); - timestampTypeAdapter = (TypeAdapter) new DefaultDateTypeAdapter(Timestamp.class, datePattern); - javaSqlDateTypeAdapter = (TypeAdapter) new DefaultDateTypeAdapter(java.sql.Date.class, datePattern); + dateTypeAdapter = DefaultDateTypeAdapter.DateType.DATE.createAdapter(datePattern); + timestampTypeAdapter = DefaultDateTypeAdapter.DateType.SQL_TIMESTAMP.createAdapter(datePattern); + javaSqlDateTypeAdapter = DefaultDateTypeAdapter.DateType.SQL_DATE.createAdapter(datePattern); } else if (dateStyle != DateFormat.DEFAULT && timeStyle != DateFormat.DEFAULT) { - dateTypeAdapter = new DefaultDateTypeAdapter(Date.class, dateStyle, timeStyle); - timestampTypeAdapter = (TypeAdapter) new DefaultDateTypeAdapter(Timestamp.class, dateStyle, timeStyle); - javaSqlDateTypeAdapter = (TypeAdapter) new DefaultDateTypeAdapter(java.sql.Date.class, dateStyle, timeStyle); + dateTypeAdapter = DefaultDateTypeAdapter.DateType.DATE.createAdapter(dateStyle, timeStyle); + timestampTypeAdapter = DefaultDateTypeAdapter.DateType.SQL_TIMESTAMP.createAdapter(dateStyle, timeStyle); + javaSqlDateTypeAdapter = DefaultDateTypeAdapter.DateType.SQL_DATE.createAdapter(dateStyle, timeStyle); } else { return; } diff --git a/gson/src/test/java/com/google/gson/DefaultDateTypeAdapterTest.java b/gson/src/test/java/com/google/gson/DefaultDateTypeAdapterTest.java index 632a482d..e626ea7a 100644 --- a/gson/src/test/java/com/google/gson/DefaultDateTypeAdapterTest.java +++ b/gson/src/test/java/com/google/gson/DefaultDateTypeAdapterTest.java @@ -23,6 +23,7 @@ import java.util.Date; import java.util.Locale; import java.util.TimeZone; +import com.google.gson.DefaultDateTypeAdapter.DateType; import com.google.gson.internal.JavaVersion; import junit.framework.TestCase; @@ -52,18 +53,18 @@ public class DefaultDateTypeAdapterTest extends TestCase { 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)); - assertFormatted("Jan 1, 1970", new DefaultDateTypeAdapter(Date.class, DateFormat.MEDIUM)); - assertFormatted("January 1, 1970", new DefaultDateTypeAdapter(Date.class, DateFormat.LONG)); + DateType.DATE.createDefaultsAdapter()); + assertFormatted("1/1/70", DateType.DATE.createAdapter(DateFormat.SHORT)); + assertFormatted("Jan 1, 1970", DateType.DATE.createAdapter(DateFormat.MEDIUM)); + assertFormatted("January 1, 1970", DateType.DATE.createAdapter(DateFormat.LONG)); assertFormatted(String.format("1/1/70%s12:00 AM", afterYearSep), - new DefaultDateTypeAdapter(DateFormat.SHORT, DateFormat.SHORT)); + DateType.DATE.createAdapter(DateFormat.SHORT, DateFormat.SHORT)); assertFormatted(String.format("Jan 1, 1970%s12:00:00 AM", afterYearSep), - new DefaultDateTypeAdapter(DateFormat.MEDIUM, DateFormat.MEDIUM)); + DateType.DATE.createAdapter(DateFormat.MEDIUM, DateFormat.MEDIUM)); assertFormatted(String.format("January 1, 1970%s12:00:00 AM UTC", afterYearLongSep), - new DefaultDateTypeAdapter(DateFormat.LONG, DateFormat.LONG)); + DateType.DATE.createAdapter(DateFormat.LONG, DateFormat.LONG)); assertFormatted(String.format("Thursday, January 1, 1970%s12:00:00 AM %s", afterYearLongSep, utcFull), - new DefaultDateTypeAdapter(DateFormat.FULL, DateFormat.FULL)); + DateType.DATE.createAdapter(DateFormat.FULL, DateFormat.FULL)); } finally { TimeZone.setDefault(defaultTimeZone); Locale.setDefault(defaultLocale); @@ -78,21 +79,21 @@ public class DefaultDateTypeAdapterTest extends TestCase { try { 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)); - assertParsed("1 janv. 1970", new DefaultDateTypeAdapter(Date.class, DateFormat.MEDIUM)); - assertParsed("1 janvier 1970", new DefaultDateTypeAdapter(Date.class, DateFormat.LONG)); + DateType.DATE.createDefaultsAdapter()); + assertParsed("01/01/70", DateType.DATE.createAdapter(DateFormat.SHORT)); + assertParsed("1 janv. 1970", DateType.DATE.createAdapter(DateFormat.MEDIUM)); + assertParsed("1 janvier 1970", DateType.DATE.createAdapter(DateFormat.LONG)); assertParsed("01/01/70 00:00", - new DefaultDateTypeAdapter(DateFormat.SHORT, DateFormat.SHORT)); + DateType.DATE.createAdapter(DateFormat.SHORT, DateFormat.SHORT)); assertParsed(String.format("1 janv. 1970%s00:00:00", afterYearSep), - new DefaultDateTypeAdapter(DateFormat.MEDIUM, DateFormat.MEDIUM)); + DateType.DATE.createAdapter(DateFormat.MEDIUM, DateFormat.MEDIUM)); assertParsed(String.format("1 janvier 1970%s00:00:00 UTC", afterYearSep), - new DefaultDateTypeAdapter(DateFormat.LONG, DateFormat.LONG)); + DateType.DATE.createAdapter(DateFormat.LONG, DateFormat.LONG)); assertParsed(JavaVersion.isJava9OrLater() ? (JavaVersion.getMajorJavaVersion() <11 ? "jeudi 1 janvier 1970 à 00:00:00 Coordinated Universal Time" : "jeudi 1 janvier 1970 à 00:00:00 Temps universel coordonné") : "jeudi 1 janvier 1970 00 h 00 UTC", - new DefaultDateTypeAdapter(DateFormat.FULL, DateFormat.FULL)); + DateType.DATE.createAdapter(DateFormat.FULL, DateFormat.FULL)); } finally { TimeZone.setDefault(defaultTimeZone); Locale.setDefault(defaultLocale); @@ -105,18 +106,18 @@ public class DefaultDateTypeAdapterTest extends TestCase { Locale defaultLocale = Locale.getDefault(); Locale.setDefault(Locale.US); try { - assertParsed("Jan 1, 1970 0:00:00 AM", new DefaultDateTypeAdapter(Date.class)); - assertParsed("1/1/70", new DefaultDateTypeAdapter(Date.class, DateFormat.SHORT)); - assertParsed("Jan 1, 1970", new DefaultDateTypeAdapter(Date.class, DateFormat.MEDIUM)); - assertParsed("January 1, 1970", new DefaultDateTypeAdapter(Date.class, DateFormat.LONG)); + assertParsed("Jan 1, 1970 0:00:00 AM", DateType.DATE.createDefaultsAdapter()); + assertParsed("1/1/70", DateType.DATE.createAdapter(DateFormat.SHORT)); + assertParsed("Jan 1, 1970", DateType.DATE.createAdapter(DateFormat.MEDIUM)); + assertParsed("January 1, 1970", DateType.DATE.createAdapter(DateFormat.LONG)); assertParsed("1/1/70 0:00 AM", - new DefaultDateTypeAdapter(DateFormat.SHORT, DateFormat.SHORT)); + DateType.DATE.createAdapter(DateFormat.SHORT, DateFormat.SHORT)); assertParsed("Jan 1, 1970 0:00:00 AM", - new DefaultDateTypeAdapter(DateFormat.MEDIUM, DateFormat.MEDIUM)); + DateType.DATE.createAdapter(DateFormat.MEDIUM, DateFormat.MEDIUM)); assertParsed("January 1, 1970 0:00:00 AM UTC", - new DefaultDateTypeAdapter(DateFormat.LONG, DateFormat.LONG)); + DateType.DATE.createAdapter(DateFormat.LONG, DateFormat.LONG)); assertParsed("Thursday, January 1, 1970 0:00:00 AM UTC", - new DefaultDateTypeAdapter(DateFormat.FULL, DateFormat.FULL)); + DateType.DATE.createAdapter(DateFormat.FULL, DateFormat.FULL)); } finally { TimeZone.setDefault(defaultTimeZone); Locale.setDefault(defaultLocale); @@ -131,8 +132,8 @@ public class DefaultDateTypeAdapterTest extends TestCase { try { 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)); + DateType.DATE.createDefaultsAdapter()); + assertParsed("Dec 31, 1969 4:00:00 PM", DateType.DATE.createDefaultsAdapter()); } finally { TimeZone.setDefault(defaultTimeZone); Locale.setDefault(defaultLocale); @@ -140,7 +141,7 @@ public class DefaultDateTypeAdapterTest extends TestCase { } public void testDateDeserializationISO8601() throws Exception { - DefaultDateTypeAdapter adapter = new DefaultDateTypeAdapter(Date.class); + DefaultDateTypeAdapter adapter = DateType.DATE.createDefaultsAdapter(); assertParsed("1970-01-01T00:00:00.000Z", adapter); assertParsed("1970-01-01T00:00Z", adapter); assertParsed("1970-01-01T00:00:00+00:00", adapter); @@ -150,7 +151,7 @@ public class DefaultDateTypeAdapterTest extends TestCase { public void testDateSerialization() throws Exception { int dateStyle = DateFormat.LONG; - DefaultDateTypeAdapter dateTypeAdapter = new DefaultDateTypeAdapter(Date.class, dateStyle); + DefaultDateTypeAdapter dateTypeAdapter = DateType.DATE.createAdapter(dateStyle); DateFormat formatter = DateFormat.getDateInstance(dateStyle, Locale.US); Date currentDate = new Date(); @@ -160,7 +161,7 @@ public class DefaultDateTypeAdapterTest extends TestCase { public void testDatePattern() throws Exception { String pattern = "yyyy-MM-dd"; - DefaultDateTypeAdapter dateTypeAdapter = new DefaultDateTypeAdapter(Date.class, pattern); + DefaultDateTypeAdapter dateTypeAdapter = DateType.DATE.createAdapter(pattern); DateFormat formatter = new SimpleDateFormat(pattern); Date currentDate = new Date(); @@ -168,33 +169,32 @@ public class DefaultDateTypeAdapterTest extends TestCase { assertEquals(toLiteral(formatter.format(currentDate)), dateString); } - @SuppressWarnings("unused") public void testInvalidDatePattern() throws Exception { try { - new DefaultDateTypeAdapter(Date.class, "I am a bad Date pattern...."); + DateType.DATE.createAdapter("I am a bad Date pattern...."); fail("Invalid date pattern should fail."); } catch (IllegalArgumentException expected) { } } public void testNullValue() throws Exception { - DefaultDateTypeAdapter adapter = new DefaultDateTypeAdapter(Date.class); + DefaultDateTypeAdapter adapter = DateType.DATE.createDefaultsAdapter(); assertNull(adapter.fromJson("null")); assertEquals("null", adapter.toJson(null)); } public void testUnexpectedToken() throws Exception { try { - DefaultDateTypeAdapter adapter = new DefaultDateTypeAdapter(Date.class); + DefaultDateTypeAdapter adapter = DateType.DATE.createDefaultsAdapter(); adapter.fromJson("{}"); fail("Unexpected token should fail."); } catch (IllegalStateException expected) { } } - private void assertFormatted(String formatted, DefaultDateTypeAdapter adapter) { + private void assertFormatted(String formatted, DefaultDateTypeAdapter adapter) { assertEquals(toLiteral(formatted), adapter.toJson(new Date(0))); } - private void assertParsed(String date, DefaultDateTypeAdapter adapter) throws IOException { + private void assertParsed(String date, DefaultDateTypeAdapter adapter) throws IOException { assertEquals(date, new Date(0), adapter.fromJson(toLiteral(date))); assertEquals("ISO 8601", new Date(0), adapter.fromJson(toLiteral("1970-01-01T00:00:00Z"))); }