Skip to content

Commit

Permalink
Merge pull request #1707 from Marcono1234/optional-sql
Browse files Browse the repository at this point in the history
Make dependency on java.sql optional
  • Loading branch information
eamonnmcmanus authored Aug 25, 2021
2 parents 03be835 + 69173b0 commit 1023f0f
Show file tree
Hide file tree
Showing 19 changed files with 443 additions and 236 deletions.
13 changes: 8 additions & 5 deletions gson/src/main/java/com/google/gson/Gson.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);

Expand Down
44 changes: 27 additions & 17 deletions gson/src/main/java/com/google/gson/GsonBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;

Expand Down Expand Up @@ -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.
*
* <p>The date format will be used to serialize and deserialize {@link java.util.Date}, {@link
* java.sql.Timestamp} and {@link java.sql.Date}.
* <p>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}.
*
* <p>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
Expand Down Expand Up @@ -602,26 +603,35 @@ public Gson create() {
this.factories, this.hierarchyFactories, factories);
}

@SuppressWarnings("unchecked")
private void addTypeAdaptersForDate(String datePattern, int dateStyle, int timeStyle,
List<TypeAdapterFactory> factories) {
DefaultDateTypeAdapter dateTypeAdapter;
TypeAdapter<Timestamp> timestampTypeAdapter;
TypeAdapter<java.sql.Date> 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);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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<TypeVariable<?>, Type>());
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand All @@ -35,45 +38,70 @@
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<Date> {

public final class DefaultDateTypeAdapter<T extends Date> extends TypeAdapter<T> {
private static final String SIMPLE_NAME = "DefaultDateTypeAdapter";

private final Class<? extends Date> dateType;
public static abstract class DateType<T extends Date> {
public static final DateType<Date> DATE = new DateType<Date>(Date.class) {
@Override protected Date deserialize(Date date) {
return date;
}
};

private final Class<T> dateClass;

protected DateType(Class<T> dateClass) {
this.dateClass = dateClass;
}

protected abstract T deserialize(Date date);

private final TypeAdapterFactory createFactory(DefaultDateTypeAdapter<T> adapter) {
return TypeAdapters.newFactory(dateClass, adapter);
}

public final TypeAdapterFactory createAdapterFactory(String datePattern) {
return createFactory(new DefaultDateTypeAdapter<T>(this, datePattern));
}

public final TypeAdapterFactory createAdapterFactory(int style) {
return createFactory(new DefaultDateTypeAdapter<T>(this, style));
}

public final TypeAdapterFactory createAdapterFactory(int dateStyle, int timeStyle) {
return createFactory(new DefaultDateTypeAdapter<T>(this, dateStyle, timeStyle));
}

public final TypeAdapterFactory createDefaultsAdapterFactory() {
return createFactory(new DefaultDateTypeAdapter<T>(this, DateFormat.DEFAULT, DateFormat.DEFAULT));
}
}

private final DateType<T> dateType;

/**
* List of 1 or more different date formats used for de-serialization attempts.
* The first of them is used for serialization as well.
*/
private final List<DateFormat> dateFormats = new ArrayList<DateFormat>();

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<T> 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<T> 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));
Expand All @@ -83,12 +111,8 @@ final class DefaultDateTypeAdapter extends TypeAdapter<Date> {
}
}

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<T> 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));
Expand All @@ -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
Expand All @@ -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) {
Expand All @@ -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);
}
}

Expand Down
27 changes: 2 additions & 25 deletions gson/src/main/java/com/google/gson/internal/bind/TypeAdapters.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -409,7 +407,7 @@ public void write(JsonWriter out, String value) throws IOException {
out.value(value);
}
};

public static final TypeAdapter<BigDecimal> BIG_DECIMAL = new TypeAdapter<BigDecimal>() {
@Override public BigDecimal read(JsonReader in) throws IOException {
if (in.peek() == JsonToken.NULL) {
Expand All @@ -427,7 +425,7 @@ public void write(JsonWriter out, String value) throws IOException {
out.value(value);
}
};

public static final TypeAdapter<BigInteger> BIG_INTEGER = new TypeAdapter<BigInteger>() {
@Override public BigInteger read(JsonReader in) throws IOException {
if (in.peek() == JsonToken.NULL) {
Expand Down Expand Up @@ -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 <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) {
if (typeToken.getRawType() != Timestamp.class) {
return null;
}

final TypeAdapter<Date> dateTypeAdapter = gson.getAdapter(Date.class);
return (TypeAdapter<T>) new TypeAdapter<Timestamp>() {
@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> CALENDAR = new TypeAdapter<Calendar>() {
private static final String YEAR = "year";
private static final String MONTH = "month";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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<java.sql.Date> {
public static final TypeAdapterFactory FACTORY = new TypeAdapterFactory() {
final class SqlDateTypeAdapter extends TypeAdapter<java.sql.Date> {
static final TypeAdapterFactory FACTORY = new TypeAdapterFactory() {
@SuppressWarnings("unchecked") // we use a runtime check to make sure the 'T's equal
@Override public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) {
return typeToken.getRawType() == java.sql.Date.class
Expand All @@ -46,6 +46,9 @@ public final class SqlDateTypeAdapter extends TypeAdapter<java.sql.Date> {

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) {
Expand Down
Loading

0 comments on commit 1023f0f

Please sign in to comment.