Trung | Added StringUtils::resizeConsecutiveChars() and related Unit Tests to enrich the StringUtils methods.

Signed-off-by: trungpnt-byte <metopsecret9999@gmail.com>
This commit is contained in:
trungpnt-byte 2025-05-19 16:52:48 +07:00
parent 094e653746
commit 1076aab3c0
2 changed files with 93 additions and 0 deletions

View File

@ -1416,4 +1416,63 @@ public abstract class StringUtils {
return charSequence.toString();
}
/**
* Resize consecutive characters to repeat a given number of times.
* <p>For example, the string "aaabbbcc" with a appointedChar of 'a' and
* repeatedCount of 2 will return "aabbcc".
* <p>Note that the appointedChar is not removed from the string.
* <p>Note that the repeatedCount is not checked for negative values.
* @param str the string to resize
* @param appointedChar the character to resize
* @param repeatedCount the number of times to repeat the character, default is 1
* @param isRemovedTrailing if true, the trailing characters will be removed
* @param isRemovedLeading if true, the leading characters will be removed
* @return the resized string, or the original string if no changes were made
*/
public static String resizeConsecutiveChars(String str, char appointedChar, @Nullable Integer repeatedCount, boolean isRemovedTrailing, boolean isRemovedLeading) {
if (str == null || str.isEmpty()) {
return str;
}
int xTimes = repeatedCount != null ? repeatedCount : 1;
char[] collector = new char[str.length()];
int collectorIdx = 0;
char[] statefulSeparator = new char[1];
for (int i = 0; i < str.length(); i++) {
char current = str.charAt(i);
if (current == appointedChar) {
if (statefulSeparator[0] == '\u0000') {
statefulSeparator[0] = appointedChar;
}
} else {
if (statefulSeparator[0] == appointedChar) {
if (collectorIdx > 0 || !isRemovedLeading) {
int repetition = xTimes;
while (repetition-- > 0) {
collector[collectorIdx++] = appointedChar;
}
statefulSeparator[0] = '\u0000';
}
}
collector[collectorIdx++] = current;
}
}
if (!isRemovedTrailing && statefulSeparator[0] == appointedChar) {
int repetition = xTimes;
while (repetition-- > 0) {
collector[collectorIdx++] = appointedChar;
}
}
return new String(collector, 0, collectorIdx);
}
/**
* Makes use of resizeConsecutiveChars() sanitize the string to have only one whitespace between words.
* @param str
* @param isTrimmed
* @return the sanitized string
*/
public static String resizeToOneWhitespace(String str, boolean isTrimmed) {
return resizeConsecutiveChars(str, ' ', 1, isTrimmed, isTrimmed);
}
}

View File

@ -18,7 +18,9 @@ package org.springframework.util;
import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Locale;
import java.util.Map;
import java.util.Properties;
import org.junit.jupiter.api.Test;
@ -817,4 +819,36 @@ class StringUtilsTests {
assertThat(StringUtils.truncate(text, 10)).isEqualTo(truncated);
}
@Test
void resizeConsecutiveChars() {
assertThat(StringUtils.resizeConsecutiveChars("11111 111111 111111 111111", '1', 1, false, false)).isEqualTo("1 1 1 1");
assertThat(StringUtils.resizeConsecutiveChars("aaabbbcc", 'a', 2, false, false)).isEqualTo("aabbbcc");
assertThat(StringUtils.resizeConsecutiveChars("111111", '1', null, false, false)).isEqualTo("1");
assertThat(StringUtils.resizeConsecutiveChars(".....$$$42....1424.1.....2.5.12..$!@..$!@%$...", '.', 1, false, false)).isEqualTo(".$$$42.1424.1.2.5.12.$!@.$!@%$.");
assertThat(StringUtils.resizeConsecutiveChars("1ewjaoij1 a11w11111e11o11iw11 12113121 1", '1', 1, false, false)).isEqualTo("1ewjaoij1 a1w1e1o1iw1 1213121 1");
// resizes whitespaces
Map<String, String> cases = new LinkedHashMap<>();
cases.put(null, null);
cases.put("", "");
cases.put(" ", "");
cases.put("Hello", "Hello");
cases.put("Hello, World!", "Hello, World!");
cases.put("Hello, World!", "Hello, World!");
cases.put("a a ", "a a");
cases.put(" a c b ncw a c j j j j j", "a c b ncw a c j j j j j");
for (Map.Entry<String, String> entry : cases.entrySet()) {
String input = entry.getKey();
String expected = entry.getValue();
assertThat(StringUtils.resizeToOneWhitespace(input, true)).as("actual ").isEqualTo(expected);
}
}
}