From 3530e874b1d5111d2eeeee6573b5846f781c2e7c Mon Sep 17 00:00:00 2001 From: Marcono1234 Date: Sat, 18 Nov 2023 23:09:25 +0100 Subject: [PATCH] Validate `GsonBuilder.setDateFormat` style arguments & extend tests (#2545) `DateFormat` validates these arguments as well, but it is currently not documented, see https://bugs.openjdk.org/browse/JDK-8319628. Also moves the other tests for `setDateFormat(String)` from DefaultTypeAdaptersTest to GsonBuilderTest. --- .../java/com/google/gson/GsonBuilder.java | 20 ++++- .../java/com/google/gson/GsonBuilderTest.java | 86 +++++++++++++++++++ .../functional/DefaultTypeAdaptersTest.java | 32 +++---- 3 files changed, 115 insertions(+), 23 deletions(-) diff --git a/gson/src/main/java/com/google/gson/GsonBuilder.java b/gson/src/main/java/com/google/gson/GsonBuilder.java index 7ebdad4e30..f787136d92 100644 --- a/gson/src/main/java/com/google/gson/GsonBuilder.java +++ b/gson/src/main/java/com/google/gson/GsonBuilder.java @@ -593,8 +593,10 @@ public GsonBuilder disableHtmlEscaping() { * class. See the documentation in {@link java.text.SimpleDateFormat} for more information on * valid date and time patterns. * - * @param pattern the pattern that dates will be serialized/deserialized to/from + * @param pattern the pattern that dates will be serialized/deserialized to/from; can be {@code + * null} to reset the pattern * @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern + * @throws IllegalArgumentException if the pattern is invalid * @since 1.2 */ @CanIgnoreReturnValue @@ -611,6 +613,14 @@ 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 style value provided. You * can call this method or {@link #setDateFormat(String)} multiple times, but only the last @@ -623,11 +633,12 @@ public GsonBuilder setDateFormat(String pattern) { * @param style the predefined date style that date objects will be serialized/deserialized * to/from * @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern + * @throws IllegalArgumentException if the style is invalid * @since 1.2 */ @CanIgnoreReturnValue public GsonBuilder setDateFormat(int style) { - this.dateStyle = style; + this.dateStyle = checkDateFormatStyle(style); this.datePattern = null; return this; } @@ -645,12 +656,13 @@ public GsonBuilder setDateFormat(int style) { * to/from * @param timeStyle the predefined style for the time portion of the date objects * @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern + * @throws IllegalArgumentException if the style values are invalid * @since 1.2 */ @CanIgnoreReturnValue public GsonBuilder setDateFormat(int dateStyle, int timeStyle) { - this.dateStyle = dateStyle; - this.timeStyle = timeStyle; + this.dateStyle = checkDateFormatStyle(dateStyle); + this.timeStyle = checkDateFormatStyle(timeStyle); this.datePattern = null; return this; } diff --git a/gson/src/test/java/com/google/gson/GsonBuilderTest.java b/gson/src/test/java/com/google/gson/GsonBuilderTest.java index a5f826db26..202c672914 100644 --- a/gson/src/test/java/com/google/gson/GsonBuilderTest.java +++ b/gson/src/test/java/com/google/gson/GsonBuilderTest.java @@ -28,6 +28,8 @@ import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.lang.reflect.Type; +import java.text.DateFormat; +import java.util.Date; import org.junit.Test; /** @@ -305,4 +307,88 @@ public void testRegisterTypeHierarchyAdapterJsonElements() { // But registering type hierarchy adapter for Object should be allowed gsonBuilder.registerTypeHierarchyAdapter(Object.class, NULL_TYPE_ADAPTER); } + + @Test + public void testSetDateFormatWithInvalidPattern() { + GsonBuilder builder = new GsonBuilder(); + String invalidPattern = "This is an invalid Pattern"; + IllegalArgumentException e = + assertThrows(IllegalArgumentException.class, () -> builder.setDateFormat(invalidPattern)); + assertThat(e) + .hasMessageThat() + .isEqualTo("The date pattern '" + invalidPattern + "' is not valid"); + } + + @Test + public void testSetDateFormatWithValidPattern() { + GsonBuilder builder = new GsonBuilder(); + String validPattern = "yyyy-MM-dd"; + // Should not throw an exception + builder.setDateFormat(validPattern); + } + + @Test + public void testSetDateFormatNullPattern() { + GsonBuilder builder = new GsonBuilder(); + @SuppressWarnings("JavaUtilDate") + Date date = new Date(0); + String originalFormatted = builder.create().toJson(date); + + String customFormatted = builder.setDateFormat("yyyy-MM-dd").create().toJson(date); + assertThat(customFormatted).isNotEqualTo(originalFormatted); + + // `null` should reset the format to the default + String resetFormatted = builder.setDateFormat(null).create().toJson(date); + assertThat(resetFormatted).isEqualTo(originalFormatted); + } + + /** + * Tests behavior for an empty date pattern; this behavior is not publicly documented at the + * moment. + */ + @Test + public void testSetDateFormatEmptyPattern() { + GsonBuilder builder = new GsonBuilder(); + @SuppressWarnings("JavaUtilDate") + Date date = new Date(0); + String originalFormatted = builder.create().toJson(date); + + String emptyFormatted = builder.setDateFormat(" ").create().toJson(date); + // Empty pattern was ignored + assertThat(emptyFormatted).isEqualTo(originalFormatted); + } + + @Test + public void testSetDateFormatValidStyle() { + GsonBuilder builder = new GsonBuilder(); + int[] validStyles = {DateFormat.FULL, DateFormat.LONG, DateFormat.MEDIUM, DateFormat.SHORT}; + + for (int style : validStyles) { + // Should not throw an exception + builder.setDateFormat(style); + builder.setDateFormat(style, style); + } + } + + @Test + public void testSetDateFormatInvalidStyle() { + GsonBuilder builder = new GsonBuilder(); + + IllegalArgumentException e = + assertThrows(IllegalArgumentException.class, () -> builder.setDateFormat(-1)); + assertThat(e).hasMessageThat().isEqualTo("Invalid style: -1"); + + e = assertThrows(IllegalArgumentException.class, () -> builder.setDateFormat(4)); + assertThat(e).hasMessageThat().isEqualTo("Invalid style: 4"); + + e = + assertThrows( + IllegalArgumentException.class, () -> builder.setDateFormat(-1, DateFormat.FULL)); + assertThat(e).hasMessageThat().isEqualTo("Invalid style: -1"); + + e = + assertThrows( + IllegalArgumentException.class, () -> builder.setDateFormat(DateFormat.FULL, -1)); + assertThat(e).hasMessageThat().isEqualTo("Invalid style: -1"); + } } 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 ccef12229c..70d597c3e4 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,6 @@ package com.google.gson.functional; import static com.google.common.truth.Truth.assertThat; -import static org.junit.Assert.assertThrows; import static org.junit.Assert.fail; import com.google.gson.Gson; @@ -493,6 +492,19 @@ public void testDefaultGregorianCalendarDeserialization() { assertThat(cal.get(Calendar.SECOND)).isEqualTo(23); } + @Test + public void testDateSerializationWithStyle() { + int style = DateFormat.SHORT; + Date date = new Date(0); + String expectedFormatted = DateFormat.getDateTimeInstance(style, style, Locale.US).format(date); + + Gson gson = new GsonBuilder().setDateFormat(style, style).create(); + String json = gson.toJson(date); + assertThat(json).isEqualTo("\"" + expectedFormatted + "\""); + // Verify that custom style is not equal to default style + assertThat(json).isNotEqualTo(new Gson().toJson(date)); + } + @Test public void testDateSerializationWithPattern() { String pattern = "yyyy-MM-dd"; @@ -738,24 +750,6 @@ public void testStringBufferDeserialization() { assertThat(sb.toString()).isEqualTo("abc"); } - @Test - public void testSetDateFormatWithInvalidPattern() { - GsonBuilder builder = new GsonBuilder(); - String invalidPattern = "This is a invalid Pattern"; - assertThrows( - IllegalArgumentException.class, - () -> { - builder.setDateFormat(invalidPattern); - }); - } - - @Test - public void testSetDateFormatWithValidPattern() { - GsonBuilder builder = new GsonBuilder(); - String validPattern = "yyyy-MM-dd"; - builder.setDateFormat(validPattern); - } - private static class MyClassTypeAdapter extends TypeAdapter> { @Override public void write(JsonWriter out, Class value) throws IOException {