From a3907a64e528cc055807d02aa4e4e6132aa07a1e Mon Sep 17 00:00:00 2001 From: Sam Brannen Date: Wed, 5 Jul 2023 16:57:51 +0200 Subject: [PATCH] Support arrays, collections, & maps in ObjectUtils.nullSafeConciseToString() Prior to this commit, there was no explicit support for arrays, collections, and maps in nullSafeConciseToString(). This lead to string representations such as the following, regardless of whether the array, collection, or map was empty. - char[]@1623b78d - java.util.ImmutableCollections$List12@74fe5c40 - java.util.ImmutableCollections$MapN@10e31a9a This commit introduces explicit support for arrays, collections, and maps in nullSafeConciseToString(), which results in the following empty/non-empty string representations. - array: {} / {...} - collection: [] / [...] - map: {} / {...} The reason a string representation of an array uses "{}" instead of "[]" (like in Arrays.toString(...)) is that ObjectUtils.nullSafeToString() already follows that convention, and the implementation of nullSafeConciseToString() aligns with that for the sake of consistency. Closes gh-30811 --- .../org/springframework/util/ObjectUtils.java | 13 +++++ .../util/ObjectUtilsTests.java | 49 +++++++++++++++---- 2 files changed, 52 insertions(+), 10 deletions(-) diff --git a/spring-core/src/main/java/org/springframework/util/ObjectUtils.java b/spring-core/src/main/java/org/springframework/util/ObjectUtils.java index e9cbd6033991..830b8d4e1914 100644 --- a/spring-core/src/main/java/org/springframework/util/ObjectUtils.java +++ b/spring-core/src/main/java/org/springframework/util/ObjectUtils.java @@ -69,6 +69,9 @@ public abstract class ObjectUtils { private static final String EMPTY_ARRAY = ARRAY_START + ARRAY_END; private static final String ARRAY_ELEMENT_SEPARATOR = ", "; private static final Object[] EMPTY_OBJECT_ARRAY = new Object[0]; + private static final String NON_EMPTY_ARRAY = ARRAY_START + "..." + ARRAY_END; + private static final String EMPTY_COLLECTION = "[]"; + private static final String NON_EMPTY_COLLECTION = "[...]"; /** @@ -967,6 +970,16 @@ public static String nullSafeConciseToString(@Nullable Object obj) { return (!optional.isPresent() ? "Optional.empty" : String.format("Optional[%s]", nullSafeConciseToString(optional.get()))); } + if (obj.getClass().isArray()) { + return (Array.getLength(obj) == 0 ? EMPTY_ARRAY : NON_EMPTY_ARRAY); + } + if (obj instanceof Collection) { + return (((Collection) obj).isEmpty() ? EMPTY_COLLECTION : NON_EMPTY_COLLECTION); + } + if (obj instanceof Map) { + // EMPTY_ARRAY and NON_EMPTY_ARRAY are also used for maps. + return (((Map) obj).isEmpty() ? EMPTY_ARRAY : NON_EMPTY_ARRAY); + } if (obj instanceof Class) { return ((Class) obj).getName(); } diff --git a/spring-core/src/test/java/org/springframework/util/ObjectUtilsTests.java b/spring-core/src/test/java/org/springframework/util/ObjectUtilsTests.java index 30b444f99c7e..a4d3b8cee39c 100644 --- a/spring-core/src/test/java/org/springframework/util/ObjectUtilsTests.java +++ b/spring-core/src/test/java/org/springframework/util/ObjectUtilsTests.java @@ -30,7 +30,6 @@ import java.sql.SQLException; import java.time.LocalDate; import java.time.ZoneId; -import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Currency; @@ -39,6 +38,7 @@ import java.util.HashSet; import java.util.List; import java.util.Locale; +import java.util.Map; import java.util.Optional; import java.util.Set; import java.util.TimeZone; @@ -1059,20 +1059,49 @@ void nullSafeConciseToStringForZoneId() { } @Test - void nullSafeConciseToStringForArraysAndCollections() { - List list = Arrays.asList("a", "b", "c"); - assertThat(ObjectUtils.nullSafeConciseToString(new int[][] {{1, 2}, {3, 4}})).startsWith(prefix(int[][].class)); - assertThat(ObjectUtils.nullSafeConciseToString(list.toArray(new Object[0]))).startsWith(prefix(Object[].class)); - assertThat(ObjectUtils.nullSafeConciseToString(list.toArray(new String[0]))).startsWith(prefix(String[].class)); - assertThat(ObjectUtils.nullSafeConciseToString(new ArrayList<>(list))).startsWith(prefix(ArrayList.class)); - assertThat(ObjectUtils.nullSafeConciseToString(new HashSet<>(list))).startsWith(prefix(HashSet.class)); + void nullSafeConciseToStringForEmptyArrays() { + assertThat(ObjectUtils.nullSafeConciseToString(new char[] {})).isEqualTo("{}"); + assertThat(ObjectUtils.nullSafeConciseToString(new int[][] {})).isEqualTo("{}"); + assertThat(ObjectUtils.nullSafeConciseToString(new String[] {})).isEqualTo("{}"); + assertThat(ObjectUtils.nullSafeConciseToString(new Integer[][] {})).isEqualTo("{}"); } @Test - void nullSafeConciseToStringForMaps() { + void nullSafeConciseToStringForNonEmptyArrays() { + assertThat(ObjectUtils.nullSafeConciseToString(new char[] {'a'})).isEqualTo("{...}"); + assertThat(ObjectUtils.nullSafeConciseToString(new int[][] {{1}, {2}})).isEqualTo("{...}"); + assertThat(ObjectUtils.nullSafeConciseToString(new String[] {"enigma"})).isEqualTo("{...}"); + assertThat(ObjectUtils.nullSafeConciseToString(new Integer[][] {{1}, {2}})).isEqualTo("{...}"); + } + + @Test + void nullSafeConciseToStringForEmptyCollections() { + List list = Collections.emptyList(); + Set set = Collections.emptySet(); + assertThat(ObjectUtils.nullSafeConciseToString(list)).isEqualTo("[]"); + assertThat(ObjectUtils.nullSafeConciseToString(set)).isEqualTo("[]"); + } + + @Test + void nullSafeConciseToStringForNonEmptyCollections() { + List list = Arrays.asList("a", "b"); + Set set = new HashSet<>(); + set.add("foo"); + assertThat(ObjectUtils.nullSafeConciseToString(list)).isEqualTo("[...]"); + assertThat(ObjectUtils.nullSafeConciseToString(set)).isEqualTo("[...]"); + } + + @Test + void nullSafeConciseToStringForEmptyMaps() { + Map map = Collections.emptyMap(); + assertThat(ObjectUtils.nullSafeConciseToString(map)).isEqualTo("{}"); + } + + @Test + void nullSafeConciseToStringForNonEmptyMaps() { HashMap map = new HashMap<>(); map.put("foo", 42L); - assertThat(ObjectUtils.nullSafeConciseToString(map)).startsWith(prefix(map.getClass())); + assertThat(ObjectUtils.nullSafeConciseToString(map)).isEqualTo("{...}"); } @Test