diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index bc39b9fbf6..8c6d287275 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -15,8 +15,7 @@ jobs: include: - java: 21 # Disable Enforcer check which (intentionally) prevents using JDK 21 for building - # Exclude 'shrinker-test' module because R8 does not support JDK 21 yet - extra-mvn-args: -Denforcer.fail=false --projects '!:shrinker-test' + extra-mvn-args: -Denforcer.fail=false runs-on: ubuntu-latest steps: diff --git a/extras/src/main/java/com/google/gson/extras/examples/rawcollections/RawCollectionsExample.java b/extras/src/main/java/com/google/gson/extras/examples/rawcollections/RawCollectionsExample.java index 6568c0b146..4d9cffa86e 100644 --- a/extras/src/main/java/com/google/gson/extras/examples/rawcollections/RawCollectionsExample.java +++ b/extras/src/main/java/com/google/gson/extras/examples/rawcollections/RawCollectionsExample.java @@ -21,6 +21,7 @@ import java.util.ArrayList; import java.util.Collection; +@SuppressWarnings({"PrivateConstructorForUtilityClass", "SystemOut"}) public class RawCollectionsExample { static class Event { private String name; diff --git a/extras/src/main/java/com/google/gson/typeadapters/PostConstructAdapterFactory.java b/extras/src/main/java/com/google/gson/typeadapters/PostConstructAdapterFactory.java index 7a3234073f..924a9089ff 100644 --- a/extras/src/main/java/com/google/gson/typeadapters/PostConstructAdapterFactory.java +++ b/extras/src/main/java/com/google/gson/typeadapters/PostConstructAdapterFactory.java @@ -61,9 +61,11 @@ public T read(JsonReader in) throws IOException { try { method.invoke(result); } catch (IllegalAccessException e) { - throw new AssertionError(); + throw new AssertionError(e); } catch (InvocationTargetException e) { - if (e.getCause() instanceof RuntimeException) throw (RuntimeException) e.getCause(); + if (e.getCause() instanceof RuntimeException) { + throw (RuntimeException) e.getCause(); + } throw new RuntimeException(e.getCause()); } } diff --git a/extras/src/main/java/com/google/gson/typeadapters/UtcDateTypeAdapter.java b/extras/src/main/java/com/google/gson/typeadapters/UtcDateTypeAdapter.java index b3e01a1881..4b2356c1b2 100644 --- a/extras/src/main/java/com/google/gson/typeadapters/UtcDateTypeAdapter.java +++ b/extras/src/main/java/com/google/gson/typeadapters/UtcDateTypeAdapter.java @@ -30,7 +30,7 @@ import java.util.TimeZone; public final class UtcDateTypeAdapter extends TypeAdapter { - private final TimeZone UTC_TIME_ZONE = TimeZone.getTimeZone("UTC"); + private static final TimeZone UTC_TIME_ZONE = TimeZone.getTimeZone("UTC"); @Override public void write(JsonWriter out, Date date) throws IOException { diff --git a/extras/src/test/java/com/google/gson/interceptors/InterceptorTest.java b/extras/src/test/java/com/google/gson/interceptors/InterceptorTest.java index 2030d337cd..9a957fc744 100644 --- a/extras/src/test/java/com/google/gson/interceptors/InterceptorTest.java +++ b/extras/src/test/java/com/google/gson/interceptors/InterceptorTest.java @@ -171,7 +171,9 @@ public void postDeserialize(User user) { if (user.name == null || user.password == null) { throw new JsonSyntaxException("name and password are required fields."); } - if (user.email == null) user.email = User.DEFAULT_EMAIL; + if (user.email == null) { + user.email = User.DEFAULT_EMAIL; + } } } @@ -192,7 +194,9 @@ public void postDeserialize(Address address) { if (address.city == null || address.state == null || address.zip == null) { throw new JsonSyntaxException("Address city, state and zip are required fields."); } - if (address.firstLine == null) address.firstLine = Address.DEFAULT_FIRST_LINE; + if (address.firstLine == null) { + address.firstLine = Address.DEFAULT_FIRST_LINE; + } } } } diff --git a/gson/pom.xml b/gson/pom.xml index f2cf9a4805..e72f89b60c 100644 --- a/gson/pom.xml +++ b/gson/pom.xml @@ -50,7 +50,7 @@ com.google.errorprone error_prone_annotations - 2.23.0 + 2.24.1 @@ -66,7 +66,7 @@ com.google.guava guava-testlib - 32.1.3-jre + 33.0.0-jre test diff --git a/gson/src/main/java/com/google/gson/Gson.java b/gson/src/main/java/com/google/gson/Gson.java index 80aa12888e..058d0bef82 100644 --- a/gson/src/main/java/com/google/gson/Gson.java +++ b/gson/src/main/java/com/google/gson/Gson.java @@ -656,6 +656,16 @@ public TypeAdapter getAdapter(TypeToken type) { return candidate; } + /** + * Returns the type adapter for {@code type}. + * + * @throws IllegalArgumentException if this Gson instance cannot serialize and deserialize {@code + * type}. + */ + public TypeAdapter getAdapter(Class type) { + return getAdapter(TypeToken.get(type)); + } + /** * This method is used to get an alternate type adapter for the specified type. This is used to * access a type adapter that is overridden by a {@link TypeAdapterFactory} that you may have @@ -748,16 +758,6 @@ public TypeAdapter getDelegateAdapter(TypeAdapterFactory skipPast, TypeTo } } - /** - * Returns the type adapter for {@code type}. - * - * @throws IllegalArgumentException if this Gson instance cannot serialize and deserialize {@code - * type}. - */ - public TypeAdapter getAdapter(Class type) { - return getAdapter(TypeToken.get(type)); - } - /** * This method serializes the specified object into its equivalent representation as a tree of * {@link JsonElement}s. This method should be used when the specified object is not a generic @@ -983,53 +983,6 @@ public void toJson(JsonElement jsonElement, Appendable writer) throws JsonIOExce } } - /** - * Returns a new JSON writer configured for the settings on this Gson instance. - * - *

