Skip to content

Commit

Permalink
Validate GsonBuilder.setDateFormat style arguments & extend tests (g…
Browse files Browse the repository at this point in the history
…oogle#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.
  • Loading branch information
Marcono1234 authored and tibor-universe committed Aug 17, 2024
1 parent 342f2d5 commit 3530e87
Show file tree
Hide file tree
Showing 3 changed files with 115 additions and 23 deletions.
20 changes: 16 additions & 4 deletions gson/src/main/java/com/google/gson/GsonBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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;
}
Expand All @@ -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;
}
Expand Down
86 changes: 86 additions & 0 deletions gson/src/test/java/com/google/gson/GsonBuilderTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;

/**
Expand Down Expand Up @@ -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");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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";
Expand Down Expand Up @@ -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<Class<?>> {
@Override
public void write(JsonWriter out, Class<?> value) throws IOException {
Expand Down

0 comments on commit 3530e87

Please sign in to comment.