Remove DateTypeAdapter to avoid code duplication (#2546)

The same functionality exists in `DefaultDateTypeAdapter` (with some slight
overhead), and there is currently code duplication between these classes.
This commit is contained in:
Marcono1234 2023-11-19 23:51:18 +01:00 committed by GitHub
parent 08174e2019
commit ea4ffde76c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 37 additions and 120 deletions

View File

@ -25,7 +25,7 @@ import com.google.gson.internal.Primitives;
import com.google.gson.internal.Streams;
import com.google.gson.internal.bind.ArrayTypeAdapter;
import com.google.gson.internal.bind.CollectionTypeAdapterFactory;
import com.google.gson.internal.bind.DateTypeAdapter;
import com.google.gson.internal.bind.DefaultDateTypeAdapter;
import com.google.gson.internal.bind.JsonAdapterAnnotationTypeAdapterFactory;
import com.google.gson.internal.bind.JsonTreeReader;
import com.google.gson.internal.bind.JsonTreeWriter;
@ -369,7 +369,7 @@ public final class Gson {
factories.add(TypeAdapters.LOCALE_FACTORY);
factories.add(TypeAdapters.INET_ADDRESS_FACTORY);
factories.add(TypeAdapters.BIT_SET_FACTORY);
factories.add(DateTypeAdapter.FACTORY);
factories.add(DefaultDateTypeAdapter.DEFAULT_STYLE_FACTORY);
factories.add(TypeAdapters.CALENDAR_FACTORY);
if (SqlTypesSupport.SUPPORTS_SQL_TYPES) {

View File

@ -1,116 +0,0 @@
/*
* Copyright (C) 2011 Google Inc.
*
* 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.bind;
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 java.io.IOException;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.ParsePosition;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Locale;
/**
* Adapter for Date. 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 DateTypeAdapter extends TypeAdapter<Date> {
public 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() == Date.class
? (TypeAdapter<T>) new DateTypeAdapter()
: null;
}
};
/**
* List of 1 or more different date formats used for de-serialization attempts. The first of them
* (default US format) is used for serialization as well.
*/
private final List<DateFormat> dateFormats = new ArrayList<>();
public DateTypeAdapter() {
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));
}
}
@Override
public Date read(JsonReader in) throws IOException {
if (in.peek() == JsonToken.NULL) {
in.nextNull();
return null;
}
return deserializeToDate(in);
}
private Date deserializeToDate(JsonReader in) throws IOException {
String s = in.nextString();
synchronized (dateFormats) {
for (DateFormat dateFormat : dateFormats) {
try {
return dateFormat.parse(s);
} catch (ParseException ignored) {
// OK: try the next format
}
}
}
try {
return ISO8601Utils.parse(s, new ParsePosition(0));
} catch (ParseException e) {
throw new JsonSyntaxException(
"Failed parsing '" + s + "' as Date; at path " + in.getPreviousPath(), e);
}
}
@Override
public void write(JsonWriter out, Date value) throws IOException {
if (value == null) {
out.nullValue();
return;
}
DateFormat dateFormat = dateFormats.get(0);
String dateFormatAsString;
synchronized (dateFormats) {
dateFormatAsString = dateFormat.format(value);
}
out.value(dateFormatAsString);
}
}

View File

@ -16,12 +16,14 @@
package com.google.gson.internal.bind;
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;
@ -35,17 +37,48 @@ import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.Objects;
import java.util.TimeZone;
/**
* This type adapter supports subclasses of date by defining a {@link
* DefaultDateTypeAdapter.DateType} and then using its {@code createAdapterFactory} methods.
*
* <p><b>Important:</b> Instances of this class (or rather the {@link SimpleDateFormat} they use)
* capture the current default {@link Locale} and {@link TimeZone} when they are created. Therefore
* avoid storing factories obtained from {@link DateType} in {@code static} fields, since they only
* create a single adapter instance and its behavior would then depend on when Gson classes are
* loaded first, and which default {@code Locale} and {@code TimeZone} was used at that point.
*
* @author Inderjeet Singh
* @author Joel Leitch
*/
public final class DefaultDateTypeAdapter<T extends Date> extends TypeAdapter<T> {
private static final String SIMPLE_NAME = "DefaultDateTypeAdapter";
/** Factory for {@link Date} adapters which use {@link DateFormat#DEFAULT} as style. */
public static final TypeAdapterFactory DEFAULT_STYLE_FACTORY =
// Because SimpleDateFormat captures the default TimeZone when it was created, let the factory
// always create new DefaultDateTypeAdapter instances (which are then cached by the Gson
// instances) instead of having a single static DefaultDateTypeAdapter instance
// Otherwise the behavior would depend on when an application first loads Gson classes and
// which default TimeZone is set at that point, which would be quite brittle
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() == Date.class
? (TypeAdapter<T>)
new DefaultDateTypeAdapter<>(
DateType.DATE, DateFormat.DEFAULT, DateFormat.DEFAULT)
: null;
}
@Override
public String toString() {
return "DefaultDateTypeAdapter#DEFAULT_STYLE_FACTORY";
}
};
public abstract static class DateType<T extends Date> {
public static final DateType<Date> DATE =
new DateType<Date>(Date.class) {
@ -123,8 +156,6 @@ public final class DefaultDateTypeAdapter<T extends Date> extends TypeAdapter<T>
}
}
// These methods need to be synchronized since JDK DateFormat classes are not thread-safe
// See issue 162
@Override
public void write(JsonWriter out, Date value) throws IOException {
if (value == null) {
@ -134,6 +165,7 @@ public final class DefaultDateTypeAdapter<T extends Date> extends TypeAdapter<T>
DateFormat dateFormat = dateFormats.get(0);
String dateFormatAsString;
// Needs to be synchronized since JDK DateFormat classes are not thread-safe
synchronized (dateFormats) {
dateFormatAsString = dateFormat.format(value);
}
@ -152,6 +184,7 @@ public final class DefaultDateTypeAdapter<T extends Date> extends TypeAdapter<T>
private Date deserializeToDate(JsonReader in) throws IOException {
String s = in.nextString();
// Needs to be synchronized since JDK DateFormat classes are not thread-safe
synchronized (dateFormats) {
for (DateFormat dateFormat : dateFormats) {
try {