The following settings are considered: - * - *

    - *
  • {@link GsonBuilder#disableHtmlEscaping()} - *
  • {@link GsonBuilder#generateNonExecutableJson()} - *
  • {@link GsonBuilder#serializeNulls()} - *
  • {@link GsonBuilder#setStrictness(Strictness)}. If no {@linkplain - * GsonBuilder#setStrictness(Strictness) explicit strictness has been set} the created - * writer will have a strictness of {@link Strictness#LEGACY_STRICT}. Otherwise, the - * strictness of the {@code Gson} instance will be used for the created writer. - *
  • {@link GsonBuilder#setPrettyPrinting()} - *
  • {@link GsonBuilder#setFormattingStyle(FormattingStyle)} - *
- */ - public JsonWriter newJsonWriter(Writer writer) throws IOException { - if (generateNonExecutableJson) { - writer.write(JSON_NON_EXECUTABLE_PREFIX); - } - JsonWriter jsonWriter = new JsonWriter(writer); - jsonWriter.setFormattingStyle(formattingStyle); - jsonWriter.setHtmlSafe(htmlSafe); - jsonWriter.setStrictness(strictness == null ? Strictness.LEGACY_STRICT : strictness); - jsonWriter.setSerializeNulls(serializeNulls); - return jsonWriter; - } - - /** - * Returns a new JSON reader configured for the settings on this Gson instance. - * - *

The following settings are considered: - * - *

    - *
  • {@link GsonBuilder#setStrictness(Strictness)}. If no {@linkplain - * GsonBuilder#setStrictness(Strictness) explicit strictness has been set} the created - * reader will have a strictness of {@link Strictness#LEGACY_STRICT}. Otherwise, the - * strictness of the {@code Gson} instance will be used for the created reader. - *
- */ - public JsonReader newJsonReader(Reader reader) { - JsonReader jsonReader = new JsonReader(reader); - jsonReader.setStrictness(strictness == null ? Strictness.LEGACY_STRICT : strictness); - return jsonReader; - } - /** * Writes the JSON for {@code jsonElement} to {@code writer}. * @@ -1078,6 +1031,53 @@ public void toJson(JsonElement jsonElement, JsonWriter writer) throws JsonIOExce } } + /** + * Returns a new JSON writer configured for the settings on this Gson instance. + * + *

The following settings are considered: + * + *

    + *
  • {@link GsonBuilder#disableHtmlEscaping()} + *
  • {@link GsonBuilder#generateNonExecutableJson()} + *
  • {@link GsonBuilder#serializeNulls()} + *
  • {@link GsonBuilder#setStrictness(Strictness)}. If no {@linkplain + * GsonBuilder#setStrictness(Strictness) explicit strictness has been set} the created + * writer will have a strictness of {@link Strictness#LEGACY_STRICT}. Otherwise, the + * strictness of the {@code Gson} instance will be used for the created writer. + *
  • {@link GsonBuilder#setPrettyPrinting()} + *
  • {@link GsonBuilder#setFormattingStyle(FormattingStyle)} + *
+ */ + public JsonWriter newJsonWriter(Writer writer) throws IOException { + if (generateNonExecutableJson) { + writer.write(JSON_NON_EXECUTABLE_PREFIX); + } + JsonWriter jsonWriter = new JsonWriter(writer); + jsonWriter.setFormattingStyle(formattingStyle); + jsonWriter.setHtmlSafe(htmlSafe); + jsonWriter.setStrictness(strictness == null ? Strictness.LEGACY_STRICT : strictness); + jsonWriter.setSerializeNulls(serializeNulls); + return jsonWriter; + } + + /** + * Returns a new JSON reader configured for the settings on this Gson instance. + * + *

The following settings are considered: + * + *

    + *
  • {@link GsonBuilder#setStrictness(Strictness)}. If no {@linkplain + * GsonBuilder#setStrictness(Strictness) explicit strictness has been set} the created + * reader will have a strictness of {@link Strictness#LEGACY_STRICT}. Otherwise, the + * strictness of the {@code Gson} instance will be used for the created reader. + *
+ */ + public JsonReader newJsonReader(Reader reader) { + JsonReader jsonReader = new JsonReader(reader); + jsonReader.setStrictness(strictness == null ? Strictness.LEGACY_STRICT : strictness); + return jsonReader; + } + /** * This method deserializes the specified JSON into an object of the specified class. It is not * suitable to use if the specified class is a generic type since it will not have the generic @@ -1262,18 +1262,6 @@ public T fromJson(Reader json, TypeToken typeOfT) return object; } - private static void assertFullConsumption(Object obj, JsonReader reader) { - try { - if (obj != null && reader.peek() != JsonToken.END_DOCUMENT) { - throw new JsonSyntaxException("JSON document was not fully consumed."); - } - } catch (MalformedJsonException e) { - throw new JsonSyntaxException(e); - } catch (IOException e) { - throw new JsonIOException(e); - } - } - // fromJson(JsonReader, Class) is unfortunately missing and cannot be added now without breaking // source compatibility in certain cases, see // https://github.com/google/gson/pull/1700#discussion_r973764414 @@ -1472,6 +1460,18 @@ public T fromJson(JsonElement json, TypeToken typeOfT) throws JsonSyntaxE return fromJson(new JsonTreeReader(json), typeOfT); } + private static void assertFullConsumption(Object obj, JsonReader reader) { + try { + if (obj != null && reader.peek() != JsonToken.END_DOCUMENT) { + throw new JsonSyntaxException("JSON document was not fully consumed."); + } + } catch (MalformedJsonException e) { + throw new JsonSyntaxException(e); + } catch (IOException e) { + throw new JsonIOException(e); + } + } + /** * Proxy type adapter for cyclic type graphs. * diff --git a/gson/src/main/java/com/google/gson/GsonBuilder.java b/gson/src/main/java/com/google/gson/GsonBuilder.java index c04dd5439e..0d9bef027d 100644 --- a/gson/src/main/java/com/google/gson/GsonBuilder.java +++ b/gson/src/main/java/com/google/gson/GsonBuilder.java @@ -614,14 +614,6 @@ public GsonBuilder setDateFormat(String pattern) { return this; } - private static int checkDateFormatStyle(int style) { - // Valid DateFormat styles are: 0, 1, 2, 3 (FULL, LONG, MEDIUM, SHORT) - if (style < 0 || style > 3) { - throw new IllegalArgumentException("Invalid style: " + style); - } - return style; - } - /** * Configures Gson to serialize {@code Date} objects according to the date style value provided. * You can call this method or {@link #setDateFormat(String)} multiple times, but only the last @@ -669,6 +661,14 @@ public GsonBuilder setDateFormat(int dateStyle, int timeStyle) { return this; } + private static int checkDateFormatStyle(int style) { + // Valid DateFormat styles are: 0, 1, 2, 3 (FULL, LONG, MEDIUM, SHORT) + if (style < 0 || style > 3) { + throw new IllegalArgumentException("Invalid style: " + style); + } + return style; + } + /** * Configures Gson for custom serialization or deserialization. This method combines the * registration of an {@link TypeAdapter}, {@link InstanceCreator}, {@link JsonSerializer}, and a @@ -722,7 +722,7 @@ public GsonBuilder registerTypeAdapter(Type type, Object typeAdapter) { return this; } - private boolean isTypeObjectOrJsonElement(Type type) { + private static boolean isTypeObjectOrJsonElement(Type type) { return type instanceof Class && (type == Object.class || JsonElement.class.isAssignableFrom((Class) type)); } @@ -901,7 +901,7 @@ public Gson create() { new ArrayList<>(reflectionFilters)); } - private void addTypeAdaptersForDate( + private static void addTypeAdaptersForDate( String datePattern, int dateStyle, int timeStyle, List factories) { TypeAdapterFactory dateAdapterFactory; boolean sqlTypesSupported = SqlTypesSupport.SUPPORTS_SQL_TYPES; diff --git a/gson/src/main/java/com/google/gson/JsonPrimitive.java b/gson/src/main/java/com/google/gson/JsonPrimitive.java index 00530d5691..2985ce98ab 100644 --- a/gson/src/main/java/com/google/gson/JsonPrimitive.java +++ b/gson/src/main/java/com/google/gson/JsonPrimitive.java @@ -38,7 +38,10 @@ public final class JsonPrimitive extends JsonElement { * * @param bool the value to create the primitive with. */ - @SuppressWarnings("deprecation") // superclass constructor + // "deprecation" suppression for superclass constructor + // "UnnecessaryBoxedVariable" Error Prone warning is correct since method does not accept + // null, but cannot be changed anymore since this is public API + @SuppressWarnings({"deprecation", "UnnecessaryBoxedVariable"}) public JsonPrimitive(Boolean bool) { value = Objects.requireNonNull(bool); } @@ -69,7 +72,10 @@ public JsonPrimitive(String string) { * * @param c the value to create the primitive with. */ - @SuppressWarnings("deprecation") // superclass constructor + // "deprecation" suppression for superclass constructor + // "UnnecessaryBoxedVariable" Error Prone warning is correct since method does not accept + // null, but cannot be changed anymore since this is public API + @SuppressWarnings({"deprecation", "UnnecessaryBoxedVariable"}) public JsonPrimitive(Character c) { // convert characters to strings since in JSON, characters are represented as a single // character string diff --git a/gson/src/main/java/com/google/gson/TypeAdapter.java b/gson/src/main/java/com/google/gson/TypeAdapter.java index fe4ce8ecde..90d330e382 100644 --- a/gson/src/main/java/com/google/gson/TypeAdapter.java +++ b/gson/src/main/java/com/google/gson/TypeAdapter.java @@ -144,69 +144,6 @@ public final void toJson(Writer out, T value) throws IOException { write(writer, value); } - /** - * This wrapper method is used to make a type adapter null tolerant. In general, a type adapter is - * required to handle nulls in write and read methods. Here is how this is typically done:
- * - *
{@code
-   * Gson gson = new GsonBuilder().registerTypeAdapter(Foo.class,
-   *   new TypeAdapter() {
-   *     public Foo read(JsonReader in) throws IOException {
-   *       if (in.peek() == JsonToken.NULL) {
-   *         in.nextNull();
-   *         return null;
-   *       }
-   *       // read a Foo from in and return it
-   *     }
-   *     public void write(JsonWriter out, Foo src) throws IOException {
-   *       if (src == null) {
-   *         out.nullValue();
-   *         return;
-   *       }
-   *       // write src as JSON to out
-   *     }
-   *   }).create();
-   * }
- * - * You can avoid this boilerplate handling of nulls by wrapping your type adapter with this - * method. Here is how we will rewrite the above example: - * - *
{@code
-   * Gson gson = new GsonBuilder().registerTypeAdapter(Foo.class,
-   *   new TypeAdapter() {
-   *     public Foo read(JsonReader in) throws IOException {
-   *       // read a Foo from in and return it
-   *     }
-   *     public void write(JsonWriter out, Foo src) throws IOException {
-   *       // write src as JSON to out
-   *     }
-   *   }.nullSafe()).create();
-   * }
- * - * Note that we didn't need to check for nulls in our type adapter after we used nullSafe. - */ - public final TypeAdapter nullSafe() { - return new TypeAdapter() { - @Override - public void write(JsonWriter out, T value) throws IOException { - if (value == null) { - out.nullValue(); - } else { - TypeAdapter.this.write(out, value); - } - } - - @Override - public T read(JsonReader reader) throws IOException { - if (reader.peek() == JsonToken.NULL) { - reader.nextNull(); - return null; - } - return TypeAdapter.this.read(reader); - } - }; - } - /** * Converts {@code value} to a JSON document. * @@ -309,4 +246,67 @@ public final T fromJsonTree(JsonElement jsonTree) { throw new JsonIOException(e); } } + + /** + * This wrapper method is used to make a type adapter null tolerant. In general, a type adapter is + * required to handle nulls in write and read methods. Here is how this is typically done:
+ * + *
{@code
+   * Gson gson = new GsonBuilder().registerTypeAdapter(Foo.class,
+   *   new TypeAdapter() {
+   *     public Foo read(JsonReader in) throws IOException {
+   *       if (in.peek() == JsonToken.NULL) {
+   *         in.nextNull();
+   *         return null;
+   *       }
+   *       // read a Foo from in and return it
+   *     }
+   *     public void write(JsonWriter out, Foo src) throws IOException {
+   *       if (src == null) {
+   *         out.nullValue();
+   *         return;
+   *       }
+   *       // write src as JSON to out
+   *     }
+   *   }).create();
+   * }
+ * + * You can avoid this boilerplate handling of nulls by wrapping your type adapter with this + * method. Here is how we will rewrite the above example: + * + *
{@code
+   * Gson gson = new GsonBuilder().registerTypeAdapter(Foo.class,
+   *   new TypeAdapter() {
+   *     public Foo read(JsonReader in) throws IOException {
+   *       // read a Foo from in and return it
+   *     }
+   *     public void write(JsonWriter out, Foo src) throws IOException {
+   *       // write src as JSON to out
+   *     }
+   *   }.nullSafe()).create();
+   * }
+ * + * Note that we didn't need to check for nulls in our type adapter after we used nullSafe. + */ + public final TypeAdapter nullSafe() { + return new TypeAdapter() { + @Override + public void write(JsonWriter out, T value) throws IOException { + if (value == null) { + out.nullValue(); + } else { + TypeAdapter.this.write(out, value); + } + } + + @Override + public T read(JsonReader reader) throws IOException { + if (reader.peek() == JsonToken.NULL) { + reader.nextNull(); + return null; + } + return TypeAdapter.this.read(reader); + } + }; + } } diff --git a/gson/src/main/java/com/google/gson/internal/JavaVersion.java b/gson/src/main/java/com/google/gson/internal/JavaVersion.java index af981d5699..fc08057dca 100644 --- a/gson/src/main/java/com/google/gson/internal/JavaVersion.java +++ b/gson/src/main/java/com/google/gson/internal/JavaVersion.java @@ -27,11 +27,11 @@ public final class JavaVersion { private static int determineMajorJavaVersion() { String javaVersion = System.getProperty("java.version"); - return getMajorJavaVersion(javaVersion); + return parseMajorJavaVersion(javaVersion); } // Visible for testing only - static int getMajorJavaVersion(String javaVersion) { + static int parseMajorJavaVersion(String javaVersion) { int version = parseDotted(javaVersion); if (version == -1) { version = extractBeginningInt(javaVersion); diff --git a/gson/src/main/java/com/google/gson/internal/JsonReaderInternalAccess.java b/gson/src/main/java/com/google/gson/internal/JsonReaderInternalAccess.java index 776b7639df..e3d4873d79 100644 --- a/gson/src/main/java/com/google/gson/internal/JsonReaderInternalAccess.java +++ b/gson/src/main/java/com/google/gson/internal/JsonReaderInternalAccess.java @@ -21,7 +21,10 @@ /** Internal-only APIs of JsonReader available only to other classes in Gson. */ public abstract class JsonReaderInternalAccess { - public static JsonReaderInternalAccess INSTANCE; + // Suppress warnings because field is initialized by `JsonReader` class during class loading + // (and therefore should be thread-safe), and any usage appears after `JsonReader` was loaded + @SuppressWarnings({"ConstantField", "NonFinalStaticField"}) + public static volatile JsonReaderInternalAccess INSTANCE; /** Changes the type of the current property name token to a string value. */ public abstract void promoteNameToValue(JsonReader reader) throws IOException; diff --git a/gson/src/main/java/com/google/gson/internal/LinkedTreeMap.java b/gson/src/main/java/com/google/gson/internal/LinkedTreeMap.java index 5058b3667b..099dd573c0 100644 --- a/gson/src/main/java/com/google/gson/internal/LinkedTreeMap.java +++ b/gson/src/main/java/com/google/gson/internal/LinkedTreeMap.java @@ -233,7 +233,7 @@ Node findByEntry(Entry entry) { return valuesEqual ? mine : null; } - private boolean equal(Object a, Object b) { + private static boolean equal(Object a, Object b) { return Objects.equals(a, b); } diff --git a/gson/src/main/java/com/google/gson/internal/NonNullElementWrapperList.java b/gson/src/main/java/com/google/gson/internal/NonNullElementWrapperList.java index 294bf0a9e5..51008f840b 100644 --- a/gson/src/main/java/com/google/gson/internal/NonNullElementWrapperList.java +++ b/gson/src/main/java/com/google/gson/internal/NonNullElementWrapperList.java @@ -76,6 +76,7 @@ public void clear() { delegate.clear(); } + @SuppressWarnings("UngroupedOverloads") // this is intentionally ungrouped, see comment above @Override public boolean remove(Object o) { return delegate.remove(o); diff --git a/gson/src/main/java/com/google/gson/internal/PreJava9DateFormatProvider.java b/gson/src/main/java/com/google/gson/internal/PreJava9DateFormatProvider.java index 02367d9ee5..552503f25f 100644 --- a/gson/src/main/java/com/google/gson/internal/PreJava9DateFormatProvider.java +++ b/gson/src/main/java/com/google/gson/internal/PreJava9DateFormatProvider.java @@ -21,12 +21,13 @@ /** Provides DateFormats for US locale with patterns which were the default ones before Java 9. */ public class PreJava9DateFormatProvider { + private PreJava9DateFormatProvider() {} /** * Returns the same DateFormat as {@code DateFormat.getDateInstance(style, Locale.US)} in Java 8 * or below. */ - public static DateFormat getUSDateFormat(int style) { + public static DateFormat getUsDateFormat(int style) { return new SimpleDateFormat(getDateFormatPattern(style), Locale.US); } @@ -34,7 +35,7 @@ public static DateFormat getUSDateFormat(int style) { * Returns the same DateFormat as {@code DateFormat.getDateTimeInstance(dateStyle, timeStyle, * Locale.US)} in Java 8 or below. */ - public static DateFormat getUSDateTimeFormat(int dateStyle, int timeStyle) { + public static DateFormat getUsDateTimeFormat(int dateStyle, int timeStyle) { String pattern = getDatePartOfDateTimePattern(dateStyle) + " " + getTimePartOfDateTimePattern(timeStyle); return new SimpleDateFormat(pattern, Locale.US); diff --git a/gson/src/main/java/com/google/gson/internal/Primitives.java b/gson/src/main/java/com/google/gson/internal/Primitives.java index 47b0e0eedf..3397ea20e5 100644 --- a/gson/src/main/java/com/google/gson/internal/Primitives.java +++ b/gson/src/main/java/com/google/gson/internal/Primitives.java @@ -60,7 +60,7 @@ public static boolean isWrapperType(Type type) { * wrap(String.class) == String.class * */ - @SuppressWarnings("unchecked") + @SuppressWarnings({"unchecked", "MissingBraces"}) public static Class wrap(Class type) { if (type == int.class) return (Class) Integer.class; if (type == float.class) return (Class) Float.class; @@ -84,7 +84,7 @@ public static Class wrap(Class type) { * unwrap(String.class) == String.class * */ - @SuppressWarnings("unchecked") + @SuppressWarnings({"unchecked", "MissingBraces"}) public static Class unwrap(Class type) { if (type == Integer.class) return (Class) int.class; if (type == Float.class) return (Class) float.class; diff --git a/gson/src/main/java/com/google/gson/internal/Streams.java b/gson/src/main/java/com/google/gson/internal/Streams.java index 2435522f85..46df853f5a 100644 --- a/gson/src/main/java/com/google/gson/internal/Streams.java +++ b/gson/src/main/java/com/google/gson/internal/Streams.java @@ -81,6 +81,7 @@ private static final class AppendableWriter extends Writer { this.appendable = appendable; } + @SuppressWarnings("UngroupedOverloads") // this is intentionally ungrouped, see comment below @Override public void write(char[] chars, int offset, int length) throws IOException { currentWrite.setChars(chars); diff --git a/gson/src/main/java/com/google/gson/internal/bind/DefaultDateTypeAdapter.java b/gson/src/main/java/com/google/gson/internal/bind/DefaultDateTypeAdapter.java index 4a371348a0..2061c112d6 100644 --- a/gson/src/main/java/com/google/gson/internal/bind/DefaultDateTypeAdapter.java +++ b/gson/src/main/java/com/google/gson/internal/bind/DefaultDateTypeAdapter.java @@ -141,7 +141,7 @@ private DefaultDateTypeAdapter(DateType dateType, int style) { dateFormats.add(DateFormat.getDateInstance(style)); } if (JavaVersion.isJava9OrLater()) { - dateFormats.add(PreJava9DateFormatProvider.getUSDateFormat(style)); + dateFormats.add(PreJava9DateFormatProvider.getUsDateFormat(style)); } } @@ -152,7 +152,7 @@ private DefaultDateTypeAdapter(DateType dateType, int dateStyle, int timeStyl dateFormats.add(DateFormat.getDateTimeInstance(dateStyle, timeStyle)); } if (JavaVersion.isJava9OrLater()) { - dateFormats.add(PreJava9DateFormatProvider.getUSDateTimeFormat(dateStyle, timeStyle)); + dateFormats.add(PreJava9DateFormatProvider.getUsDateTimeFormat(dateStyle, timeStyle)); } } @@ -187,10 +187,13 @@ private Date deserializeToDate(JsonReader in) throws IOException { // Needs to be synchronized since JDK DateFormat classes are not thread-safe synchronized (dateFormats) { for (DateFormat dateFormat : dateFormats) { + TimeZone originalTimeZone = dateFormat.getTimeZone(); try { return dateFormat.parse(s); } catch (ParseException ignored) { // OK: try the next format + } finally { + dateFormat.setTimeZone(originalTimeZone); } } } diff --git a/gson/src/main/java/com/google/gson/internal/bind/JsonAdapterAnnotationTypeAdapterFactory.java b/gson/src/main/java/com/google/gson/internal/bind/JsonAdapterAnnotationTypeAdapterFactory.java index 0ce1b0317a..822956742c 100644 --- a/gson/src/main/java/com/google/gson/internal/bind/JsonAdapterAnnotationTypeAdapterFactory.java +++ b/gson/src/main/java/com/google/gson/internal/bind/JsonAdapterAnnotationTypeAdapterFactory.java @@ -67,7 +67,7 @@ public JsonAdapterAnnotationTypeAdapterFactory(ConstructorConstructor constructo } // Separate helper method to make sure callers retrieve annotation in a consistent way - private JsonAdapter getAnnotation(Class rawType) { + private static JsonAdapter getAnnotation(Class rawType) { return rawType.getAnnotation(JsonAdapter.class); } diff --git a/gson/src/main/java/com/google/gson/internal/bind/JsonTreeReader.java b/gson/src/main/java/com/google/gson/internal/bind/JsonTreeReader.java index d8e1ebe759..fc23c2d53d 100644 --- a/gson/src/main/java/com/google/gson/internal/bind/JsonTreeReader.java +++ b/gson/src/main/java/com/google/gson/internal/bind/JsonTreeReader.java @@ -371,13 +371,13 @@ private String getPath(boolean usePreviousPath) { } @Override - public String getPreviousPath() { - return getPath(true); + public String getPath() { + return getPath(false); } @Override - public String getPath() { - return getPath(false); + public String getPreviousPath() { + return getPath(true); } private String locationString() { diff --git a/gson/src/main/java/com/google/gson/internal/bind/JsonTreeWriter.java b/gson/src/main/java/com/google/gson/internal/bind/JsonTreeWriter.java index 3aa8b22d46..fda2cf131c 100644 --- a/gson/src/main/java/com/google/gson/internal/bind/JsonTreeWriter.java +++ b/gson/src/main/java/com/google/gson/internal/bind/JsonTreeWriter.java @@ -167,18 +167,6 @@ public JsonWriter value(String value) throws IOException { return this; } - @Override - public JsonWriter jsonValue(String value) throws IOException { - throw new UnsupportedOperationException(); - } - - @CanIgnoreReturnValue - @Override - public JsonWriter nullValue() throws IOException { - put(JsonNull.INSTANCE); - return this; - } - @CanIgnoreReturnValue @Override public JsonWriter value(boolean value) throws IOException { @@ -241,6 +229,18 @@ public JsonWriter value(Number value) throws IOException { return this; } + @CanIgnoreReturnValue + @Override + public JsonWriter nullValue() throws IOException { + put(JsonNull.INSTANCE); + return this; + } + + @Override + public JsonWriter jsonValue(String value) throws IOException { + throw new UnsupportedOperationException(); + } + @Override public void flush() throws IOException {} diff --git a/gson/src/main/java/com/google/gson/internal/bind/ReflectiveTypeAdapterFactory.java b/gson/src/main/java/com/google/gson/internal/bind/ReflectiveTypeAdapterFactory.java index b7f8393ddb..79e93bcc77 100644 --- a/gson/src/main/java/com/google/gson/internal/bind/ReflectiveTypeAdapterFactory.java +++ b/gson/src/main/java/com/google/gson/internal/bind/ReflectiveTypeAdapterFactory.java @@ -197,7 +197,9 @@ private BoundField createBoundField( constructorConstructor, context, fieldType, annotation, false); } final boolean jsonAdapterPresent = mapped != null; - if (mapped == null) mapped = context.getAdapter(fieldType); + if (mapped == null) { + mapped = context.getAdapter(fieldType); + } @SuppressWarnings("unchecked") final TypeAdapter typeAdapter = (TypeAdapter) mapped; 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 5d4bd1b5a2..53803bea32 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 @@ -594,7 +594,7 @@ public URL read(JsonReader in) throws IOException { return null; } String nextString = in.nextString(); - return "null".equals(nextString) ? null : new URL(nextString); + return nextString.equals("null") ? null : new URL(nextString); } @Override @@ -615,7 +615,7 @@ public URI read(JsonReader in) throws IOException { } try { String nextString = in.nextString(); - return "null".equals(nextString) ? null : new URI(nextString); + return nextString.equals("null") ? null : new URI(nextString); } catch (URISyntaxException e) { throw new JsonIOException(e); } @@ -724,18 +724,27 @@ public Calendar read(JsonReader in) throws IOException { while (in.peek() != JsonToken.END_OBJECT) { String name = in.nextName(); int value = in.nextInt(); - if (YEAR.equals(name)) { - year = value; - } else if (MONTH.equals(name)) { - month = value; - } else if (DAY_OF_MONTH.equals(name)) { - dayOfMonth = value; - } else if (HOUR_OF_DAY.equals(name)) { - hourOfDay = value; - } else if (MINUTE.equals(name)) { - minute = value; - } else if (SECOND.equals(name)) { - second = value; + switch (name) { + case YEAR: + year = value; + break; + case MONTH: + month = value; + break; + case DAY_OF_MONTH: + dayOfMonth = value; + break; + case HOUR_OF_DAY: + hourOfDay = value; + break; + case MINUTE: + minute = value; + break; + case SECOND: + second = value; + break; + default: + // Ignore unknown JSON property } } in.endObject(); @@ -1030,6 +1039,7 @@ public TypeAdapter create(Gson gson, TypeToken typeToken) { } }; + @SuppressWarnings("TypeParameterNaming") public static TypeAdapterFactory newFactory( final TypeToken type, final TypeAdapter typeAdapter) { return new TypeAdapterFactory() { @@ -1041,6 +1051,7 @@ public TypeAdapter create(Gson gson, TypeToken typeToken) { }; } + @SuppressWarnings("TypeParameterNaming") public static TypeAdapterFactory newFactory( final Class type, final TypeAdapter typeAdapter) { return new TypeAdapterFactory() { @@ -1057,6 +1068,7 @@ public String toString() { }; } + @SuppressWarnings("TypeParameterNaming") public static TypeAdapterFactory newFactory( final Class unboxed, final Class boxed, final TypeAdapter typeAdapter) { return new TypeAdapterFactory() { @@ -1080,6 +1092,7 @@ public String toString() { }; } + @SuppressWarnings("TypeParameterNaming") public static TypeAdapterFactory newFactoryForMultipleTypes( final Class base, final Class sub, diff --git a/gson/src/main/java/com/google/gson/internal/bind/util/ISO8601Utils.java b/gson/src/main/java/com/google/gson/internal/bind/util/ISO8601Utils.java index 05dbafa096..28074a10d1 100644 --- a/gson/src/main/java/com/google/gson/internal/bind/util/ISO8601Utils.java +++ b/gson/src/main/java/com/google/gson/internal/bind/util/ISO8601Utils.java @@ -36,6 +36,8 @@ // Date parsing code from Jackson databind ISO8601Utils.java // https://github.com/FasterXML/jackson-databind/blob/2.8/src/main/java/com/fasterxml/jackson/databind/util/ISO8601Utils.java public class ISO8601Utils { + private ISO8601Utils() {} + /** * ID to represent the 'UTC' string, default timezone since Jackson 2.7 * @@ -197,7 +199,9 @@ public static Date parse(String date, ParsePosition pos) throws ParseException { char c = date.charAt(offset); if (c != 'Z' && c != '+' && c != '-') { seconds = parseInt(date, offset, offset += 2); - if (seconds > 59 && seconds < 63) seconds = 59; // truncate up to 3 leap seconds + if (seconds > 59 && seconds < 63) { + seconds = 59; // truncate up to 3 leap seconds + } // milliseconds can be optional in the format if (checkOffset(date, offset, '.')) { offset += 1; @@ -241,7 +245,7 @@ public static Date parse(String date, ParsePosition pos) throws ParseException { offset += timezoneOffset.length(); // 18-Jun-2015, tatu: Minor simplification, skip offset of "+0000"/"+00:00" - if ("+0000".equals(timezoneOffset) || "+00:00".equals(timezoneOffset)) { + if (timezoneOffset.equals("+0000") || timezoneOffset.equals("+00:00")) { timezone = TIMEZONE_UTC; } else { // 18-Jun-2015, tatu: Looks like offsets only work from GMT, not UTC... @@ -372,7 +376,9 @@ private static void padInt(StringBuilder buffer, int value, int length) { private static int indexOfNonDigit(String string, int offset) { for (int i = offset; i < string.length(); i++) { char c = string.charAt(i); - if (c < '0' || c > '9') return i; + if (c < '0' || c > '9') { + return i; + } } return string.length(); } diff --git a/gson/src/main/java/com/google/gson/internal/sql/SqlDateTypeAdapter.java b/gson/src/main/java/com/google/gson/internal/sql/SqlDateTypeAdapter.java index bebd6ca777..1991daefb0 100644 --- a/gson/src/main/java/com/google/gson/internal/sql/SqlDateTypeAdapter.java +++ b/gson/src/main/java/com/google/gson/internal/sql/SqlDateTypeAdapter.java @@ -29,6 +29,7 @@ import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; +import java.util.TimeZone; /** * Adapter for java.sql.Date. Although this class appears stateless, it is not. DateFormat captures @@ -59,15 +60,17 @@ public java.sql.Date read(JsonReader in) throws IOException { return null; } String s = in.nextString(); - try { - Date utilDate; - synchronized (this) { - utilDate = format.parse(s); + synchronized (this) { + TimeZone originalTimeZone = format.getTimeZone(); // Save the original time zone + try { + Date utilDate = format.parse(s); + return new java.sql.Date(utilDate.getTime()); + } catch (ParseException e) { + throw new JsonSyntaxException( + "Failed parsing '" + s + "' as SQL Date; at path " + in.getPreviousPath(), e); + } finally { + format.setTimeZone(originalTimeZone); // Restore the original time zone after parsing } - return new java.sql.Date(utilDate.getTime()); - } catch (ParseException e) { - throw new JsonSyntaxException( - "Failed parsing '" + s + "' as SQL Date; at path " + in.getPreviousPath(), e); } } diff --git a/gson/src/main/java/com/google/gson/internal/sql/SqlTimeTypeAdapter.java b/gson/src/main/java/com/google/gson/internal/sql/SqlTimeTypeAdapter.java index 25a6fda95a..d63ae0677e 100644 --- a/gson/src/main/java/com/google/gson/internal/sql/SqlTimeTypeAdapter.java +++ b/gson/src/main/java/com/google/gson/internal/sql/SqlTimeTypeAdapter.java @@ -30,6 +30,7 @@ import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; +import java.util.TimeZone; /** * Adapter for java.sql.Time. Although this class appears stateless, it is not. DateFormat captures @@ -60,14 +61,17 @@ public Time read(JsonReader in) throws IOException { return null; } String s = in.nextString(); - try { - synchronized (this) { + synchronized (this) { + TimeZone originalTimeZone = format.getTimeZone(); // Save the original time zone + try { Date date = format.parse(s); return new Time(date.getTime()); + } catch (ParseException e) { + throw new JsonSyntaxException( + "Failed parsing '" + s + "' as SQL Time; at path " + in.getPreviousPath(), e); + } finally { + format.setTimeZone(originalTimeZone); // Restore the original time zone } - } catch (ParseException e) { - throw new JsonSyntaxException( - "Failed parsing '" + s + "' as SQL Time; at path " + in.getPreviousPath(), e); } } diff --git a/gson/src/main/java/com/google/gson/stream/JsonReader.java b/gson/src/main/java/com/google/gson/stream/JsonReader.java index 2f6cae5faf..2dc4e65402 100644 --- a/gson/src/main/java/com/google/gson/stream/JsonReader.java +++ b/gson/src/main/java/com/google/gson/stream/JsonReader.java @@ -1141,6 +1141,8 @@ private String nextUnquotedValue() throws IOException { case '\r': case '\n': break findNonLiteralCharacter; + default: + // skip character to be included in string value } } @@ -1223,6 +1225,8 @@ private void skipUnquotedValue() throws IOException { case '\n': pos += i; return; + default: + // skip the character } } pos += i; @@ -1380,6 +1384,7 @@ public void skipValue() throws IOException { case PEEKED_EOF: // Do nothing return; + default: // For all other tokens there is nothing to do; token has already been consumed from // underlying reader } @@ -1586,7 +1591,8 @@ String locationString() { private String getPath(boolean usePreviousPath) { StringBuilder result = new StringBuilder().append('$'); for (int i = 0; i < stackSize; i++) { - switch (stack[i]) { + int scope = stack[i]; + switch (scope) { case JsonScope.EMPTY_ARRAY: case JsonScope.NONEMPTY_ARRAY: int pathIndex = pathIndices[i]; @@ -1608,6 +1614,8 @@ private String getPath(boolean usePreviousPath) { case JsonScope.EMPTY_DOCUMENT: case JsonScope.CLOSED: break; + default: + throw new AssertionError("Unknown scope value: " + scope); } } return result.toString(); @@ -1615,38 +1623,38 @@ private String getPath(boolean usePreviousPath) { /** * Returns a JSONPath in dot-notation - * to the previous (or current) location in the JSON document. That means: + * to the next (or current) location in the JSON document. That means: * *
    - *
  • For JSON arrays the path points to the index of the previous element.
    - * If no element has been consumed yet it uses the index 0 (even if there are no elements). + *
  • For JSON arrays the path points to the index of the next element (even if there are no + * further elements). *
  • For JSON objects the path points to the last property, or to the current property if its * name has already been consumed. *
* - *

This method can be useful to add additional context to exception messages after a - * value has been consumed. + *

This method can be useful to add additional context to exception messages before a + * value is consumed, for example when the {@linkplain #peek() peeked} token is unexpected. */ - public String getPreviousPath() { - return getPath(true); + public String getPath() { + return getPath(false); } /** * Returns a JSONPath in dot-notation - * to the next (or current) location in the JSON document. That means: + * to the previous (or current) location in the JSON document. That means: * *

    - *
  • For JSON arrays the path points to the index of the next element (even if there are no - * further elements). + *
  • For JSON arrays the path points to the index of the previous element.
    + * If no element has been consumed yet it uses the index 0 (even if there are no elements). *
  • For JSON objects the path points to the last property, or to the current property if its * name has already been consumed. *
* - *

This method can be useful to add additional context to exception messages before a - * value is consumed, for example when the {@linkplain #peek() peeked} token is unexpected. + *

This method can be useful to add additional context to exception messages after a + * value has been consumed. */ - public String getPath() { - return getPath(false); + public String getPreviousPath() { + return getPath(true); } /** diff --git a/gson/src/main/java/com/google/gson/stream/JsonScope.java b/gson/src/main/java/com/google/gson/stream/JsonScope.java index f77b53da7c..16259578c3 100644 --- a/gson/src/main/java/com/google/gson/stream/JsonScope.java +++ b/gson/src/main/java/com/google/gson/stream/JsonScope.java @@ -23,6 +23,7 @@ * @since 1.6 */ final class JsonScope { + private JsonScope() {} /** An array with no elements requires no separator before the next element. */ static final int EMPTY_ARRAY = 1; diff --git a/gson/src/main/java/com/google/gson/stream/JsonWriter.java b/gson/src/main/java/com/google/gson/stream/JsonWriter.java index fd0b6b8a65..498a0ab4e1 100644 --- a/gson/src/main/java/com/google/gson/stream/JsonWriter.java +++ b/gson/src/main/java/com/google/gson/stream/JsonWriter.java @@ -396,7 +396,7 @@ public final boolean getSerializeNulls() { @CanIgnoreReturnValue public JsonWriter beginArray() throws IOException { writeDeferredName(); - return open(EMPTY_ARRAY, '['); + return openScope(EMPTY_ARRAY, '['); } /** @@ -406,7 +406,7 @@ public JsonWriter beginArray() throws IOException { */ @CanIgnoreReturnValue public JsonWriter endArray() throws IOException { - return close(EMPTY_ARRAY, NONEMPTY_ARRAY, ']'); + return closeScope(EMPTY_ARRAY, NONEMPTY_ARRAY, ']'); } /** @@ -418,7 +418,7 @@ public JsonWriter endArray() throws IOException { @CanIgnoreReturnValue public JsonWriter beginObject() throws IOException { writeDeferredName(); - return open(EMPTY_OBJECT, '{'); + return openScope(EMPTY_OBJECT, '{'); } /** @@ -428,12 +428,12 @@ public JsonWriter beginObject() throws IOException { */ @CanIgnoreReturnValue public JsonWriter endObject() throws IOException { - return close(EMPTY_OBJECT, NONEMPTY_OBJECT, '}'); + return closeScope(EMPTY_OBJECT, NONEMPTY_OBJECT, '}'); } /** Enters a new scope by appending any necessary whitespace and the given bracket. */ @CanIgnoreReturnValue - private JsonWriter open(int empty, char openBracket) throws IOException { + private JsonWriter openScope(int empty, char openBracket) throws IOException { beforeValue(); push(empty); out.write(openBracket); @@ -442,7 +442,7 @@ private JsonWriter open(int empty, char openBracket) throws IOException { /** Closes the current scope by appending any necessary whitespace and the given bracket. */ @CanIgnoreReturnValue - private JsonWriter close(int empty, int nonempty, char closeBracket) throws IOException { + private JsonWriter closeScope(int empty, int nonempty, char closeBracket) throws IOException { int context = peek(); if (context != nonempty && context != empty) { throw new IllegalStateException("Nesting problem."); @@ -524,47 +524,6 @@ public JsonWriter value(String value) throws IOException { return this; } - /** - * Writes {@code value} directly to the writer without quoting or escaping. This might not be - * supported by all implementations, if not supported an {@code UnsupportedOperationException} is - * thrown. - * - * @param value the literal string value, or null to encode a null literal. - * @return this writer. - * @throws UnsupportedOperationException if this writer does not support writing raw JSON values. - * @since 2.4 - */ - @CanIgnoreReturnValue - public JsonWriter jsonValue(String value) throws IOException { - if (value == null) { - return nullValue(); - } - writeDeferredName(); - beforeValue(); - out.append(value); - return this; - } - - /** - * Encodes {@code null}. - * - * @return this writer. - */ - @CanIgnoreReturnValue - public JsonWriter nullValue() throws IOException { - if (deferredName != null) { - if (serializeNulls) { - writeDeferredName(); - } else { - deferredName = null; - return this; // skip the name and the value - } - } - beforeValue(); - out.write("null"); - return this; - } - /** * Encodes {@code value}. * @@ -649,25 +608,6 @@ public JsonWriter value(long value) throws IOException { return this; } - /** - * Returns whether the {@code toString()} of {@code c} can be trusted to return a valid JSON - * number. - */ - private static boolean isTrustedNumberType(Class c) { - // Note: Don't consider LazilyParsedNumber trusted because it could contain - // an arbitrary malformed string - return c == Integer.class - || c == Long.class - || c == Double.class - || c == Float.class - || c == Byte.class - || c == Short.class - || c == BigDecimal.class - || c == BigInteger.class - || c == AtomicInteger.class - || c == AtomicLong.class; - } - /** * Encodes {@code value}. The value is written by directly writing the {@link Number#toString()} * result to JSON. Implementations must make sure that the result represents a valid JSON number. @@ -706,6 +646,47 @@ public JsonWriter value(Number value) throws IOException { return this; } + /** + * Encodes {@code null}. + * + * @return this writer. + */ + @CanIgnoreReturnValue + public JsonWriter nullValue() throws IOException { + if (deferredName != null) { + if (serializeNulls) { + writeDeferredName(); + } else { + deferredName = null; + return this; // skip the name and the value + } + } + beforeValue(); + out.write("null"); + return this; + } + + /** + * Writes {@code value} directly to the writer without quoting or escaping. This might not be + * supported by all implementations, if not supported an {@code UnsupportedOperationException} is + * thrown. + * + * @param value the literal string value, or null to encode a null literal. + * @return this writer. + * @throws UnsupportedOperationException if this writer does not support writing raw JSON values. + * @since 2.4 + */ + @CanIgnoreReturnValue + public JsonWriter jsonValue(String value) throws IOException { + if (value == null) { + return nullValue(); + } + writeDeferredName(); + beforeValue(); + out.append(value); + return this; + } + /** * Ensures all buffered data is written to the underlying {@link Writer} and flushes that writer. */ @@ -733,6 +714,25 @@ public void close() throws IOException { stackSize = 0; } + /** + * Returns whether the {@code toString()} of {@code c} can be trusted to return a valid JSON + * number. + */ + private static boolean isTrustedNumberType(Class c) { + // Note: Don't consider LazilyParsedNumber trusted because it could contain + // an arbitrary malformed string + return c == Integer.class + || c == Long.class + || c == Double.class + || c == Float.class + || c == Byte.class + || c == Short.class + || c == BigDecimal.class + || c == BigInteger.class + || c == AtomicInteger.class + || c == AtomicLong.class; + } + private void string(String value) throws IOException { String[] replacements = htmlSafe ? HTML_SAFE_REPLACEMENT_CHARS : REPLACEMENT_CHARS; out.write('\"'); diff --git a/gson/src/test/java/com/google/gson/FieldNamingPolicyTest.java b/gson/src/test/java/com/google/gson/FieldNamingPolicyTest.java index aefb2125fd..0d085269f8 100644 --- a/gson/src/test/java/com/google/gson/FieldNamingPolicyTest.java +++ b/gson/src/test/java/com/google/gson/FieldNamingPolicyTest.java @@ -115,7 +115,7 @@ class Dummy { @Test public void testLowerCasingLocaleIndependent() throws Exception { class Dummy { - @SuppressWarnings("unused") + @SuppressWarnings({"unused", "ConstantField"}) int I; } diff --git a/gson/src/test/java/com/google/gson/GsonTypeAdapterTest.java b/gson/src/test/java/com/google/gson/GsonTypeAdapterTest.java index 0825c8f8ed..6422bacc42 100644 --- a/gson/src/test/java/com/google/gson/GsonTypeAdapterTest.java +++ b/gson/src/test/java/com/google/gson/GsonTypeAdapterTest.java @@ -151,7 +151,7 @@ public void testDeserializerForAbstractClass() { assertSerialized("{\"b\":\"beep\",\"a\":\"android\"}", Concrete.class, false, false, instance); } - private void assertSerialized( + private static void assertSerialized( String expected, Class instanceType, boolean registerAbstractDeserializer, diff --git a/gson/src/test/java/com/google/gson/JavaSerializationTest.java b/gson/src/test/java/com/google/gson/JavaSerializationTest.java index d18a6f3658..219e16bb56 100644 --- a/gson/src/test/java/com/google/gson/JavaSerializationTest.java +++ b/gson/src/test/java/com/google/gson/JavaSerializationTest.java @@ -66,7 +66,7 @@ public void testNumberIsSerializable() throws Exception { } @SuppressWarnings("unchecked") // Serialization promises to return the same type. - private T serializedCopy(T object) throws IOException, ClassNotFoundException { + private static T serializedCopy(T object) throws IOException, ClassNotFoundException { ByteArrayOutputStream bytesOut = new ByteArrayOutputStream(); ObjectOutputStream out = new ObjectOutputStream(bytesOut); out.writeObject(object); diff --git a/gson/src/test/java/com/google/gson/ParameterizedTypeFixtures.java b/gson/src/test/java/com/google/gson/ParameterizedTypeFixtures.java index fa4d96dbfb..34b7adc70c 100644 --- a/gson/src/test/java/com/google/gson/ParameterizedTypeFixtures.java +++ b/gson/src/test/java/com/google/gson/ParameterizedTypeFixtures.java @@ -19,7 +19,6 @@ import com.google.common.base.Objects; import com.google.gson.internal.$Gson$Types; import com.google.gson.internal.Primitives; -import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; @@ -33,6 +32,7 @@ * @author Joel Leitch */ public class ParameterizedTypeFixtures { + private ParameterizedTypeFixtures() {} public static final class MyParameterizedType { public final T value; @@ -50,7 +50,7 @@ public String getExpectedJson() { return String.format("{\"value\":%s}", valueAsJson); } - private String getExpectedJson(Object obj) { + private static String getExpectedJson(Object obj) { Class clazz = obj.getClass(); if (Primitives.isWrapperType(Primitives.wrap(clazz))) { return obj.toString(); @@ -62,15 +62,7 @@ private String getExpectedJson(Object obj) { Method method = clazz.getMethod("getExpectedJson"); Object results = method.invoke(obj); return (String) results; - } catch (SecurityException e) { - throw new RuntimeException(e); - } catch (NoSuchMethodException e) { - throw new RuntimeException(e); - } catch (IllegalArgumentException e) { - throw new RuntimeException(e); - } catch (IllegalAccessException e) { - throw new RuntimeException(e); - } catch (InvocationTargetException e) { + } catch (ReflectiveOperationException e) { throw new RuntimeException(e); } } diff --git a/gson/src/test/java/com/google/gson/PrimitiveTypeAdapter.java b/gson/src/test/java/com/google/gson/PrimitiveTypeAdapter.java index bc626c2c41..8b9ae41545 100644 --- a/gson/src/test/java/com/google/gson/PrimitiveTypeAdapter.java +++ b/gson/src/test/java/com/google/gson/PrimitiveTypeAdapter.java @@ -18,7 +18,6 @@ import com.google.gson.internal.Primitives; import java.lang.reflect.Constructor; -import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; /** @@ -45,13 +44,7 @@ public T adaptType(Object from, Class to) { try { Constructor constructor = aClass.getConstructor(String.class); return (T) constructor.newInstance(from.toString()); - } catch (NoSuchMethodException e) { - throw new JsonParseException(e); - } catch (IllegalAccessException e) { - throw new JsonParseException(e); - } catch (InvocationTargetException e) { - throw new JsonParseException(e); - } catch (InstantiationException e) { + } catch (ReflectiveOperationException e) { throw new JsonParseException(e); } } else if (Enum.class.isAssignableFrom(to)) { @@ -60,11 +53,7 @@ public T adaptType(Object from, Class to) { try { Method valuesMethod = to.getMethod("valueOf", String.class); return (T) valuesMethod.invoke(null, from.toString()); - } catch (NoSuchMethodException e) { - throw new RuntimeException(e); - } catch (IllegalAccessException e) { - throw new RuntimeException(e); - } catch (InvocationTargetException e) { + } catch (ReflectiveOperationException e) { throw new RuntimeException(e); } } else { diff --git a/gson/src/test/java/com/google/gson/common/MoreAsserts.java b/gson/src/test/java/com/google/gson/common/MoreAsserts.java index 39128fbf3e..3b74056dcc 100644 --- a/gson/src/test/java/com/google/gson/common/MoreAsserts.java +++ b/gson/src/test/java/com/google/gson/common/MoreAsserts.java @@ -30,6 +30,7 @@ * @author Inderjeet Singh */ public class MoreAsserts { + private MoreAsserts() {} /** * Asserts that the specified {@code value} is not present in {@code collection} diff --git a/gson/src/test/java/com/google/gson/common/TestTypes.java b/gson/src/test/java/com/google/gson/common/TestTypes.java index a0fde26c77..b41f4b36da 100644 --- a/gson/src/test/java/com/google/gson/common/TestTypes.java +++ b/gson/src/test/java/com/google/gson/common/TestTypes.java @@ -36,6 +36,7 @@ * @author Joel Leitch */ public class TestTypes { + private TestTypes() {} public static class Base { public static final String BASE_NAME = Base.class.getSimpleName(); 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 70d597c3e4..af3ffe11ca 100644 --- a/gson/src/test/java/com/google/gson/functional/DefaultTypeAdaptersTest.java +++ b/gson/src/test/java/com/google/gson/functional/DefaultTypeAdaptersTest.java @@ -16,7 +16,7 @@ package com.google.gson.functional; import static com.google.common.truth.Truth.assertThat; -import static org.junit.Assert.fail; +import static org.junit.Assert.assertThrows; import com.google.gson.Gson; import com.google.gson.GsonBuilder; @@ -90,17 +90,15 @@ public void tearDown() { @Test public void testClassSerialization() { - try { - gson.toJson(String.class); - fail(); - } catch (UnsupportedOperationException expected) { - assertThat(expected) - .hasMessageThat() - .isEqualTo( - "Attempted to serialize java.lang.Class: java.lang.String. Forgot to register a type" - + " adapter?\n" - + "See https://github.com/google/gson/blob/main/Troubleshooting.md#java-lang-class-unsupported"); - } + var exception = + assertThrows(UnsupportedOperationException.class, () -> gson.toJson(String.class)); + assertThat(exception) + .hasMessageThat() + .isEqualTo( + "Attempted to serialize java.lang.Class: java.lang.String. Forgot to register a type" + + " adapter?\n" + + "See https://github.com/google/gson/blob/main/Troubleshooting.md#java-lang-class-unsupported"); + // Override with a custom type adapter for class. gson = new GsonBuilder().registerTypeAdapter(Class.class, new MyClassTypeAdapter()).create(); assertThat(gson.toJson(String.class)).isEqualTo("\"java.lang.String\""); @@ -108,16 +106,15 @@ public void testClassSerialization() { @Test public void testClassDeserialization() { - try { - gson.fromJson("String.class", Class.class); - fail(); - } catch (UnsupportedOperationException expected) { - assertThat(expected) - .hasMessageThat() - .isEqualTo( - "Attempted to deserialize a java.lang.Class. Forgot to register a type adapter?\n" - + "See https://github.com/google/gson/blob/main/Troubleshooting.md#java-lang-class-unsupported"); - } + var exception = + assertThrows( + UnsupportedOperationException.class, () -> gson.fromJson("String.class", Class.class)); + assertThat(exception) + .hasMessageThat() + .isEqualTo( + "Attempted to deserialize a java.lang.Class. Forgot to register a type adapter?\n" + + "See https://github.com/google/gson/blob/main/Troubleshooting.md#java-lang-class-unsupported"); + // Override with a custom type adapter for class. gson = new GsonBuilder().registerTypeAdapter(Class.class, new MyClassTypeAdapter()).create(); assertThat(gson.fromJson("java.lang.String", Class.class)).isAssignableTo(String.class); @@ -289,11 +286,10 @@ public void testBigDecimalFieldDeserialization() { @Test public void testBadValueForBigDecimalDeserialization() { - try { - gson.fromJson("{\"value\"=1.5e-1.0031}", ClassWithBigDecimal.class); - fail("Exponent of a BigDecimal must be an integer value."); - } catch (JsonParseException expected) { - } + // Exponent of a BigDecimal must be an integer value + assertThrows( + JsonParseException.class, + () -> gson.fromJson("{\"value\": 1.5e-1.0031}", ClassWithBigDecimal.class)); } @Test @@ -374,23 +370,17 @@ public void testBitSetDeserialization() { json = "[true,false,true,true,true,true,false,false,true,false,false]"; assertThat(gson.fromJson(json, BitSet.class)).isEqualTo(expected); - try { - gson.fromJson("[1, []]", BitSet.class); - fail(); - } catch (JsonSyntaxException e) { - assertThat(e) - .hasMessageThat() - .isEqualTo("Invalid bitset value type: BEGIN_ARRAY; at path $[1]"); - } + var exception = + assertThrows(JsonSyntaxException.class, () -> gson.fromJson("[1, []]", BitSet.class)); + assertThat(exception) + .hasMessageThat() + .isEqualTo("Invalid bitset value type: BEGIN_ARRAY; at path $[1]"); - try { - gson.fromJson("[1, 2]", BitSet.class); - fail(); - } catch (JsonSyntaxException e) { - assertThat(e) - .hasMessageThat() - .isEqualTo("Invalid bitset value 2, expected 0 or 1; at path $[1]"); - } + exception = + assertThrows(JsonSyntaxException.class, () -> gson.fromJson("[1, 2]", BitSet.class)); + assertThat(exception) + .hasMessageThat() + .isEqualTo("Invalid bitset value 2, expected 0 or 1; at path $[1]"); } @Test @@ -468,28 +458,47 @@ public void testDefaultCalendarDeserialization() { @Test public void testDefaultGregorianCalendarSerialization() { + GregorianCalendar cal = new GregorianCalendar(TimeZone.getTimeZone("UTC"), Locale.US); + // Calendar was created with current time, must clear it + cal.clear(); + cal.set(2018, Calendar.JUNE, 25, 10, 20, 30); + Gson gson = new GsonBuilder().create(); - GregorianCalendar cal = new GregorianCalendar(); String json = gson.toJson(cal); - assertThat(json).contains("year"); - assertThat(json).contains("month"); - assertThat(json).contains("dayOfMonth"); - assertThat(json).contains("hourOfDay"); - assertThat(json).contains("minute"); - assertThat(json).contains("second"); + assertThat(json) + .isEqualTo( + "{\"year\":2018,\"month\":5,\"dayOfMonth\":25,\"hourOfDay\":10,\"minute\":20,\"second\":30}"); } @Test public void testDefaultGregorianCalendarDeserialization() { - Gson gson = new GsonBuilder().create(); - String json = "{year:2009,month:2,dayOfMonth:11,hourOfDay:14,minute:29,second:23}"; - GregorianCalendar cal = gson.fromJson(json, GregorianCalendar.class); - assertThat(cal.get(Calendar.YEAR)).isEqualTo(2009); - assertThat(cal.get(Calendar.MONTH)).isEqualTo(2); - assertThat(cal.get(Calendar.DAY_OF_MONTH)).isEqualTo(11); - assertThat(cal.get(Calendar.HOUR_OF_DAY)).isEqualTo(14); - assertThat(cal.get(Calendar.MINUTE)).isEqualTo(29); - assertThat(cal.get(Calendar.SECOND)).isEqualTo(23); + TimeZone defaultTimeZone = TimeZone.getDefault(); + Locale defaultLocale = Locale.getDefault(); + + try { + // Calendar deserialization uses default TimeZone and Locale; set them here to make the test + // deterministic + TimeZone.setDefault(TimeZone.getTimeZone("UTC")); + Locale.setDefault(Locale.US); + + Gson gson = new GsonBuilder().create(); + String json = + "{\"year\":2009,\"month\":2,\"dayOfMonth\":11,\"hourOfDay\":14,\"minute\":29,\"second\":23}"; + GregorianCalendar cal = gson.fromJson(json, GregorianCalendar.class); + assertThat(cal.get(Calendar.YEAR)).isEqualTo(2009); + assertThat(cal.get(Calendar.MONTH)).isEqualTo(2); + assertThat(cal.get(Calendar.DAY_OF_MONTH)).isEqualTo(11); + assertThat(cal.get(Calendar.HOUR_OF_DAY)).isEqualTo(14); + assertThat(cal.get(Calendar.MINUTE)).isEqualTo(29); + assertThat(cal.get(Calendar.SECOND)).isEqualTo(23); + assertThat(cal.getTimeInMillis()).isEqualTo(1236781763000L); + + // Serializing value again should be equivalent to original JSON + assertThat(gson.toJson(cal)).isEqualTo(json); + } finally { + TimeZone.setDefault(defaultTimeZone); + Locale.setDefault(defaultLocale); + } } @Test @@ -654,16 +663,13 @@ public void testJsonNullDeserialization() { @Test public void testJsonElementTypeMismatch() { - try { - gson.fromJson("\"abc\"", JsonObject.class); - fail(); - } catch (JsonSyntaxException expected) { - assertThat(expected) - .hasMessageThat() - .isEqualTo( - "Expected a com.google.gson.JsonObject but was com.google.gson.JsonPrimitive;" - + " at path $"); - } + var exception = + assertThrows(JsonSyntaxException.class, () -> gson.fromJson("\"abc\"", JsonObject.class)); + assertThat(exception) + .hasMessageThat() + .isEqualTo( + "Expected a com.google.gson.JsonObject but was com.google.gson.JsonPrimitive;" + + " at path $"); } private static class ClassWithBigDecimal { diff --git a/gson/src/test/java/com/google/gson/functional/EscapingTest.java b/gson/src/test/java/com/google/gson/functional/EscapingTest.java index 1383c67cbe..1eeeb1768d 100644 --- a/gson/src/test/java/com/google/gson/functional/EscapingTest.java +++ b/gson/src/test/java/com/google/gson/functional/EscapingTest.java @@ -70,8 +70,8 @@ public void testEscapingObjectFields() { assertThat(jsonRepresentation).doesNotContain(">"); assertThat(jsonRepresentation).contains("\\\""); - BagOfPrimitives expectedObject = gson.fromJson(jsonRepresentation, BagOfPrimitives.class); - assertThat(objWithPrimitives.getExpectedJson()).isEqualTo(expectedObject.getExpectedJson()); + BagOfPrimitives deserialized = gson.fromJson(jsonRepresentation, BagOfPrimitives.class); + assertThat(deserialized.getExpectedJson()).isEqualTo(objWithPrimitives.getExpectedJson()); } @Test diff --git a/gson/src/test/java/com/google/gson/functional/FieldNamingTest.java b/gson/src/test/java/com/google/gson/functional/FieldNamingTest.java index 308fe1aa5d..36f03f2889 100644 --- a/gson/src/test/java/com/google/gson/functional/FieldNamingTest.java +++ b/gson/src/test/java/com/google/gson/functional/FieldNamingTest.java @@ -91,11 +91,12 @@ public void testLowerCaseWithDashes() { + "'annotatedName':7,'lower-id':8,'_9':9}"); } - private Gson getGsonWithNamingPolicy(FieldNamingPolicy fieldNamingPolicy) { + private static Gson getGsonWithNamingPolicy(FieldNamingPolicy fieldNamingPolicy) { return new GsonBuilder().setFieldNamingPolicy(fieldNamingPolicy).create(); } - @SuppressWarnings("unused") // fields are used reflectively + // Suppress because fields are used reflectively, and the names are intentionally unconventional + @SuppressWarnings({"unused", "MemberName", "ConstantField"}) private static class TestNames { int lowerCamel = 1; int UpperCamel = 2; diff --git a/gson/src/test/java/com/google/gson/functional/GsonVersionDiagnosticsTest.java b/gson/src/test/java/com/google/gson/functional/GsonVersionDiagnosticsTest.java index d47aed1aa8..1cbeb4156e 100644 --- a/gson/src/test/java/com/google/gson/functional/GsonVersionDiagnosticsTest.java +++ b/gson/src/test/java/com/google/gson/functional/GsonVersionDiagnosticsTest.java @@ -79,7 +79,7 @@ public void testAssertionErrorInDeserializationPrintsVersion() { ensureAssertionErrorPrintsGsonVersion(e); } - private void ensureAssertionErrorPrintsGsonVersion(AssertionError expected) { + private static void ensureAssertionErrorPrintsGsonVersion(AssertionError expected) { String msg = expected.getMessage(); // System.err.println(msg); int start = msg.indexOf("(GSON"); diff --git a/gson/src/test/java/com/google/gson/functional/InheritanceTest.java b/gson/src/test/java/com/google/gson/functional/InheritanceTest.java index b2268fcd77..f41b133fb3 100644 --- a/gson/src/test/java/com/google/gson/functional/InheritanceTest.java +++ b/gson/src/test/java/com/google/gson/functional/InheritanceTest.java @@ -275,7 +275,7 @@ public String getExpectedJson() { } @CanIgnoreReturnValue - private StringBuilder append(StringBuilder sb, Collection c) { + private static StringBuilder append(StringBuilder sb, Collection c) { sb.append("["); boolean first = true; for (Object o : c) { diff --git a/gson/src/test/java/com/google/gson/functional/Java17RecordTest.java b/gson/src/test/java/com/google/gson/functional/Java17RecordTest.java index 37a17f89d8..437fe8124e 100644 --- a/gson/src/test/java/com/google/gson/functional/Java17RecordTest.java +++ b/gson/src/test/java/com/google/gson/functional/Java17RecordTest.java @@ -354,6 +354,7 @@ public void testStaticFieldDeserialization() { } private record RecordWithStaticField() { + @SuppressWarnings("NonFinalStaticField") static String s = "initial"; } diff --git a/gson/src/test/java/com/google/gson/functional/JsonAdapterAnnotationOnClassesTest.java b/gson/src/test/java/com/google/gson/functional/JsonAdapterAnnotationOnClassesTest.java index 7ca84d7a01..3a765f1352 100644 --- a/gson/src/test/java/com/google/gson/functional/JsonAdapterAnnotationOnClassesTest.java +++ b/gson/src/test/java/com/google/gson/functional/JsonAdapterAnnotationOnClassesTest.java @@ -44,6 +44,7 @@ import org.junit.Test; /** Functional tests for the {@link JsonAdapter} annotation on classes. */ +@SuppressWarnings("ClassNamedLikeTypeParameter") // for dummy classes A, B, ... public final class JsonAdapterAnnotationOnClassesTest { @Test @@ -305,7 +306,8 @@ private static final class ClassWithIncorrectJsonAdapter { // This class is used in JsonAdapter Javadoc as an example @JsonAdapter(UserJsonAdapter.class) private static class User { - final String firstName, lastName; + final String firstName; + final String lastName; User(String firstName, String lastName) { this.firstName = firstName; diff --git a/gson/src/test/java/com/google/gson/functional/JsonAdapterSerializerDeserializerTest.java b/gson/src/test/java/com/google/gson/functional/JsonAdapterSerializerDeserializerTest.java index 090db3ac62..393aebe4a6 100644 --- a/gson/src/test/java/com/google/gson/functional/JsonAdapterSerializerDeserializerTest.java +++ b/gson/src/test/java/com/google/gson/functional/JsonAdapterSerializerDeserializerTest.java @@ -240,6 +240,7 @@ public void write(JsonWriter out, User value) throws IOException { assertThat(deserialized.userDN).isNull(); } + @SuppressWarnings("MemberName") private static final class WithNullSafe { // "userS..." uses JsonSerializer @JsonAdapter(value = UserSerializer.class, nullSafe = false) diff --git a/gson/src/test/java/com/google/gson/functional/JsonTreeTest.java b/gson/src/test/java/com/google/gson/functional/JsonTreeTest.java index e02b0cc278..4a00f399d0 100644 --- a/gson/src/test/java/com/google/gson/functional/JsonTreeTest.java +++ b/gson/src/test/java/com/google/gson/functional/JsonTreeTest.java @@ -89,7 +89,7 @@ public void testJsonTreeNull() { assertThat(jsonElement.has("stringValue")).isFalse(); } - private void assertContains(JsonObject json, JsonPrimitive child) { + private static void assertContains(JsonObject json, JsonPrimitive child) { for (Map.Entry entry : json.entrySet()) { JsonElement node = entry.getValue(); if (node.isJsonPrimitive()) { diff --git a/gson/src/test/java/com/google/gson/functional/MapTest.java b/gson/src/test/java/com/google/gson/functional/MapTest.java index cdc127ccc8..0bd11c4c7a 100644 --- a/gson/src/test/java/com/google/gson/functional/MapTest.java +++ b/gson/src/test/java/com/google/gson/functional/MapTest.java @@ -641,7 +641,7 @@ public void testDeserializeMapOfMaps() { assertThat(gson.fromJson(json, type)).isEqualTo(map); } - private Map newMap(K key1, V value1, K key2, V value2) { + private static Map newMap(K key1, V value1, K key2, V value2) { Map result = new LinkedHashMap<>(); result.put(key1, value1); result.put(key2, value2); diff --git a/gson/src/test/java/com/google/gson/functional/MoreSpecificTypeSerializationTest.java b/gson/src/test/java/com/google/gson/functional/MoreSpecificTypeSerializationTest.java index f9e1cb4c99..0702884143 100644 --- a/gson/src/test/java/com/google/gson/functional/MoreSpecificTypeSerializationTest.java +++ b/gson/src/test/java/com/google/gson/functional/MoreSpecificTypeSerializationTest.java @@ -23,6 +23,7 @@ import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; +import java.util.List; import java.util.Map; import org.junit.Before; import org.junit.Test; @@ -51,7 +52,7 @@ public void testSubclassFields() { @Test public void testListOfSubclassFields() { - Collection list = new ArrayList<>(); + List list = new ArrayList<>(); list.add(new Base(1)); list.add(new Sub(2, 3)); ClassWithContainersOfBaseFields target = new ClassWithContainersOfBaseFields(list, null); @@ -89,7 +90,7 @@ public void testParameterizedSubclassFields() { */ @Test public void testListOfParameterizedSubclassFields() { - Collection> list = new ArrayList<>(); + List> list = new ArrayList<>(); list.add(new ParameterizedBase<>("one")); list.add(new ParameterizedSub<>("two", "three")); ClassWithContainersOfParameterizedBaseFields target = diff --git a/gson/src/test/java/com/google/gson/functional/NullObjectAndFieldTest.java b/gson/src/test/java/com/google/gson/functional/NullObjectAndFieldTest.java index c0dfe1b613..d84baa4b62 100644 --- a/gson/src/test/java/com/google/gson/functional/NullObjectAndFieldTest.java +++ b/gson/src/test/java/com/google/gson/functional/NullObjectAndFieldTest.java @@ -177,12 +177,13 @@ public void testAbsentJsonElementsAreSetToNull() { public static class ClassWithInitializedMembers { // Using a mix of no-args constructor and field initializers - // Also, some fields are intialized and some are not (so initialized per JVM spec) + // Also, some fields are initialized and some are not (so initialized per JVM spec) public static final String MY_STRING_DEFAULT = "string"; private static final int MY_INT_DEFAULT = 2; private static final boolean MY_BOOLEAN_DEFAULT = true; int[] array; - String str1, str2; + String str1; + String str2; int int1 = MY_INT_DEFAULT; int int2; boolean bool1 = MY_BOOLEAN_DEFAULT; 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 59b05f0345..d0f7e38d76 100644 --- a/gson/src/test/java/com/google/gson/functional/ObjectTest.java +++ b/gson/src/test/java/com/google/gson/functional/ObjectTest.java @@ -736,10 +736,12 @@ public void testStaticFieldDeserialization() { } } + @SuppressWarnings({"PrivateConstructorForUtilityClass", "NonFinalStaticField"}) static class ClassWithStaticField { static String s = "initial"; } + @SuppressWarnings("PrivateConstructorForUtilityClass") static class ClassWithStaticFinalField { static final String s = "initial"; } diff --git a/gson/src/test/java/com/google/gson/functional/PrettyPrintingTest.java b/gson/src/test/java/com/google/gson/functional/PrettyPrintingTest.java index 6f3def68ea..1671d50194 100644 --- a/gson/src/test/java/com/google/gson/functional/PrettyPrintingTest.java +++ b/gson/src/test/java/com/google/gson/functional/PrettyPrintingTest.java @@ -39,8 +39,6 @@ */ public class PrettyPrintingTest { - private static final boolean DEBUG = false; - private Gson gson; @Before @@ -52,31 +50,75 @@ public void setUp() throws Exception { public void testPrettyPrintList() { BagOfPrimitives b = new BagOfPrimitives(); List listOfB = new ArrayList<>(); - for (int i = 0; i < 15; ++i) { + for (int i = 0; i < 3; ++i) { listOfB.add(b); } Type typeOfSrc = new TypeToken>() {}.getType(); String json = gson.toJson(listOfB, typeOfSrc); - print(json); + assertThat(json) + .isEqualTo( + "[\n" + + " {\n" + + " \"longValue\": 0,\n" + + " \"intValue\": 0,\n" + + " \"booleanValue\": false,\n" + + " \"stringValue\": \"\"\n" + + " },\n" + + " {\n" + + " \"longValue\": 0,\n" + + " \"intValue\": 0,\n" + + " \"booleanValue\": false,\n" + + " \"stringValue\": \"\"\n" + + " },\n" + + " {\n" + + " \"longValue\": 0,\n" + + " \"intValue\": 0,\n" + + " \"booleanValue\": false,\n" + + " \"stringValue\": \"\"\n" + + " }\n" + + "]"); } @Test public void testPrettyPrintArrayOfObjects() { ArrayOfObjects target = new ArrayOfObjects(); String json = gson.toJson(target); - print(json); + assertThat(json) + .isEqualTo( + "{\n" + + " \"elements\": [\n" + + " {\n" + + " \"longValue\": 0,\n" + + " \"intValue\": 2,\n" + + " \"booleanValue\": false,\n" + + " \"stringValue\": \"i0\"\n" + + " },\n" + + " {\n" + + " \"longValue\": 1,\n" + + " \"intValue\": 3,\n" + + " \"booleanValue\": false,\n" + + " \"stringValue\": \"i1\"\n" + + " },\n" + + " {\n" + + " \"longValue\": 2,\n" + + " \"intValue\": 4,\n" + + " \"booleanValue\": false,\n" + + " \"stringValue\": \"i2\"\n" + + " }\n" + + " ]\n" + + "}"); } @Test public void testPrettyPrintArrayOfPrimitives() { - int[] ints = new int[] {1, 2, 3, 4, 5}; + int[] ints = {1, 2, 3, 4, 5}; String json = gson.toJson(ints); assertThat(json).isEqualTo("[\n 1,\n 2,\n 3,\n 4,\n 5\n]"); } @Test public void testPrettyPrintArrayOfPrimitiveArrays() { - int[][] ints = new int[][] {{1, 2}, {3, 4}, {5, 6}, {7, 8}, {9, 0}, {10}}; + int[][] ints = {{1, 2}, {3, 4}, {5, 6}, {7, 8}, {9, 0}, {10}}; String json = gson.toJson(ints); assertThat(json) .isEqualTo( @@ -121,14 +163,8 @@ private static class ClassWithMap { @Test public void testMultipleArrays() { - int[][][] ints = new int[][][] {{{1}, {2}}}; + int[][][] ints = {{{1}, {2}}}; String json = gson.toJson(ints); assertThat(json).isEqualTo("[\n [\n [\n 1\n ],\n [\n 2\n ]\n ]\n]"); } - - private void print(String msg) { - if (DEBUG) { - System.out.println(msg); - } - } } diff --git a/gson/src/test/java/com/google/gson/functional/PrimitiveTest.java b/gson/src/test/java/com/google/gson/functional/PrimitiveTest.java index 175b2809ed..2c51f09750 100644 --- a/gson/src/test/java/com/google/gson/functional/PrimitiveTest.java +++ b/gson/src/test/java/com/google/gson/functional/PrimitiveTest.java @@ -526,7 +526,7 @@ public void testMoreSpecificSerialization() { assertThat(actualJson).isNotEqualTo(expectedJson); } - private String extractElementFromArray(String json) { + private static String extractElementFromArray(String json) { return json.substring(json.indexOf('[') + 1, json.indexOf(']')); } diff --git a/gson/src/test/java/com/google/gson/functional/ReflectionAccessFilterTest.java b/gson/src/test/java/com/google/gson/functional/ReflectionAccessFilterTest.java index a3ea4340b9..ba06a190d2 100644 --- a/gson/src/test/java/com/google/gson/functional/ReflectionAccessFilterTest.java +++ b/gson/src/test/java/com/google/gson/functional/ReflectionAccessFilterTest.java @@ -152,7 +152,7 @@ public void testBlockAllJavaExtendingJdkClass() { } private static class ClassWithStaticField { - @SuppressWarnings("unused") + @SuppressWarnings({"unused", "NonFinalStaticField"}) private static int i = 1; } diff --git a/gson/src/test/java/com/google/gson/functional/TypeAdapterPrecedenceTest.java b/gson/src/test/java/com/google/gson/functional/TypeAdapterPrecedenceTest.java index fc51836f3e..0252ec5052 100644 --- a/gson/src/test/java/com/google/gson/functional/TypeAdapterPrecedenceTest.java +++ b/gson/src/test/java/com/google/gson/functional/TypeAdapterPrecedenceTest.java @@ -139,7 +139,7 @@ private Foo(String name) { } } - private JsonSerializer newSerializer(final String name) { + private static JsonSerializer newSerializer(final String name) { return new JsonSerializer() { @Override public JsonElement serialize(Foo src, Type typeOfSrc, JsonSerializationContext context) { @@ -148,7 +148,7 @@ public JsonElement serialize(Foo src, Type typeOfSrc, JsonSerializationContext c }; } - private JsonDeserializer newDeserializer(final String name) { + private static JsonDeserializer newDeserializer(final String name) { return new JsonDeserializer() { @Override public Foo deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) { @@ -157,7 +157,7 @@ public Foo deserialize(JsonElement json, Type typeOfT, JsonDeserializationContex }; } - private TypeAdapter newTypeAdapter(final String name) { + private static TypeAdapter newTypeAdapter(final String name) { return new TypeAdapter() { @Override public Foo read(JsonReader in) throws IOException { diff --git a/gson/src/test/java/com/google/gson/functional/VersioningTest.java b/gson/src/test/java/com/google/gson/functional/VersioningTest.java index bfaca22286..698cf60ddd 100644 --- a/gson/src/test/java/com/google/gson/functional/VersioningTest.java +++ b/gson/src/test/java/com/google/gson/functional/VersioningTest.java @@ -85,9 +85,12 @@ public void testVersionedClassesSerialization() { public void testVersionedClassesDeserialization() { Gson gson = gsonWithVersion(1.0); String json = "{\"a\":3,\"b\":4,\"c\":5}"; + Version1 version1 = gson.fromJson(json, Version1.class); assertThat(version1.a).isEqualTo(3); assertThat(version1.b).isEqualTo(4); + + @SuppressWarnings("MemberName") Version1_1 version1_1 = gson.fromJson(json, Version1_1.class); assertThat(version1_1.a).isEqualTo(3); assertThat(version1_1.b).isEqualTo(4); @@ -104,6 +107,8 @@ public void testIgnoreLaterVersionClassSerialization() { public void testIgnoreLaterVersionClassDeserialization() { Gson gson = gsonWithVersion(1.0); String json = "{\"a\":3,\"b\":4,\"c\":5,\"d\":6}"; + + @SuppressWarnings("MemberName") Version1_2 version1_2 = gson.fromJson(json, Version1_2.class); // Since the class is versioned to be after 1.0, we expect null // This is the new behavior in Gson 2.0 diff --git a/gson/src/test/java/com/google/gson/internal/GsonTypesTest.java b/gson/src/test/java/com/google/gson/internal/GsonTypesTest.java index 8c133a7888..1e9d613e4a 100644 --- a/gson/src/test/java/com/google/gson/internal/GsonTypesTest.java +++ b/gson/src/test/java/com/google/gson/internal/GsonTypesTest.java @@ -24,6 +24,7 @@ import java.util.List; import org.junit.Test; +@SuppressWarnings("ClassNamedLikeTypeParameter") // for dummy classes A, B, ... public final class GsonTypesTest { @Test @@ -87,10 +88,14 @@ private final class NonStaticInner {} * type, returns null. */ public static Type getFirstTypeArgument(Type type) throws Exception { - if (!(type instanceof ParameterizedType)) return null; + if (!(type instanceof ParameterizedType)) { + return null; + } ParameterizedType ptype = (ParameterizedType) type; Type[] actualTypeArguments = ptype.getActualTypeArguments(); - if (actualTypeArguments.length == 0) return null; + if (actualTypeArguments.length == 0) { + return null; + } return $Gson$Types.canonicalize(actualTypeArguments[0]); } } diff --git a/gson/src/test/java/com/google/gson/internal/JavaVersionTest.java b/gson/src/test/java/com/google/gson/internal/JavaVersionTest.java index 44a6dc85b7..28369ffe54 100644 --- a/gson/src/test/java/com/google/gson/internal/JavaVersionTest.java +++ b/gson/src/test/java/com/google/gson/internal/JavaVersionTest.java @@ -37,49 +37,49 @@ public void testGetMajorJavaVersion() { @Test public void testJava6() { // http://www.oracle.com/technetwork/java/javase/version-6-141920.html - assertThat(JavaVersion.getMajorJavaVersion("1.6.0")).isEqualTo(6); + assertThat(JavaVersion.parseMajorJavaVersion("1.6.0")).isEqualTo(6); } @Test public void testJava7() { // http://www.oracle.com/technetwork/java/javase/jdk7-naming-418744.html - assertThat(JavaVersion.getMajorJavaVersion("1.7.0")).isEqualTo(7); + assertThat(JavaVersion.parseMajorJavaVersion("1.7.0")).isEqualTo(7); } @Test public void testJava8() { - assertThat(JavaVersion.getMajorJavaVersion("1.8")).isEqualTo(8); - assertThat(JavaVersion.getMajorJavaVersion("1.8.0")).isEqualTo(8); - assertThat(JavaVersion.getMajorJavaVersion("1.8.0_131")).isEqualTo(8); - assertThat(JavaVersion.getMajorJavaVersion("1.8.0_60-ea")).isEqualTo(8); - assertThat(JavaVersion.getMajorJavaVersion("1.8.0_111-internal")).isEqualTo(8); + assertThat(JavaVersion.parseMajorJavaVersion("1.8")).isEqualTo(8); + assertThat(JavaVersion.parseMajorJavaVersion("1.8.0")).isEqualTo(8); + assertThat(JavaVersion.parseMajorJavaVersion("1.8.0_131")).isEqualTo(8); + assertThat(JavaVersion.parseMajorJavaVersion("1.8.0_60-ea")).isEqualTo(8); + assertThat(JavaVersion.parseMajorJavaVersion("1.8.0_111-internal")).isEqualTo(8); // openjdk8 per https://github.com/AdoptOpenJDK/openjdk-build/issues/93 - assertThat(JavaVersion.getMajorJavaVersion("1.8.0-internal")).isEqualTo(8); - assertThat(JavaVersion.getMajorJavaVersion("1.8.0_131-adoptopenjdk")).isEqualTo(8); + assertThat(JavaVersion.parseMajorJavaVersion("1.8.0-internal")).isEqualTo(8); + assertThat(JavaVersion.parseMajorJavaVersion("1.8.0_131-adoptopenjdk")).isEqualTo(8); } @Test public void testJava9() { // Legacy style - assertThat(JavaVersion.getMajorJavaVersion("9.0.4")).isEqualTo(9); // Oracle JDK 9 + assertThat(JavaVersion.parseMajorJavaVersion("9.0.4")).isEqualTo(9); // Oracle JDK 9 // Debian as reported in https://github.com/google/gson/issues/1310 - assertThat(JavaVersion.getMajorJavaVersion("9-Debian")).isEqualTo(9); + assertThat(JavaVersion.parseMajorJavaVersion("9-Debian")).isEqualTo(9); // New style - assertThat(JavaVersion.getMajorJavaVersion("9-ea+19")).isEqualTo(9); - assertThat(JavaVersion.getMajorJavaVersion("9+100")).isEqualTo(9); - assertThat(JavaVersion.getMajorJavaVersion("9.0.1+20")).isEqualTo(9); - assertThat(JavaVersion.getMajorJavaVersion("9.1.1+20")).isEqualTo(9); + assertThat(JavaVersion.parseMajorJavaVersion("9-ea+19")).isEqualTo(9); + assertThat(JavaVersion.parseMajorJavaVersion("9+100")).isEqualTo(9); + assertThat(JavaVersion.parseMajorJavaVersion("9.0.1+20")).isEqualTo(9); + assertThat(JavaVersion.parseMajorJavaVersion("9.1.1+20")).isEqualTo(9); } @Test public void testJava10() { - assertThat(JavaVersion.getMajorJavaVersion("10.0.1")).isEqualTo(10); // Oracle JDK 10.0.1 + assertThat(JavaVersion.parseMajorJavaVersion("10.0.1")).isEqualTo(10); // Oracle JDK 10.0.1 } @Test public void testUnknownVersionFormat() { - assertThat(JavaVersion.getMajorJavaVersion("Java9")).isEqualTo(6); // unknown format + assertThat(JavaVersion.parseMajorJavaVersion("Java9")).isEqualTo(6); // unknown format } } diff --git a/gson/src/test/java/com/google/gson/internal/LinkedTreeMapTest.java b/gson/src/test/java/com/google/gson/internal/LinkedTreeMapTest.java index 93f736286c..fd31817c83 100644 --- a/gson/src/test/java/com/google/gson/internal/LinkedTreeMapTest.java +++ b/gson/src/test/java/com/google/gson/internal/LinkedTreeMapTest.java @@ -237,7 +237,7 @@ public void testJavaSerialization() throws IOException, ClassNotFoundException { @SuppressWarnings("varargs") @SafeVarargs - private final void assertIterationOrder(Iterable actual, T... expected) { + private static final void assertIterationOrder(Iterable actual, T... expected) { ArrayList actualList = new ArrayList<>(); for (T t : actual) { actualList.add(t); diff --git a/gson/src/test/java/com/google/gson/internal/bind/DefaultDateTypeAdapterTest.java b/gson/src/test/java/com/google/gson/internal/bind/DefaultDateTypeAdapterTest.java index 1aac71d2c0..fc13a39e90 100644 --- a/gson/src/test/java/com/google/gson/internal/bind/DefaultDateTypeAdapterTest.java +++ b/gson/src/test/java/com/google/gson/internal/bind/DefaultDateTypeAdapterTest.java @@ -21,6 +21,7 @@ import static org.junit.Assert.fail; import com.google.gson.Gson; +import com.google.gson.GsonBuilder; import com.google.gson.TypeAdapter; import com.google.gson.TypeAdapterFactory; import com.google.gson.internal.bind.DefaultDateTypeAdapter.DateType; @@ -51,7 +52,7 @@ public void testFormattingInFr() { assertFormattingAlwaysEmitsUsLocale(Locale.FRANCE); } - private void assertFormattingAlwaysEmitsUsLocale(Locale locale) { + private static void assertFormattingAlwaysEmitsUsLocale(Locale locale) { TimeZone defaultTimeZone = TimeZone.getDefault(); TimeZone.setDefault(TimeZone.getTimeZone("UTC")); Locale defaultLocale = Locale.getDefault(); @@ -223,6 +224,34 @@ public void testUnexpectedToken() throws Exception { } } + @Test + public void testGsonDateFormat() { + TimeZone originalTimeZone = TimeZone.getDefault(); + // Set the default timezone to UTC + TimeZone.setDefault(TimeZone.getTimeZone("UTC")); + try { + Gson gson = new GsonBuilder().setDateFormat("yyyy-MM-dd HH:mm z").create(); + Date originalDate = new Date(0); + + // Serialize the date object + String json = gson.toJson(originalDate); + assertThat(json).isEqualTo("\"1970-01-01 00:00 UTC\""); + + // Deserialize a date string with the PST timezone + Date deserializedDate = gson.fromJson("\"1970-01-01 00:00 PST\"", Date.class); + // Assert that the deserialized date's time is correct + assertThat(deserializedDate.getTime()).isEqualTo(new Date(28800000).getTime()); + + // Serialize the deserialized date object again + String jsonAfterDeserialization = gson.toJson(deserializedDate); + // The expectation is that the date, after deserialization, when serialized again should still + // be in the UTC timezone + assertThat(jsonAfterDeserialization).isEqualTo("\"1970-01-01 08:00 UTC\""); + } finally { + TimeZone.setDefault(originalTimeZone); + } + } + private static TypeAdapter dateAdapter(TypeAdapterFactory adapterFactory) { TypeAdapter adapter = adapterFactory.create(new Gson(), TypeToken.get(Date.class)); assertThat(adapter).isNotNull(); diff --git a/gson/src/test/java/com/google/gson/internal/bind/util/ISO8601UtilsTest.java b/gson/src/test/java/com/google/gson/internal/bind/util/ISO8601UtilsTest.java index 6fcfd153a5..7f2b3b20fa 100644 --- a/gson/src/test/java/com/google/gson/internal/bind/util/ISO8601UtilsTest.java +++ b/gson/src/test/java/com/google/gson/internal/bind/util/ISO8601UtilsTest.java @@ -18,7 +18,6 @@ import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.assertThrows; -import static org.junit.Assert.fail; import java.text.ParseException; import java.text.ParsePosition; @@ -52,7 +51,7 @@ public void testDateFormatString() { Date date = calendar.getTime(); String dateStr = ISO8601Utils.format(date); String expectedDate = "2018-06-25"; - assertThat(dateStr.substring(0, expectedDate.length())).isEqualTo(expectedDate); + assertThat(dateStr).startsWith(expectedDate); } @Test @@ -87,21 +86,13 @@ public void testDateParseWithDefaultTimezone() throws ParseException { @Test public void testDateParseInvalidDay() { String dateStr = "2022-12-33"; - try { - ISO8601Utils.parse(dateStr, new ParsePosition(0)); - fail("Expected parsing to fail"); - } catch (ParseException expected) { - } + assertThrows(ParseException.class, () -> ISO8601Utils.parse(dateStr, new ParsePosition(0))); } @Test public void testDateParseInvalidMonth() { String dateStr = "2022-14-30"; - try { - ISO8601Utils.parse(dateStr, new ParsePosition(0)); - fail("Expected parsing to fail"); - } catch (ParseException expected) { - } + assertThrows(ParseException.class, () -> ISO8601Utils.parse(dateStr, new ParsePosition(0))); } @Test diff --git a/gson/src/test/java/com/google/gson/internal/sql/SqlTypesGsonTest.java b/gson/src/test/java/com/google/gson/internal/sql/SqlTypesGsonTest.java index afc6373698..883b530a03 100644 --- a/gson/src/test/java/com/google/gson/internal/sql/SqlTypesGsonTest.java +++ b/gson/src/test/java/com/google/gson/internal/sql/SqlTypesGsonTest.java @@ -21,7 +21,6 @@ import com.google.gson.Gson; import com.google.gson.GsonBuilder; import com.google.gson.functional.DefaultTypeAdaptersTest; -import java.sql.Date; import java.sql.Time; import java.sql.Timestamp; import java.util.Locale; @@ -30,6 +29,8 @@ import org.junit.Before; import org.junit.Test; +// Suppression for `java.sql.Date` to make it explicit that this is not `java.util.Date` +@SuppressWarnings("UnnecessarilyFullyQualified") public class SqlTypesGsonTest { private Gson gson; private TimeZone oldTimeZone; @@ -52,7 +53,7 @@ public void tearDown() throws Exception { @Test public void testNullSerializationAndDeserialization() { - testNullSerializationAndDeserialization(Date.class); + testNullSerializationAndDeserialization(java.sql.Date.class); testNullSerializationAndDeserialization(Time.class); testNullSerializationAndDeserialization(Timestamp.class); } diff --git a/gson/src/test/java/com/google/gson/metrics/PerformanceTest.java b/gson/src/test/java/com/google/gson/metrics/PerformanceTest.java index f54c70f35a..2e2add3050 100644 --- a/gson/src/test/java/com/google/gson/metrics/PerformanceTest.java +++ b/gson/src/test/java/com/google/gson/metrics/PerformanceTest.java @@ -39,6 +39,7 @@ * @author Inderjeet Singh * @author Joel Leitch */ +@SuppressWarnings("SystemOut") // allow System.out because test is for manual execution anyway public class PerformanceTest { private static final int COLLECTION_SIZE = 5000; @@ -292,7 +293,7 @@ public void testLargeGsonMapRoundTrip() throws Exception { Map unused = gson.fromJson(json, longToLong); } - private String buildJsonForClassWithList() { + private static String buildJsonForClassWithList() { StringBuilder sb = new StringBuilder("{"); sb.append("field:").append("'str',"); sb.append("list:["); @@ -301,12 +302,12 @@ private String buildJsonForClassWithList() { if (first) { first = false; } else { - sb.append(","); + sb.append(','); } sb.append("{field:'element-" + i + "'}"); } - sb.append("]"); - sb.append("}"); + sb.append(']'); + sb.append('}'); String json = sb.toString(); return json; } diff --git a/gson/src/test/java/com/google/gson/reflect/TypeTokenTest.java b/gson/src/test/java/com/google/gson/reflect/TypeTokenTest.java index aa1e2b6b20..e0277a406f 100644 --- a/gson/src/test/java/com/google/gson/reflect/TypeTokenTest.java +++ b/gson/src/test/java/com/google/gson/reflect/TypeTokenTest.java @@ -37,6 +37,9 @@ * * @author Jesse Wilson */ +// Suppress because these classes are only needed for this test, but must be top-level classes +// to not have an enclosing type +@SuppressWarnings("MultipleTopLevelClasses") public final class TypeTokenTest { // These fields are accessed using reflection by the tests below List listOfInteger = null; diff --git a/gson/src/test/java/com/google/gson/stream/JsonReaderTest.java b/gson/src/test/java/com/google/gson/stream/JsonReaderTest.java index f6629669c6..45c14920ef 100644 --- a/gson/src/test/java/com/google/gson/stream/JsonReaderTest.java +++ b/gson/src/test/java/com/google/gson/stream/JsonReaderTest.java @@ -884,7 +884,7 @@ public void testMalformedNumbers() throws IOException { assertNotANumber("-.0e1"); } - private void assertNotANumber(String s) throws IOException { + private static void assertNotANumber(String s) throws IOException { JsonReader reader = new JsonReader(reader(s)); reader.setStrictness(Strictness.LENIENT); assertThat(reader.peek()).isEqualTo(JsonToken.STRING); @@ -1858,6 +1858,7 @@ public void testBomForbiddenAsOtherCharacterInDocument() throws IOException { } } + @SuppressWarnings("UngroupedOverloads") @Test public void testFailWithPosition() throws IOException { testFailWithPosition("Expected value at line 6 column 5 path $[1]", "[\n\n\n\n\n\"a\",}]"); @@ -1909,7 +1910,7 @@ public void testFailWithPositionIsOffsetByBom() throws IOException { testFailWithPosition("Expected value at line 1 column 6 path $[1]", "\ufeff[\"a\",}]"); } - private void testFailWithPosition(String message, String json) throws IOException { + private static void testFailWithPosition(String message, String json) throws IOException { // Validate that it works reading the string normally. JsonReader reader1 = new JsonReader(reader(json)); reader1.setStrictness(Strictness.LENIENT); @@ -2264,7 +2265,7 @@ public void testLenientExtraCommasInMaps() throws IOException { } } - private String repeat(char c, int count) { + private static String repeat(char c, int count) { char[] array = new char[count]; Arrays.fill(array, c); return new String(array); @@ -2382,7 +2383,7 @@ private static void assertUnexpectedStructureError( + troubleshootingId); } - private void assertDocument(String document, Object... expectations) throws IOException { + private static void assertDocument(String document, Object... expectations) throws IOException { JsonReader reader = new JsonReader(reader(document)); reader.setStrictness(Strictness.LENIENT); for (Object expectation : expectations) { @@ -2419,7 +2420,7 @@ private void assertDocument(String document, Object... expectations) throws IOEx } /** Returns a reader that returns one character at a time. */ - private Reader reader(final String s) { + private static Reader reader(final String s) { /* if (true) */ return new StringReader(s); /* return new Reader() { int position = 0; diff --git a/metrics/pom.xml b/metrics/pom.xml index fed952242d..a31e6a5116 100644 --- a/metrics/pom.xml +++ b/metrics/pom.xml @@ -54,7 +54,7 @@ com.fasterxml.jackson.core jackson-databind - 2.16.0 + 2.16.1 com.google.caliper diff --git a/metrics/src/main/java/com/google/gson/metrics/NonUploadingCaliperRunner.java b/metrics/src/main/java/com/google/gson/metrics/NonUploadingCaliperRunner.java index 69acaed9ff..57a2a6a18b 100644 --- a/metrics/src/main/java/com/google/gson/metrics/NonUploadingCaliperRunner.java +++ b/metrics/src/main/java/com/google/gson/metrics/NonUploadingCaliperRunner.java @@ -19,6 +19,8 @@ import com.google.caliper.runner.CaliperMain; class NonUploadingCaliperRunner { + private NonUploadingCaliperRunner() {} + private static String[] concat(String first, String... others) { if (others.length == 0) { return new String[] {first}; diff --git a/metrics/src/main/java/com/google/gson/metrics/ParseBenchmark.java b/metrics/src/main/java/com/google/gson/metrics/ParseBenchmark.java index ec8c3ffc68..4151b2abb5 100644 --- a/metrics/src/main/java/com/google/gson/metrics/ParseBenchmark.java +++ b/metrics/src/main/java/com/google/gson/metrics/ParseBenchmark.java @@ -32,6 +32,7 @@ import com.google.gson.JsonParser; import com.google.gson.annotations.SerializedName; import com.google.gson.reflect.TypeToken; +import com.google.gson.stream.JsonReader; import java.io.CharArrayReader; import java.io.File; import java.io.IOException; @@ -173,13 +174,12 @@ interface Parser { private static class GsonStreamParser implements Parser { @Override public void parse(char[] data, Document document) throws Exception { - com.google.gson.stream.JsonReader jsonReader = - new com.google.gson.stream.JsonReader(new CharArrayReader(data)); + JsonReader jsonReader = new JsonReader(new CharArrayReader(data)); readToken(jsonReader); jsonReader.close(); } - private void readToken(com.google.gson.stream.JsonReader reader) throws IOException { + private static void readToken(JsonReader reader) throws IOException { while (true) { switch (reader.peek()) { case BEGIN_ARRAY: @@ -211,8 +211,6 @@ private void readToken(com.google.gson.stream.JsonReader reader) throws IOExcept break; case END_DOCUMENT: return; - default: - throw new IllegalArgumentException("Unexpected token" + reader.peek()); } } } @@ -221,8 +219,7 @@ private void readToken(com.google.gson.stream.JsonReader reader) throws IOExcept private static class GsonSkipParser implements Parser { @Override public void parse(char[] data, Document document) throws Exception { - com.google.gson.stream.JsonReader jsonReader = - new com.google.gson.stream.JsonReader(new CharArrayReader(data)); + JsonReader jsonReader = new JsonReader(new CharArrayReader(data)); jsonReader.skipValue(); jsonReader.close(); } @@ -309,6 +306,7 @@ public void parse(char[] data, Document document) throws Exception { } } + @SuppressWarnings("MemberName") static class Tweet { @JsonProperty String coordinates; @JsonProperty boolean favorited; @@ -332,6 +330,7 @@ static class Tweet { @JsonProperty String in_reply_to_user_id_str; } + @SuppressWarnings("MemberName") static class User { @JsonProperty String name; @JsonProperty String profile_sidebar_border_color; diff --git a/pom.xml b/pom.xml index a3e158638a..16c89a329d 100644 --- a/pom.xml +++ b/pom.xml @@ -93,7 +93,7 @@ com.google.truth truth - 1.1.5 + 1.3.0 @@ -134,7 +134,7 @@ com.diffplug.spotless spotless-maven-plugin - 2.41.0 + 2.43.0 @@ -173,8 +173,6 @@ so formatting will fail if build is executed with JDK 11 --> src/test/java/com/google/gson/functional/Java17RecordTest.java src/test/java/com/google/gson/native_test/Java17RecordReflectionTest.java - - target/generated-test-sources/protobuf/**/*.java @@ -215,7 +213,7 @@ org.apache.maven.plugins maven-compiler-plugin - 3.11.0 + 3.12.1 true true @@ -226,6 +224,66 @@ -Xplugin:ErrorProne -XepExcludedPaths:.*/generated-test-sources/protobuf/.* -Xep:NotJavadoc:OFF + + -XepAllSuggestionsAsWarnings + + -Xep:AnnotationPosition + -Xep:AssertFalse + -Xep:ClassName + -Xep:ClassNamedLikeTypeParameter:WARN + -Xep:ComparisonContractViolated + -Xep:ConstantField:WARN + -Xep:DepAnn + -Xep:DifferentNameButSame + -Xep:EmptyIf + -Xep:EqualsBrokenForNull + -Xep:ForEachIterable:WARN + -Xep:FunctionalInterfaceClash + -Xep:InitializeInline + -Xep:InterfaceWithOnlyStatics + -Xep:LambdaFunctionalInterface:WARN + -Xep:LongLiteralLowerCaseSuffix + -Xep:MemberName + -Xep:MissingBraces:WARN + -Xep:MissingDefault + -Xep:MixedArrayDimensions:WARN + -Xep:MultiVariableDeclaration:WARN + -Xep:MultipleTopLevelClasses:WARN + -Xep:NonCanonicalStaticMemberImport + -Xep:NonFinalStaticField + -Xep:PackageLocation:WARN + -Xep:PrimitiveArrayPassedToVarargsMethod + -Xep:PrivateConstructorForUtilityClass:WARN + -Xep:RemoveUnusedImports:WARN + -Xep:StatementSwitchToExpressionSwitch:OFF + -Xep:StaticQualifiedUsingExpression + -Xep:SwitchDefault:WARN + -Xep:SystemExitOutsideMain + -Xep:SystemOut + -Xep:TestExceptionChecker + -Xep:ThrowSpecificExceptions:OFF + -Xep:TryFailRefactoring:OFF + -Xep:TypeParameterNaming:WARN + -Xep:UnescapedEntity + -Xep:UngroupedOverloads:WARN + -Xep:UnnecessarilyFullyQualified + -Xep:UnnecessarilyUsedValue + -Xep:UnnecessaryAnonymousClass:OFF + -Xep:UnnecessaryBoxedVariable:WARN + -Xep:UnnecessaryDefaultInEnumSwitch + -Xep:UnnecessaryFinal:OFF + -Xep:UnnecessaryStaticImport:WARN + -Xep:UnusedException + -Xep:UrlInSee + -Xep:UseCorrectAssertInTests + -Xep:UseEnumSwitch:WARN + -Xep:WildcardImport:WARN + -Xep:YodaCondition @@ -235,7 +293,7 @@ com.google.errorprone error_prone_core - 2.23.0 + 2.24.1 @@ -243,7 +301,7 @@ org.apache.maven.plugins maven-javadoc-plugin - 3.6.2 + 3.6.3 @@ -271,7 +329,7 @@ org.apache.maven.plugins maven-surefire-plugin - 3.2.2 + 3.2.5 org.apache.maven.plugins diff --git a/proto/pom.xml b/proto/pom.xml index 9d1cec2697..99583c399f 100644 --- a/proto/pom.xml +++ b/proto/pom.xml @@ -32,7 +32,7 @@ 2023-01-01T00:00:00Z - 3.25.1 + 3.25.2 @@ -58,7 +58,7 @@ com.google.guava guava - 32.1.3-jre + 33.0.0-jre diff --git a/proto/src/main/java/com/google/gson/protobuf/ProtoTypeAdapter.java b/proto/src/main/java/com/google/gson/protobuf/ProtoTypeAdapter.java index c00a272aef..7b872f1885 100644 --- a/proto/src/main/java/com/google/gson/protobuf/ProtoTypeAdapter.java +++ b/proto/src/main/java/com/google/gson/protobuf/ProtoTypeAdapter.java @@ -39,7 +39,6 @@ import com.google.protobuf.Extension; import com.google.protobuf.Message; import java.lang.reflect.Field; -import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Type; import java.util.ArrayList; @@ -194,8 +193,7 @@ public static Builder newBuilder() { return new Builder(EnumSerialization.NAME, CaseFormat.LOWER_UNDERSCORE, CaseFormat.LOWER_CAMEL); } - private static final com.google.protobuf.Descriptors.FieldDescriptor.Type ENUM_TYPE = - com.google.protobuf.Descriptors.FieldDescriptor.Type.ENUM; + private static final FieldDescriptor.Type ENUM_TYPE = FieldDescriptor.Type.ENUM; private static final ConcurrentMap, Method>> mapOfMapOfMethods = new MapMaker().makeMap(); @@ -263,69 +261,56 @@ public Message deserialize(JsonElement json, Type typeOfT, JsonDeserializationCo throw new IllegalStateException("only generated messages are supported"); } - try { - // Invoke the ProtoClass.newBuilder() method - Message.Builder protoBuilder = - (Message.Builder) getCachedMethod(protoClass, "newBuilder").invoke(null); + // Invoke the ProtoClass.newBuilder() method + Message.Builder protoBuilder = + (Message.Builder) getCachedMethod(protoClass, "newBuilder").invoke(null); - Message defaultInstance = - (Message) getCachedMethod(protoClass, "getDefaultInstance").invoke(null); + Message defaultInstance = + (Message) getCachedMethod(protoClass, "getDefaultInstance").invoke(null); - Descriptor protoDescriptor = - (Descriptor) getCachedMethod(protoClass, "getDescriptor").invoke(null); - // Call setters on all of the available fields - for (FieldDescriptor fieldDescriptor : protoDescriptor.getFields()) { - String jsonFieldName = - getCustSerializedName(fieldDescriptor.getOptions(), fieldDescriptor.getName()); + Descriptor protoDescriptor = + (Descriptor) getCachedMethod(protoClass, "getDescriptor").invoke(null); + // Call setters on all of the available fields + for (FieldDescriptor fieldDescriptor : protoDescriptor.getFields()) { + String jsonFieldName = + getCustSerializedName(fieldDescriptor.getOptions(), fieldDescriptor.getName()); - JsonElement jsonElement = jsonObject.get(jsonFieldName); - if (jsonElement != null && !jsonElement.isJsonNull()) { - // Do not reuse jsonFieldName here, it might have a custom value - Object fieldValue; - if (fieldDescriptor.getType() == ENUM_TYPE) { - if (jsonElement.isJsonArray()) { - // Handling array - Collection enumCollection = - new ArrayList<>(jsonElement.getAsJsonArray().size()); - for (JsonElement element : jsonElement.getAsJsonArray()) { - enumCollection.add( - findValueByNameAndExtension(fieldDescriptor.getEnumType(), element)); - } - fieldValue = enumCollection; - } else { - // No array, just a plain value - fieldValue = - findValueByNameAndExtension(fieldDescriptor.getEnumType(), jsonElement); + JsonElement jsonElement = jsonObject.get(jsonFieldName); + if (jsonElement != null && !jsonElement.isJsonNull()) { + // Do not reuse jsonFieldName here, it might have a custom value + Object fieldValue; + if (fieldDescriptor.getType() == ENUM_TYPE) { + if (jsonElement.isJsonArray()) { + // Handling array + Collection enumCollection = + new ArrayList<>(jsonElement.getAsJsonArray().size()); + for (JsonElement element : jsonElement.getAsJsonArray()) { + enumCollection.add( + findValueByNameAndExtension(fieldDescriptor.getEnumType(), element)); } - protoBuilder.setField(fieldDescriptor, fieldValue); - } else if (fieldDescriptor.isRepeated()) { - // If the type is an array, then we have to grab the type from the class. - // protobuf java field names are always lower camel case - String protoArrayFieldName = - protoFormat.to(CaseFormat.LOWER_CAMEL, fieldDescriptor.getName()) + "_"; - Field protoArrayField = protoClass.getDeclaredField(protoArrayFieldName); - Type protoArrayFieldType = protoArrayField.getGenericType(); - fieldValue = context.deserialize(jsonElement, protoArrayFieldType); - protoBuilder.setField(fieldDescriptor, fieldValue); + fieldValue = enumCollection; } else { - Object field = defaultInstance.getField(fieldDescriptor); - fieldValue = context.deserialize(jsonElement, field.getClass()); - protoBuilder.setField(fieldDescriptor, fieldValue); + // No array, just a plain value + fieldValue = findValueByNameAndExtension(fieldDescriptor.getEnumType(), jsonElement); } + protoBuilder.setField(fieldDescriptor, fieldValue); + } else if (fieldDescriptor.isRepeated()) { + // If the type is an array, then we have to grab the type from the class. + // protobuf java field names are always lower camel case + String protoArrayFieldName = + protoFormat.to(CaseFormat.LOWER_CAMEL, fieldDescriptor.getName()) + "_"; + Field protoArrayField = protoClass.getDeclaredField(protoArrayFieldName); + Type protoArrayFieldType = protoArrayField.getGenericType(); + fieldValue = context.deserialize(jsonElement, protoArrayFieldType); + protoBuilder.setField(fieldDescriptor, fieldValue); + } else { + Object field = defaultInstance.getField(fieldDescriptor); + fieldValue = context.deserialize(jsonElement, field.getClass()); + protoBuilder.setField(fieldDescriptor, fieldValue); } } - return protoBuilder.build(); - } catch (SecurityException e) { - throw new JsonParseException(e); - } catch (NoSuchMethodException e) { - throw new JsonParseException(e); - } catch (IllegalArgumentException e) { - throw new JsonParseException(e); - } catch (IllegalAccessException e) { - throw new JsonParseException(e); - } catch (InvocationTargetException e) { - throw new JsonParseException(e); } + return protoBuilder.build(); } catch (Exception e) { throw new JsonParseException("Error while parsing proto", e); } diff --git a/shrinker-test/pom.xml b/shrinker-test/pom.xml index 4b789e034a..6e47eefa79 100644 --- a/shrinker-test/pom.xml +++ b/shrinker-test/pom.xml @@ -218,8 +218,7 @@ but it appears that can be ignored --> com.android.tools r8 - - 8.1.72 + 8.2.42 @@ -229,7 +228,7 @@ org.apache.maven.plugins maven-failsafe-plugin - 3.2.2 + 3.2.5 diff --git a/shrinker-test/src/main/java/com/example/GenericClasses.java b/shrinker-test/src/main/java/com/example/GenericClasses.java index cd91149be4..10d8659f18 100644 --- a/shrinker-test/src/main/java/com/example/GenericClasses.java +++ b/shrinker-test/src/main/java/com/example/GenericClasses.java @@ -8,6 +8,8 @@ import java.io.IOException; public class GenericClasses { + private GenericClasses() {} + static class GenericClass { @SerializedName("t") T t; diff --git a/shrinker-test/src/main/java/com/example/Main.java b/shrinker-test/src/main/java/com/example/Main.java index e7bf6bdacd..745200a026 100644 --- a/shrinker-test/src/main/java/com/example/Main.java +++ b/shrinker-test/src/main/java/com/example/Main.java @@ -15,6 +15,8 @@ import java.util.function.Supplier; public class Main { + private Main() {} + /** * Main entrypoint, called by {@code ShrinkingIT.test()}. * diff --git a/shrinker-test/src/main/java/com/example/NoSerializedNameMain.java b/shrinker-test/src/main/java/com/example/NoSerializedNameMain.java index 6b0d3704c7..33aa320a28 100644 --- a/shrinker-test/src/main/java/com/example/NoSerializedNameMain.java +++ b/shrinker-test/src/main/java/com/example/NoSerializedNameMain.java @@ -10,6 +10,8 @@ * therefore not matched by the default {@code gson.pro} rules. */ public class NoSerializedNameMain { + private NoSerializedNameMain() {} + static class TestClassNoArgsConstructor { // Has a no-args default constructor. public String s; diff --git a/shrinker-test/src/main/java/com/example/TestExecutor.java b/shrinker-test/src/main/java/com/example/TestExecutor.java index 09b0c94486..c5f38227db 100644 --- a/shrinker-test/src/main/java/com/example/TestExecutor.java +++ b/shrinker-test/src/main/java/com/example/TestExecutor.java @@ -5,6 +5,8 @@ import java.util.function.Supplier; public class TestExecutor { + private TestExecutor() {} + /** * Helper method for running individual tests. In case of an exception wraps it and includes the * {@code name} of the test to make debugging issues with the obfuscated JARs a bit easier.