Skip to content

Commit 6cc00dd

Browse files
committed
Refactor StringUtils.split methods and improve tests #191
Reimplemented StringUtils.split to avoid StringTokenizer, handle null and empty delimiters, and added toStringArray utility. Updated StringUtilsTest to cover new edge cases and ensure compatibility with Spring's delimitedListToStringArray.
1 parent c4ec584 commit 6cc00dd

2 files changed

Lines changed: 111 additions & 25 deletions

File tree

microsphere-java-core/src/main/java/io/microsphere/util/StringUtils.java

Lines changed: 84 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -17,18 +17,21 @@
1717
package io.microsphere.util;
1818

1919
import io.microsphere.annotation.Immutable;
20+
import io.microsphere.annotation.Nonnull;
2021
import io.microsphere.annotation.Nullable;
2122

22-
import java.util.StringTokenizer;
23+
import java.util.ArrayList;
24+
import java.util.Collection;
25+
import java.util.List;
2326

24-
import static io.microsphere.util.ArrayUtils.asArray;
27+
import static io.microsphere.collection.CollectionUtils.isEmpty;
28+
import static io.microsphere.util.ArrayUtils.ofArray;
2529
import static io.microsphere.util.CharSequenceUtils.isEmpty;
2630
import static io.microsphere.util.CharSequenceUtils.length;
2731
import static java.lang.Character.isDigit;
2832
import static java.lang.Character.isWhitespace;
2933
import static java.lang.Character.toLowerCase;
3034
import static java.lang.Character.toUpperCase;
31-
import static java.lang.String.valueOf;
3235

