From 0a1aeafe08e186573ce93e8c6855f9ee006876b2 Mon Sep 17 00:00:00 2001 From: Sam Brannen Date: Wed, 5 Apr 2023 12:15:35 +0200 Subject: [PATCH] Introduce StringUtils.truncate() StringUtils.truncate() serves as central, consistent way for truncating strings used in log messages and exception failure messages, for immediate use in LogFormatUtils and ObjectUtils. See gh-30286 Closes gh-30290 --- .../core/log/LogFormatUtils.java | 5 ++- .../org/springframework/util/StringUtils.java | 42 ++++++++++++++++++- .../util/StringUtilsTests.java | 25 ++++++++++- 3 files changed, 68 insertions(+), 4 deletions(-) diff --git a/spring-core/src/main/java/org/springframework/core/log/LogFormatUtils.java b/spring-core/src/main/java/org/springframework/core/log/LogFormatUtils.java index 550a202255c..786680bb2a3 100644 --- a/spring-core/src/main/java/org/springframework/core/log/LogFormatUtils.java +++ b/spring-core/src/main/java/org/springframework/core/log/LogFormatUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2021 the original author or authors. + * Copyright 2002-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -23,6 +23,7 @@ import org.apache.commons.logging.Log; import org.springframework.lang.Nullable; import org.springframework.util.ObjectUtils; +import org.springframework.util.StringUtils; /** * Utility methods for formatting and logging messages. @@ -78,7 +79,7 @@ public abstract class LogFormatUtils { result = ObjectUtils.nullSafeToString(ex); } if (maxLength != -1) { - result = (result.length() > maxLength ? result.substring(0, maxLength) + " (truncated)..." : result); + result = StringUtils.truncate(result, maxLength); } if (replaceNewlinesAndControlCharacters) { result = NEWLINE_PATTERN.matcher(result).replaceAll(""); diff --git a/spring-core/src/main/java/org/springframework/util/StringUtils.java b/spring-core/src/main/java/org/springframework/util/StringUtils.java index 4fe02d63892..e803a05db2d 100644 --- a/spring-core/src/main/java/org/springframework/util/StringUtils.java +++ b/spring-core/src/main/java/org/springframework/util/StringUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2022 the original author or authors. + * Copyright 2002-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -75,6 +75,10 @@ public abstract class StringUtils { private static final char EXTENSION_SEPARATOR = '.'; + private static final int DEFAULT_TRUNCATION_THRESHOLD = 100; + + private static final String TRUNCATION_SUFFIX = " (truncated)..."; + //--------------------------------------------------------------------- // General convenience methods for working with Strings @@ -1388,4 +1392,40 @@ public abstract class StringUtils { return arrayToDelimitedString(arr, ","); } + /** + * Truncate the supplied {@link CharSequence}. + *

Delegates to {@link #truncate(CharSequence, int)}, supplying {@code 100} + * as the threshold. + * @param charSequence the {@code CharSequence} to truncate + * @return a truncated string, or a string representation of the original + * {@code CharSequence} if its length does not exceed the threshold + * @since 5.3.27 + */ + public static String truncate(CharSequence charSequence) { + return truncate(charSequence, DEFAULT_TRUNCATION_THRESHOLD); + } + + /** + * Truncate the supplied {@link CharSequence}. + *

If the length of the {@code CharSequence} is greater than the threshold, + * this method returns a {@linkplain CharSequence#subSequence(int, int) + * subsequence} of the {@code CharSequence} (up to the threshold) appended + * with the suffix {@code " (truncated)..."}. Otherwise, this method returns + * {@code charSequence.toString()}. + * @param charSequence the {@code CharSequence} to truncate + * @param threshold the maximum length after which to truncate; must be a + * positive number + * @return a truncated string, or a string representation of the original + * {@code CharSequence} if its length does not exceed the threshold + * @since 5.3.27 + */ + public static String truncate(CharSequence charSequence, int threshold) { + Assert.isTrue(threshold > 0, + () -> "Truncation threshold must be a positive number: " + threshold); + if (charSequence.length() > threshold) { + return charSequence.subSequence(0, threshold) + TRUNCATION_SUFFIX; + } + return charSequence.toString(); + } + } diff --git a/spring-core/src/test/java/org/springframework/util/StringUtilsTests.java b/spring-core/src/test/java/org/springframework/util/StringUtilsTests.java index 4609e6832cb..f82778d0b6e 100644 --- a/spring-core/src/test/java/org/springframework/util/StringUtilsTests.java +++ b/spring-core/src/test/java/org/springframework/util/StringUtilsTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2022 the original author or authors. + * Copyright 2002-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -22,6 +22,8 @@ import java.util.Locale; import java.util.Properties; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; @@ -785,4 +787,25 @@ class StringUtilsTests { assertThat(StringUtils.collectionToCommaDelimitedString(Collections.singletonList(null))).isEqualTo("null"); } + @Test + void truncatePreconditions() { + assertThatIllegalArgumentException() + .isThrownBy(() -> StringUtils.truncate("foo", 0)) + .withMessage("Truncation threshold must be a positive number: 0"); + assertThatIllegalArgumentException() + .isThrownBy(() -> StringUtils.truncate("foo", -99)) + .withMessage("Truncation threshold must be a positive number: -99"); + } + + @ParameterizedTest + @CsvSource(delimiterString = "-->", value = { + "aardvark --> aardvark", + "aardvark12 --> aardvark12", + "aardvark123 --> aardvark12 (truncated)...", + "aardvark, bird, cat --> aardvark, (truncated)..." + }) + void truncate(String text, String truncated) { + assertThat(StringUtils.truncate(text, 10)).isEqualTo(truncated); + } + }