diff --git a/gson/src/main/java/com/google/gson/Gson.java b/gson/src/main/java/com/google/gson/Gson.java
index 27f3ee9246..a7938c84e6 100644
--- a/gson/src/main/java/com/google/gson/Gson.java
+++ b/gson/src/main/java/com/google/gson/Gson.java
@@ -49,9 +49,8 @@
import com.google.gson.internal.bind.MapTypeAdapterFactory;
import com.google.gson.internal.bind.ObjectTypeAdapter;
import com.google.gson.internal.bind.ReflectiveTypeAdapterFactory;
-import com.google.gson.internal.bind.SqlDateTypeAdapter;
-import com.google.gson.internal.bind.TimeTypeAdapter;
import com.google.gson.internal.bind.TypeAdapters;
+import com.google.gson.internal.sql.SqlTypesSupport;
import com.google.gson.reflect.TypeToken;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonToken;
@@ -262,9 +261,13 @@ public Gson() {
factories.add(TypeAdapters.BIT_SET_FACTORY);
factories.add(DateTypeAdapter.FACTORY);
factories.add(TypeAdapters.CALENDAR_FACTORY);
- factories.add(TimeTypeAdapter.FACTORY);
- factories.add(SqlDateTypeAdapter.FACTORY);
- factories.add(TypeAdapters.TIMESTAMP_FACTORY);
+
+ if (SqlTypesSupport.SUPPORTS_SQL_TYPES) {
+ factories.add(SqlTypesSupport.TIME_FACTORY);
+ factories.add(SqlTypesSupport.DATE_FACTORY);
+ factories.add(SqlTypesSupport.TIMESTAMP_FACTORY);
+ }
+
factories.add(ArrayTypeAdapter.FACTORY);
factories.add(TypeAdapters.CLASS_FACTORY);
diff --git a/gson/src/main/java/com/google/gson/GsonBuilder.java b/gson/src/main/java/com/google/gson/GsonBuilder.java
index b97be452be..b2fd74edec 100644
--- a/gson/src/main/java/com/google/gson/GsonBuilder.java
+++ b/gson/src/main/java/com/google/gson/GsonBuilder.java
@@ -17,7 +17,6 @@
package com.google.gson;
import java.lang.reflect.Type;
-import java.sql.Timestamp;
import java.text.DateFormat;
import java.util.ArrayList;
import java.util.Collections;
@@ -28,8 +27,10 @@
import com.google.gson.internal.$Gson$Preconditions;
import com.google.gson.internal.Excluder;
+import com.google.gson.internal.bind.DefaultDateTypeAdapter;
import com.google.gson.internal.bind.TreeTypeAdapter;
import com.google.gson.internal.bind.TypeAdapters;
+import com.google.gson.internal.sql.SqlTypesSupport;
import com.google.gson.reflect.TypeToken;
import com.google.gson.stream.JsonReader;
@@ -417,8 +418,8 @@ public GsonBuilder disableHtmlEscaping() {
* call this method or {@link #setDateFormat(int)} multiple times, but only the last invocation
* will be used to decide the serialization format.
*
- *
The date format will be used to serialize and deserialize {@link java.util.Date}, {@link
- * java.sql.Timestamp} and {@link java.sql.Date}.
+ *
The date format will be used to serialize and deserialize {@link java.util.Date} and in case
+ * the {@code java.sql} module is present, also {@link java.sql.Timestamp} and {@link java.sql.Date}.
*
*
Note that this pattern must abide by the convention provided by {@code SimpleDateFormat}
* class. See the documentation in {@link java.text.SimpleDateFormat} for more information on
@@ -602,26 +603,35 @@ public Gson create() {
this.factories, this.hierarchyFactories, factories);
}
- @SuppressWarnings("unchecked")
private void addTypeAdaptersForDate(String datePattern, int dateStyle, int timeStyle,
List factories) {
- 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);
+ TypeAdapterFactory dateAdapterFactory;
+ boolean sqlTypesSupported = SqlTypesSupport.SUPPORTS_SQL_TYPES;
+ TypeAdapterFactory sqlTimestampAdapterFactory = null;
+ TypeAdapterFactory sqlDateAdapterFactory = null;
+
+ if (datePattern != null && !datePattern.trim().isEmpty()) {
+ dateAdapterFactory = DefaultDateTypeAdapter.DateType.DATE.createAdapterFactory(datePattern);
+
+ if (sqlTypesSupported) {
+ sqlTimestampAdapterFactory = SqlTypesSupport.TIMESTAMP_DATE_TYPE.createAdapterFactory(datePattern);
+ sqlDateAdapterFactory = SqlTypesSupport.DATE_DATE_TYPE.createAdapterFactory(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);
+ dateAdapterFactory = DefaultDateTypeAdapter.DateType.DATE.createAdapterFactory(dateStyle, timeStyle);
+
+ if (sqlTypesSupported) {
+ sqlTimestampAdapterFactory = SqlTypesSupport.TIMESTAMP_DATE_TYPE.createAdapterFactory(dateStyle, timeStyle);
+ sqlDateAdapterFactory = SqlTypesSupport.DATE_DATE_TYPE.createAdapterFactory(dateStyle, timeStyle);
+ }
} else {
return;
}
- factories.add(TypeAdapters.newFactory(Date.class, dateTypeAdapter));
- factories.add(TypeAdapters.newFactory(Timestamp.class, timestampTypeAdapter));
- factories.add(TypeAdapters.newFactory(java.sql.Date.class, javaSqlDateTypeAdapter));
+ factories.add(dateAdapterFactory);
+ if (sqlTypesSupported) {
+ factories.add(sqlTimestampAdapterFactory);
+ factories.add(sqlDateAdapterFactory);
+ }
}
}
diff --git a/gson/src/main/java/com/google/gson/internal/$Gson$Types.java b/gson/src/main/java/com/google/gson/internal/$Gson$Types.java
index a708dc055b..617a644b1f 100644
--- a/gson/src/main/java/com/google/gson/internal/$Gson$Types.java
+++ b/gson/src/main/java/com/google/gson/internal/$Gson$Types.java
@@ -339,6 +339,7 @@ public static Type[] getMapKeyAndValueTypes(Type context, Class> contextRawTyp
}
public static Type resolve(Type context, Class> contextRawType, Type toResolve) {
+
return resolve(context, contextRawType, toResolve, new HashMap, Type>());
}
diff --git a/gson/src/main/java/com/google/gson/DefaultDateTypeAdapter.java b/gson/src/main/java/com/google/gson/internal/bind/DefaultDateTypeAdapter.java
similarity index 60%
rename from gson/src/main/java/com/google/gson/DefaultDateTypeAdapter.java
rename to gson/src/main/java/com/google/gson/internal/bind/DefaultDateTypeAdapter.java
index 522963795a..f56faee0f9 100644
--- a/gson/src/main/java/com/google/gson/DefaultDateTypeAdapter.java
+++ b/gson/src/main/java/com/google/gson/internal/bind/DefaultDateTypeAdapter.java
@@ -14,10 +14,9 @@
* limitations under the License.
*/
-package com.google.gson;
+package com.google.gson.internal.bind;
import java.io.IOException;
-import java.sql.Timestamp;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.ParsePosition;
@@ -27,6 +26,10 @@
import java.util.List;
import java.util.Locale;
+import com.google.gson.JsonSyntaxException;
+import com.google.gson.TypeAdapter;
+import com.google.gson.TypeAdapterFactory;
+import com.google.gson.internal.$Gson$Preconditions;
import com.google.gson.internal.JavaVersion;
import com.google.gson.internal.PreJava9DateFormatProvider;
import com.google.gson.internal.bind.util.ISO8601Utils;
@@ -35,17 +38,53 @@
import com.google.gson.stream.JsonWriter;
/**
- * This type adapter supports three subclasses of date: Date, Timestamp, and
- * java.sql.Date.
+ * This type adapter supports subclasses of date by defining a
+ * {@link DefaultDateTypeAdapter.DateType} and then using its {@code createAdapterFactory}
+ * methods.
*
* @author Inderjeet Singh
* @author Joel Leitch
*/
-final class DefaultDateTypeAdapter extends TypeAdapter {
-
+public final class DefaultDateTypeAdapter extends TypeAdapter {
private static final String SIMPLE_NAME = "DefaultDateTypeAdapter";
- private final Class extends Date> dateType;
+ public static abstract class DateType {
+ public static final DateType DATE = new DateType(Date.class) {
+ @Override protected Date deserialize(Date date) {
+ return date;
+ }
+ };
+
+ private final Class dateClass;
+
+ protected DateType(Class dateClass) {
+ this.dateClass = dateClass;
+ }
+
+ protected abstract T deserialize(Date date);
+
+ private final TypeAdapterFactory createFactory(DefaultDateTypeAdapter adapter) {
+ return TypeAdapters.newFactory(dateClass, adapter);
+ }
+
+ public final TypeAdapterFactory createAdapterFactory(String datePattern) {
+ return createFactory(new DefaultDateTypeAdapter(this, datePattern));
+ }
+
+ public final TypeAdapterFactory createAdapterFactory(int style) {
+ return createFactory(new DefaultDateTypeAdapter(this, style));
+ }
+
+ public final TypeAdapterFactory createAdapterFactory(int dateStyle, int timeStyle) {
+ return createFactory(new DefaultDateTypeAdapter(this, dateStyle, timeStyle));
+ }
+
+ public final TypeAdapterFactory createDefaultsAdapterFactory() {
+ return createFactory(new DefaultDateTypeAdapter(this, DateFormat.DEFAULT, DateFormat.DEFAULT));
+ }
+ }
+
+ private final DateType dateType;
/**
* List of 1 or more different date formats used for de-serialization attempts.
@@ -53,27 +92,16 @@ final class DefaultDateTypeAdapter extends TypeAdapter {
*/
private final List dateFormats = new ArrayList();
- DefaultDateTypeAdapter(Class extends Date> 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 extends Date> dateType, String datePattern) {
- this.dateType = verifyDateType(dateType);
+ private DefaultDateTypeAdapter(DateType dateType, String datePattern) {
+ this.dateType = $Gson$Preconditions.checkNotNull(dateType);
dateFormats.add(new SimpleDateFormat(datePattern, Locale.US));
if (!Locale.getDefault().equals(Locale.US)) {
dateFormats.add(new SimpleDateFormat(datePattern));
}
}
- DefaultDateTypeAdapter(Class extends Date> dateType, int style) {
- this.dateType = verifyDateType(dateType);
+ private DefaultDateTypeAdapter(DateType dateType, int style) {
+ this.dateType = $Gson$Preconditions.checkNotNull(dateType);
dateFormats.add(DateFormat.getDateInstance(style, Locale.US));
if (!Locale.getDefault().equals(Locale.US)) {
dateFormats.add(DateFormat.getDateInstance(style));
@@ -83,12 +111,8 @@ final class DefaultDateTypeAdapter extends TypeAdapter {
}
}
- public DefaultDateTypeAdapter(int dateStyle, int timeStyle) {
- this(Date.class, dateStyle, timeStyle);
- }
-
- public DefaultDateTypeAdapter(Class extends Date> dateType, int dateStyle, int timeStyle) {
- this.dateType = verifyDateType(dateType);
+ private DefaultDateTypeAdapter(DateType dateType, int dateStyle, int timeStyle) {
+ this.dateType = $Gson$Preconditions.checkNotNull(dateType);
dateFormats.add(DateFormat.getDateTimeInstance(dateStyle, timeStyle, Locale.US));
if (!Locale.getDefault().equals(Locale.US)) {
dateFormats.add(DateFormat.getDateTimeInstance(dateStyle, timeStyle));
@@ -98,13 +122,6 @@ public DefaultDateTypeAdapter(Class extends Date> dateType, int dateStyle, int
}
}
- private static Class extends Date> verifyDateType(Class extends Date> 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);
- }
- return dateType;
- }
-
// These methods need to be synchronized since JDK DateFormat classes are not thread-safe
// See issue 162
@Override
@@ -120,22 +137,13 @@ public void write(JsonWriter out, Date value) throws IOException {
}
@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) {
@@ -145,11 +153,12 @@ private Date deserializeToDate(String s) {
return dateFormat.parse(s);
} catch (ParseException ignored) {}
}
- try {
- return ISO8601Utils.parse(s, new ParsePosition(0));
- } catch (ParseException e) {
- throw new JsonSyntaxException(s, e);
- }
+ }
+
+ try {
+ return ISO8601Utils.parse(s, new ParsePosition(0));
+ } catch (ParseException e) {
+ throw new JsonSyntaxException(s, e);
}
}
diff --git a/gson/src/main/java/com/google/gson/internal/bind/TypeAdapters.java b/gson/src/main/java/com/google/gson/internal/bind/TypeAdapters.java
index 04b13ada81..6bbd680ab4 100644
--- a/gson/src/main/java/com/google/gson/internal/bind/TypeAdapters.java
+++ b/gson/src/main/java/com/google/gson/internal/bind/TypeAdapters.java
@@ -26,12 +26,10 @@
import java.net.URL;
import java.security.AccessController;
import java.security.PrivilegedAction;
-import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.BitSet;
import java.util.Calendar;
import java.util.Currency;
-import java.util.Date;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.List;
@@ -409,7 +407,7 @@ public void write(JsonWriter out, String value) throws IOException {
out.value(value);
}
};
-
+
public static final TypeAdapter BIG_DECIMAL = new TypeAdapter() {
@Override public BigDecimal read(JsonReader in) throws IOException {
if (in.peek() == JsonToken.NULL) {
@@ -427,7 +425,7 @@ public void write(JsonWriter out, String value) throws IOException {
out.value(value);
}
};
-
+
public static final TypeAdapter BIG_INTEGER = new TypeAdapter() {
@Override public BigInteger read(JsonReader in) throws IOException {
if (in.peek() == JsonToken.NULL) {
@@ -572,27 +570,6 @@ public void write(JsonWriter out, Currency value) throws IOException {
}.nullSafe();
public static final TypeAdapterFactory CURRENCY_FACTORY = newFactory(Currency.class, CURRENCY);
- public static final TypeAdapterFactory TIMESTAMP_FACTORY = new TypeAdapterFactory() {
- @SuppressWarnings("unchecked") // we use a runtime check to make sure the 'T's equal
- @Override public TypeAdapter create(Gson gson, TypeToken typeToken) {
- if (typeToken.getRawType() != Timestamp.class) {
- return null;
- }
-
- final TypeAdapter dateTypeAdapter = gson.getAdapter(Date.class);
- return (TypeAdapter) new TypeAdapter() {
- @Override public Timestamp read(JsonReader in) throws IOException {
- Date date = dateTypeAdapter.read(in);
- return date != null ? new Timestamp(date.getTime()) : null;
- }
-
- @Override public void write(JsonWriter out, Timestamp value) throws IOException {
- dateTypeAdapter.write(out, value);
- }
- };
- }
- };
-
public static final TypeAdapter CALENDAR = new TypeAdapter() {
private static final String YEAR = "year";
private static final String MONTH = "month";
diff --git a/gson/src/main/java/com/google/gson/internal/bind/SqlDateTypeAdapter.java b/gson/src/main/java/com/google/gson/internal/sql/SqlDateTypeAdapter.java
similarity index 91%
rename from gson/src/main/java/com/google/gson/internal/bind/SqlDateTypeAdapter.java
rename to gson/src/main/java/com/google/gson/internal/sql/SqlDateTypeAdapter.java
index 5ec244f29e..b3da1fefd4 100644
--- a/gson/src/main/java/com/google/gson/internal/bind/SqlDateTypeAdapter.java
+++ b/gson/src/main/java/com/google/gson/internal/sql/SqlDateTypeAdapter.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.google.gson.internal.bind;
+package com.google.gson.internal.sql;
import com.google.gson.Gson;
import com.google.gson.JsonSyntaxException;
@@ -35,8 +35,8 @@
* this class state. DateFormat isn't thread safe either, so this class has
* to synchronize its read and write methods.
*/
-public final class SqlDateTypeAdapter extends TypeAdapter {
- public static final TypeAdapterFactory FACTORY = new TypeAdapterFactory() {
+final class SqlDateTypeAdapter extends TypeAdapter {
+ static final TypeAdapterFactory FACTORY = new TypeAdapterFactory() {
@SuppressWarnings("unchecked") // we use a runtime check to make sure the 'T's equal
@Override public TypeAdapter create(Gson gson, TypeToken typeToken) {
return typeToken.getRawType() == java.sql.Date.class
@@ -46,6 +46,9 @@ public final class SqlDateTypeAdapter extends TypeAdapter {
private final DateFormat format = new SimpleDateFormat("MMM d, yyyy");
+ private SqlDateTypeAdapter() {
+ }
+
@Override
public synchronized java.sql.Date read(JsonReader in) throws IOException {
if (in.peek() == JsonToken.NULL) {
diff --git a/gson/src/main/java/com/google/gson/internal/bind/TimeTypeAdapter.java b/gson/src/main/java/com/google/gson/internal/sql/SqlTimeTypeAdapter.java
similarity index 86%
rename from gson/src/main/java/com/google/gson/internal/bind/TimeTypeAdapter.java
rename to gson/src/main/java/com/google/gson/internal/sql/SqlTimeTypeAdapter.java
index 55d4b2f693..ee65726b4e 100644
--- a/gson/src/main/java/com/google/gson/internal/bind/TimeTypeAdapter.java
+++ b/gson/src/main/java/com/google/gson/internal/sql/SqlTimeTypeAdapter.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.google.gson.internal.bind;
+package com.google.gson.internal.sql;
import com.google.gson.Gson;
import com.google.gson.JsonSyntaxException;
@@ -32,21 +32,24 @@
import java.util.Date;
/**
- * Adapter for Time. Although this class appears stateless, it is not.
+ * Adapter for java.sql.Time. Although this class appears stateless, it is not.
* DateFormat captures its time zone and locale when it is created, which gives
* this class state. DateFormat isn't thread safe either, so this class has
* to synchronize its read and write methods.
*/
-public final class TimeTypeAdapter extends TypeAdapter