Extend supported types in ObjectUtils.nullSafeConciseToString()
This commit extends the list of explicitly supported types in ObjectUtils.nullSafeConciseToString() with the following. - Optional - File - Path - InetAddress - Charset - Currency - TimeZone - ZoneId - Pattern Closes gh-30805
This commit is contained in:
parent
08bce69d3d
commit
3ef1b7d83c
|
|
@ -16,18 +16,26 @@
|
|||
|
||||
package org.springframework.util;
|
||||
|
||||
import java.io.File;
|
||||
import java.lang.reflect.Array;
|
||||
import java.net.InetAddress;
|
||||
import java.net.URI;
|
||||
import java.net.URL;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.file.Path;
|
||||
import java.time.ZoneId;
|
||||
import java.time.temporal.Temporal;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Currency;
|
||||
import java.util.Date;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.StringJoiner;
|
||||
import java.util.TimeZone;
|
||||
import java.util.UUID;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
|
|
@ -900,19 +908,27 @@ public abstract class ObjectUtils {
|
|||
* <p>Returns:
|
||||
* <ul>
|
||||
* <li>{@code "null"} if {@code obj} is {@code null}</li>
|
||||
* <li>{@code"Optional.empty"} if {@code obj} is an empty {@link Optional}</li>
|
||||
* <li>{@code"Optional[<concise-string>]"} if {@code obj} is a non-empty {@code Optional},
|
||||
* where {@code <concise-string>} is the result of invoking {@link #nullSafeConciseToString}
|
||||
* on the object contained in the {@code Optional}</li>
|
||||
* <li>{@linkplain Class#getName() Class name} if {@code obj} is a {@link Class}</li>
|
||||
* <li>{@linkplain Charset#name() Charset name} if {@code obj} is a {@link Charset}</li>
|
||||
* <li>{@linkplain TimeZone#getID() TimeZone ID} if {@code obj} is a {@link TimeZone}</li>
|
||||
* <li>{@linkplain ZoneId#getId() Zone ID} if {@code obj} is a {@link ZoneId}</li>
|
||||
* <li>Potentially {@linkplain StringUtils#truncate(CharSequence) truncated string}
|
||||
* if {@code obj} is a {@link String} or {@link CharSequence}</li>
|
||||
* <li>Potentially {@linkplain StringUtils#truncate(CharSequence) truncated string}
|
||||
* if {@code obj} is a <em>simple value type</em> whose {@code toString()} method
|
||||
* returns a non-null value.</li>
|
||||
* returns a non-null value</li>
|
||||
* <li>Otherwise, a string representation of the object's type name concatenated
|
||||
* with {@code @} and a hex string form of the object's identity hash code</li>
|
||||
* with {@code "@"} and a hex string form of the object's identity hash code</li>
|
||||
* </ul>
|
||||
* <p>In the context of this method, a <em>simple value type</em> is any of the following:
|
||||
* a primitive wrapper (excluding {@code Void}), an {@code Enum}, a {@code Number},
|
||||
* a {@code Date}, a {@code Temporal}, a {@code UUID}, a {@code URI}, a {@code URL},
|
||||
* or a {@code Locale}.
|
||||
* primitive wrapper (excluding {@link Void}), {@link Enum}, {@link Number},
|
||||
* {@link Date}, {@link Temporal}, {@link File}, {@link Path}, {@link URI},
|
||||
* {@link URL}, {@link InetAddress}, {@link Currency}, {@link Locale},
|
||||
* {@link UUID}, {@link Pattern}.
|
||||
* @param obj the object to build a string representation for
|
||||
* @return a concise string representation of the supplied object
|
||||
* @since 5.3.27
|
||||
|
|
@ -923,9 +939,22 @@ public abstract class ObjectUtils {
|
|||
if (obj == null) {
|
||||
return "null";
|
||||
}
|
||||
if (obj instanceof Optional<?> optional) {
|
||||
return (optional.isEmpty() ? "Optional.empty" :
|
||||
"Optional[%s]".formatted(nullSafeConciseToString(optional.get())));
|
||||
}
|
||||
if (obj instanceof Class<?> clazz) {
|
||||
return clazz.getName();
|
||||
}
|
||||
if (obj instanceof Charset charset) {
|
||||
return charset.name();
|
||||
}
|
||||
if (obj instanceof TimeZone timeZone) {
|
||||
return timeZone.getID();
|
||||
}
|
||||
if (obj instanceof ZoneId zoneId) {
|
||||
return zoneId.getId();
|
||||
}
|
||||
if (obj instanceof CharSequence charSequence) {
|
||||
return StringUtils.truncate(charSequence);
|
||||
}
|
||||
|
|
@ -941,7 +970,10 @@ public abstract class ObjectUtils {
|
|||
|
||||
/**
|
||||
* Derived from {@link org.springframework.beans.BeanUtils#isSimpleValueType}.
|
||||
* As of 5.3.28, considering {@code UUID} in addition to the bean-level check.
|
||||
* <p>As of 5.3.28, considering {@link UUID} in addition to the bean-level check.
|
||||
* <p>As of 5.3.29, additionally considering {@link File}, {@link Path},
|
||||
* {@link InetAddress}, {@link Charset}, {@link Currency}, {@link TimeZone},
|
||||
* {@link ZoneId}, {@link Pattern}.
|
||||
*/
|
||||
private static boolean isSimpleValueType(Class<?> type) {
|
||||
return (Void.class != type && void.class != type &&
|
||||
|
|
@ -951,10 +983,18 @@ public abstract class ObjectUtils {
|
|||
Number.class.isAssignableFrom(type) ||
|
||||
Date.class.isAssignableFrom(type) ||
|
||||
Temporal.class.isAssignableFrom(type) ||
|
||||
UUID.class == type ||
|
||||
ZoneId.class.isAssignableFrom(type) ||
|
||||
TimeZone.class.isAssignableFrom(type) ||
|
||||
File.class.isAssignableFrom(type) ||
|
||||
Path.class.isAssignableFrom(type) ||
|
||||
Charset.class.isAssignableFrom(type) ||
|
||||
Currency.class.isAssignableFrom(type) ||
|
||||
InetAddress.class.isAssignableFrom(type) ||
|
||||
URI.class == type ||
|
||||
URL.class == type ||
|
||||
UUID.class == type ||
|
||||
Locale.class == type ||
|
||||
Pattern.class == type ||
|
||||
Class.class == type));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -16,23 +16,34 @@
|
|||
|
||||
package org.springframework.util;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.math.BigDecimal;
|
||||
import java.math.BigInteger;
|
||||
import java.net.InetAddress;
|
||||
import java.net.URI;
|
||||
import java.net.URL;
|
||||
import java.net.UnknownHostException;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Path;
|
||||
import java.sql.SQLException;
|
||||
import java.time.LocalDate;
|
||||
import java.time.ZoneId;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Currency;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
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;
|
||||
import java.util.UUID;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.junit.jupiter.api.Nested;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
|
@ -826,6 +837,41 @@ class ObjectUtilsTests {
|
|||
assertThat(ObjectUtils.nullSafeConciseToString(null)).isEqualTo("null");
|
||||
}
|
||||
|
||||
@Test
|
||||
void nullSafeConciseToStringForEmptyOptional() {
|
||||
Optional<String> optional = Optional.empty();
|
||||
assertThat(ObjectUtils.nullSafeConciseToString(optional)).isEqualTo("Optional.empty");
|
||||
}
|
||||
|
||||
@Test
|
||||
void nullSafeConciseToStringForNonEmptyOptionals() {
|
||||
Optional<Tropes> optionalEnum = Optional.of(Tropes.BAR);
|
||||
String expected = "Optional[BAR]";
|
||||
assertThat(ObjectUtils.nullSafeConciseToString(optionalEnum)).isEqualTo(expected);
|
||||
|
||||
String repeat100 = "X".repeat(100);
|
||||
String repeat101 = "X".repeat(101);
|
||||
|
||||
Optional<String> optionalString = Optional.of(repeat100);
|
||||
expected = "Optional[%s]".formatted(repeat100);
|
||||
assertThat(ObjectUtils.nullSafeConciseToString(optionalString)).isEqualTo(expected);
|
||||
|
||||
optionalString = Optional.of(repeat101);
|
||||
expected = "Optional[%s]".formatted(repeat100 + truncated);
|
||||
assertThat(ObjectUtils.nullSafeConciseToString(optionalString)).isEqualTo(expected);
|
||||
}
|
||||
|
||||
@Test
|
||||
void nullSafeConciseToStringForNonEmptyOptionalCustomType() {
|
||||
class CustomType {
|
||||
}
|
||||
|
||||
CustomType customType = new CustomType();
|
||||
Optional<CustomType> optional = Optional.of(customType);
|
||||
String expected = "Optional[%s]".formatted(ObjectUtils.nullSafeConciseToString(customType));
|
||||
assertThat(ObjectUtils.nullSafeConciseToString(optional)).isEqualTo(expected);
|
||||
}
|
||||
|
||||
@Test
|
||||
void nullSafeConciseToStringForClass() {
|
||||
assertThat(ObjectUtils.nullSafeConciseToString(String.class)).isEqualTo("java.lang.String");
|
||||
|
|
@ -889,6 +935,30 @@ class ObjectUtilsTests {
|
|||
assertThat(ObjectUtils.nullSafeConciseToString(id)).isEqualTo(id.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
void nullSafeConciseToStringForFile() {
|
||||
String path = "/tmp/file.txt";
|
||||
assertThat(ObjectUtils.nullSafeConciseToString(new File(path))).isEqualTo(path);
|
||||
|
||||
path = "/tmp/" + "xyz".repeat(32);
|
||||
assertThat(ObjectUtils.nullSafeConciseToString(new File(path)))
|
||||
.hasSize(truncatedLength)
|
||||
.startsWith(path.subSequence(0, 100))
|
||||
.endsWith(truncated);
|
||||
}
|
||||
|
||||
@Test
|
||||
void nullSafeConciseToStringForPath() {
|
||||
String path = "/tmp/file.txt";
|
||||
assertThat(ObjectUtils.nullSafeConciseToString(Path.of(path))).isEqualTo(path);
|
||||
|
||||
path = "/tmp/" + "xyz".repeat(32);
|
||||
assertThat(ObjectUtils.nullSafeConciseToString(Path.of(path)))
|
||||
.hasSize(truncatedLength)
|
||||
.startsWith(path.subSequence(0, 100))
|
||||
.endsWith(truncated);
|
||||
}
|
||||
|
||||
@Test
|
||||
void nullSafeConciseToStringForURI() {
|
||||
String uri = "https://www.example.com/?foo=1&bar=2&baz=3";
|
||||
|
|
@ -913,11 +983,56 @@ class ObjectUtilsTests {
|
|||
.endsWith(truncated);
|
||||
}
|
||||
|
||||
@Test
|
||||
void nullSafeConciseToStringForInetAddress() {
|
||||
InetAddress localhost = getLocalhost();
|
||||
assertThat(ObjectUtils.nullSafeConciseToString(localhost)).isEqualTo(localhost.toString());
|
||||
}
|
||||
|
||||
private static InetAddress getLocalhost() {
|
||||
try {
|
||||
return InetAddress.getLocalHost();
|
||||
}
|
||||
catch (UnknownHostException ex) {
|
||||
return InetAddress.getLoopbackAddress();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void nullSafeConciseToStringForCharset() {
|
||||
Charset charset = StandardCharsets.UTF_8;
|
||||
assertThat(ObjectUtils.nullSafeConciseToString(charset)).isEqualTo(charset.name());
|
||||
}
|
||||
|
||||
@Test
|
||||
void nullSafeConciseToStringForCurrency() {
|
||||
Currency currency = Currency.getInstance(Locale.US);
|
||||
assertThat(ObjectUtils.nullSafeConciseToString(currency)).isEqualTo(currency.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
void nullSafeConciseToStringForLocale() {
|
||||
assertThat(ObjectUtils.nullSafeConciseToString(Locale.GERMANY)).isEqualTo("de_DE");
|
||||
}
|
||||
|
||||
@Test
|
||||
void nullSafeConciseToStringForRegExPattern() {
|
||||
Pattern pattern = Pattern.compile("^(foo|bar)$");
|
||||
assertThat(ObjectUtils.nullSafeConciseToString(pattern)).isEqualTo(pattern.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
void nullSafeConciseToStringForTimeZone() {
|
||||
TimeZone timeZone = TimeZone.getDefault();
|
||||
assertThat(ObjectUtils.nullSafeConciseToString(timeZone)).isEqualTo(timeZone.getID());
|
||||
}
|
||||
|
||||
@Test
|
||||
void nullSafeConciseToStringForZoneId() {
|
||||
ZoneId zoneId = ZoneId.systemDefault();
|
||||
assertThat(ObjectUtils.nullSafeConciseToString(zoneId)).isEqualTo(zoneId.getId());
|
||||
}
|
||||
|
||||
@Test
|
||||
void nullSafeConciseToStringForArraysAndCollections() {
|
||||
List<String> list = List.of("a", "b", "c");
|
||||
|
|
|
|||
Loading…
Reference in New Issue