3336
/**
3437
* The utilities class for {@link String}
@@ -127,8 +130,29 @@ public static boolean isNotBlank(String value) {
127130
* @param delimiter the char used as a delimiter to split the String
128131
* @return an array of Strings, split by the delimiter; never null
129132
*/
130-
public static String[] split(String value, char delimiter) {
131-
return split(value, valueOf(delimiter));
133+
@Nonnull
134+
public static String[] split(@Nullable String value, char delimiter) {
135+
int length = length(value);
136+
if (length < 1) {
137+
return EMPTY_STRING_ARRAY;
138+
}
139+
140+
List<String> result = new ArrayList<>();
141+
142+
int startIndex = 0;
143+
int endIndex;
144+
145+
while ((endIndex = value.indexOf(delimiter, startIndex)) > -1) {
146+
String part = value.substring(startIndex, endIndex);
147+
result.add(part);
148+
startIndex = endIndex + 1;
149+
}
150+
if (startIndex <= length) {
151+
// Add rest of String, but not in case of empty input.
152+
result.add(value.substring(startIndex));
153+
}
154+
155+
return toStringArray(result);
132156
}
133157

134158
/**
@@ -140,7 +164,9 @@ public static String[] split(String value, char delimiter) {
140164
* <h3>Example Usage</h3>
141165
* <pre>{@code
142166
* StringUtils.split(null, ",") = []
167+
* StringUtils.split("", null) = []
143168
* StringUtils.split("", ";") = []
169+
* StringUtils.split("abc", "") = ["a", "b", "c"]
144170
* StringUtils.split("a,b,c", ",") = ["a", "b", "c"]
145171
* StringUtils.split("a;b;c", ",") = ["a;b;c"]
146172
* StringUtils.split("a,,b,c", ",") = ["a", "", "b", "c"]
@@ -150,12 +176,41 @@ public static String[] split(String value, char delimiter) {
150176
* @param delimiter the String used as a delimiter to split the String, may be null or empty
151177
* @return an array of Strings, split by the delimiter; never null
152178
*/
153-
public static String[] split(String value, String delimiter) {
154-
if (isEmpty(value) || isEmpty(delimiter)) {
179+
@Nonnull
180+
public static String[] split(@Nullable String value, @Nullable String delimiter) {
181+
int length = length(value);
182+
if (length < 1) {
155183
return EMPTY_STRING_ARRAY;
156184
}
157-
StringTokenizer stringTokenizer = new StringTokenizer(value, delimiter);
158-
return (String[]) asArray(stringTokenizer, String.class);
185+
186+
if (delimiter == null) {
187+
return ofArray(value);
188+
}
189+
190+
int delimiterLength = delimiter.length();
191+
192+
List<String> result = new ArrayList<>();
193+
194+
if (delimiterLength == 0) {
195+
for (int i = 0; i < value.length(); i++) {
196+
result.add(value.substring(i, i + 1));
197+
}
198+
} else {
199+
int startIndex = 0;
200+
int endIndex;
201+
202+
while ((endIndex = value.indexOf(delimiter, startIndex)) > -1) {
203+
String part = value.substring(startIndex, endIndex);
204+
result.add(part);
205+
startIndex = endIndex + delimiterLength;
206+
}
207+
if (startIndex <= length) {
208+
// Add rest of String, but not in case of empty input.
209+
result.add(value.substring(startIndex));
210+
}
211+
}
212+
213+
return toStringArray(result);
159214
}
160215

161216
/**
@@ -780,6 +835,26 @@ public static String uncapitalize(String str) {
780835
return changeFirstCharacter(str, false);
781836
}
782837

838+
/**
839+
* Convert the given {@link Collection} into a {@code String} array.
840+
* <p>The {@code Collection} must contain {@code String} elements only.
841+
*
842+
* <h3>Example Usage</h3>
843+
* <pre>{@code
844+
* StringUtils.toStringArray(null) = []
845+
* StringUtils.toStringArray(new ArrayList<>()) = []
846+
* StringUtils.toStringArray(Arrays.asList("a", "b", "c")) = ["a", "b", "c"]
847+
* }</pre>
848+
*
849+
* @param collection the {@code Collection} to convert
850+
* (potentially {@code null} or empty)
851+
* @return the resulting {@code String} array
852+
*/
853+
@Nonnull
854+
public static String[] toStringArray(@Nullable Collection<String> collection) {
855+
return (!isEmpty(collection) ? collection.toArray(EMPTY_STRING_ARRAY) : EMPTY_STRING_ARRAY);
856+
}
857+
783858
static String changeFirstCharacter(String str, boolean capitalize) {
784859
int len = length(str);
785860
if (len < 1) {

microsphere-java-core/src/test/java/io/microsphere/util/StringUtilsTest.java

Lines changed: 27 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,11 @@
33
import org.junit.jupiter.api.Test;
44

55
import static io.microsphere.constants.SymbolConstants.COMMA;
6-
import static io.microsphere.constants.SymbolConstants.COMMA_CHAR;
76
import static io.microsphere.constants.SymbolConstants.DOT;
87
import static io.microsphere.constants.SymbolConstants.SPACE;
9-
import static io.microsphere.constants.SymbolConstants.SPACE_CHAR;
108
import static io.microsphere.constants.SymbolConstants.VERTICAL_BAR;
119
import static io.microsphere.util.ArrayUtils.ofArray;
10+
import static io.microsphere.util.CharSequenceUtils.length;
1211
import static io.microsphere.util.CharSequenceUtilsTest.TEST_BLANK_STRING;
1312
import static io.microsphere.util.CharSequenceUtilsTest.TEST_CSV_STRING;
1413
import static io.microsphere.util.CharSequenceUtilsTest.TEST_EMPTY_STRING;
@@ -42,6 +41,7 @@
4241
import static org.junit.jupiter.api.Assertions.assertNull;
4342
import static org.junit.jupiter.api.Assertions.assertSame;
4443
import static org.junit.jupiter.api.Assertions.assertTrue;
44+
import static org.springframework.util.StringUtils.delimitedListToStringArray;
4545

4646
/**
4747
* {@link StringUtils} Test
@@ -81,26 +81,37 @@ void testIsNotBlank() {
8181

8282
@Test
8383
void testSplit() {
84-
String[] values = split(null, SPACE_CHAR);
85-
assertSame(EMPTY_STRING_ARRAY, values);
84+
assertSame(EMPTY_STRING_ARRAY, assertSplit(null, SPACE));
8685

87-
values = split(TEST_EMPTY_STRING, SPACE);
88-
assertSame(EMPTY_STRING_ARRAY, values);
86+
assertSame(EMPTY_STRING_ARRAY, assertSplit(TEST_EMPTY_STRING, SPACE));
8987

90-
values = split(TEST_BLANK_STRING, null);
91-
assertSame(EMPTY_STRING_ARRAY, values);
88+
assertSame(EMPTY_STRING_ARRAY, assertSplit(TEST_EMPTY_STRING, EMPTY_STRING));
9289

93-
values = split(TEST_BLANK_STRING, SPACE);
94-
assertArrayEquals(EMPTY_STRING_ARRAY, values);
90+
assertArrayEquals(ofArray(TEST_BLANK_STRING), assertSplit(TEST_BLANK_STRING, null));
9591

96-
values = split(SPACE + SPACE, SPACE);
97-
assertArrayEquals(EMPTY_STRING_ARRAY, values);
92+
assertArrayEquals(ofArray(TEST_BLANK_STRING), assertSplit(TEST_BLANK_STRING, EMPTY_STRING));
9893

99-
values = split(SPACE + SPACE + SPACE, SPACE);
100-
assertArrayEquals(EMPTY_STRING_ARRAY, values);
94+
assertArrayEquals(ofArray(EMPTY_STRING, EMPTY_STRING), assertSplit(TEST_BLANK_STRING, SPACE));
10195

102-
values = split(TEST_CSV_STRING, COMMA_CHAR);
103-
assertArrayEquals(ofArray("a", "b", "c"), values);
96+
assertArrayEquals(ofArray(EMPTY_STRING, EMPTY_STRING, EMPTY_STRING), assertSplit(SPACE + SPACE, SPACE));
97+
98+
assertArrayEquals(ofArray(EMPTY_STRING, EMPTY_STRING, EMPTY_STRING, EMPTY_STRING), assertSplit(SPACE + SPACE + SPACE, SPACE));
99+
100+
assertArrayEquals(ofArray("a", "b", "c"), assertSplit(TEST_CSV_STRING, COMMA));
101+
102+
assertArrayEquals(ofArray(TEST_CSV_STRING), assertSplit(TEST_CSV_STRING, SPACE));
103+
104+
assertArrayEquals(ofArray("a", "", "b", "c"), assertSplit("a, , b, c", ", "));
105+
}
106+
107+
String[] assertSplit(String str, String delimiter) {
108+
String[] values = split(str, delimiter);
109+
if (length(delimiter) == 1) {
110+
assertArrayEquals(split(str, delimiter.charAt(0)), values);
111+
}
112+
String[] valuesFromString = delimitedListToStringArray(str, delimiter);
113+
assertArrayEquals(valuesFromString, values);
114+
return values;
104115
}
105116

106117
@Test

0 commit comments

Comments
 (0)