Skip to content

Commit 5d41e0f

Browse files
committed
Avoid temporary String creation in StringUtils.starts/endsWithIgnoreCase
Issue: SPR-16095
1 parent 9a88ebd commit 5d41e0f

File tree

2 files changed

+60
-48
lines changed

2 files changed

+60
-48
lines changed

spring-core/src/main/java/org/springframework/util/StringUtils.java

Lines changed: 10 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,7 @@ private static boolean containsText(CharSequence str) {
164164
}
165165
}
166166
return false;
167-
}
167+
}
168168

169169
/**
170170
* Check whether the given {@code CharSequence} contains any whitespace characters.
@@ -314,7 +314,6 @@ public static String trimTrailingCharacter(String str, char trailingCharacter) {
314314
return sb.toString();
315315
}
316316

317-
318317
/**
319318
* Test if the given {@code String} starts with the specified prefix,
320319
* ignoring upper/lower case.
@@ -323,19 +322,8 @@ public static String trimTrailingCharacter(String str, char trailingCharacter) {
323322
* @see java.lang.String#startsWith
324323
*/
325324
public static boolean startsWithIgnoreCase(@Nullable String str, @Nullable String prefix) {
326-
if (str == null || prefix == null) {
327-
return false;
328-
}
329-
if (str.startsWith(prefix)) {
330-
return true;
331-
}
332-
if (str.length() < prefix.length()) {
333-
return false;
334-
}
335-
336-
String lcStr = str.substring(0, prefix.length()).toLowerCase();
337-
String lcPrefix = prefix.toLowerCase();
338-
return lcStr.equals(lcPrefix);
325+
return (str != null && prefix != null && str.length() >= prefix.length() &&
326+
str.regionMatches(true, 0, prefix, 0, prefix.length()));
339327
}
340328

341329
/**
@@ -346,19 +334,8 @@ public static boolean startsWithIgnoreCase(@Nullable String str, @Nullable Strin
346334
* @see java.lang.String#endsWith
347335
*/
348336
public static boolean endsWithIgnoreCase(@Nullable String str, @Nullable String suffix) {
349-
if (str == null || suffix == null) {
350-
return false;
351-
}
352-
if (str.endsWith(suffix)) {
353-
return true;
354-
}
355-
if (str.length() < suffix.length()) {
356-
return false;
357-
}
358-
359-
String lcStr = str.substring(str.length() - suffix.length()).toLowerCase();
360-
String lcSuffix = suffix.toLowerCase();
361-
return lcStr.equals(lcSuffix);
337+
return (str != null && suffix != null && str.length() >= suffix.length() &&
338+
str.regionMatches(true, str.length() - suffix.length(), suffix, 0, suffix.length()));
362339
}
363340

364341
/**
@@ -369,9 +346,11 @@ public static boolean endsWithIgnoreCase(@Nullable String str, @Nullable String
369346
* @param substring the substring to match at the given index
370347
*/
371348
public static boolean substringMatch(CharSequence str, int index, CharSequence substring) {
372-
for (int j = 0; j < substring.length(); j++) {
373-
int i = index + j;
374-
if (i >= str.length() || str.charAt(i) != substring.charAt(j)) {
349+
if (index + substring.length() > str.length()) {
350+
return false;
351+
}
352+
for (int i = 0; i < substring.length(); i++) {
353+
if (str.charAt(index + i) != substring.charAt(i)) {
375354
return false;
376355
}
377356
}

spring-core/src/test/java/org/springframework/util/StringUtilsTests.java

Lines changed: 50 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,56 @@ public void testTrimTrailingCharacter() {
138138
assertEquals(" a b c", StringUtils.trimTrailingCharacter(" a b c ", ' '));
139139
}
140140

141+
@Test
142+
public void testStartsWithIgnoreCase() {
143+
String suffix = "fOo";
144+
assertTrue(StringUtils.startsWithIgnoreCase("foo", suffix));
145+
assertTrue(StringUtils.startsWithIgnoreCase("Foo", suffix));
146+
assertTrue(StringUtils.startsWithIgnoreCase("foobar", suffix));
147+
assertTrue(StringUtils.startsWithIgnoreCase("foobarbar", suffix));
148+
assertTrue(StringUtils.startsWithIgnoreCase("Foobar", suffix));
149+
assertTrue(StringUtils.startsWithIgnoreCase("FoobarBar", suffix));
150+
assertTrue(StringUtils.startsWithIgnoreCase("foObar", suffix));
151+
assertTrue(StringUtils.startsWithIgnoreCase("FOObar", suffix));
152+
assertTrue(StringUtils.startsWithIgnoreCase("fOobar", suffix));
153+
assertFalse(StringUtils.startsWithIgnoreCase(null, suffix));
154+
assertFalse(StringUtils.startsWithIgnoreCase("fOobar", null));
155+
assertFalse(StringUtils.startsWithIgnoreCase("b", suffix));
156+
}
157+
158+
@Test
159+
public void testEndsWithIgnoreCase() {
160+
String suffix = "fOo";
161+
assertTrue(StringUtils.endsWithIgnoreCase("foo", suffix));
162+
assertTrue(StringUtils.endsWithIgnoreCase("Foo", suffix));
163+
assertTrue(StringUtils.endsWithIgnoreCase("barfoo", suffix));
164+
assertTrue(StringUtils.endsWithIgnoreCase("barbarfoo", suffix));
165+
assertTrue(StringUtils.endsWithIgnoreCase("barFoo", suffix));
166+
assertTrue(StringUtils.endsWithIgnoreCase("barBarFoo", suffix));
167+
assertTrue(StringUtils.endsWithIgnoreCase("barfoO", suffix));
168+
assertTrue(StringUtils.endsWithIgnoreCase("barFOO", suffix));
169+
assertTrue(StringUtils.endsWithIgnoreCase("barfOo", suffix));
170+
assertFalse(StringUtils.endsWithIgnoreCase(null, suffix));
171+
assertFalse(StringUtils.endsWithIgnoreCase("barfOo", null));
172+
assertFalse(StringUtils.endsWithIgnoreCase("b", suffix));
173+
}
174+
175+
@Test
176+
public void testSubstringMatch() {
177+
assertTrue(StringUtils.substringMatch("foo", 0, "foo"));
178+
assertTrue(StringUtils.substringMatch("foo", 1, "oo"));
179+
assertTrue(StringUtils.substringMatch("foo", 2, "o"));
180+
assertFalse(StringUtils.substringMatch("foo", 0, "fOo"));
181+
assertFalse(StringUtils.substringMatch("foo", 1, "fOo"));
182+
assertFalse(StringUtils.substringMatch("foo", 2, "fOo"));
183+
assertFalse(StringUtils.substringMatch("foo", 3, "fOo"));
184+
assertFalse(StringUtils.substringMatch("foo", 1, "Oo"));
185+
assertFalse(StringUtils.substringMatch("foo", 2, "Oo"));
186+
assertFalse(StringUtils.substringMatch("foo", 3, "Oo"));
187+
assertFalse(StringUtils.substringMatch("foo", 2, "O"));
188+
assertFalse(StringUtils.substringMatch("foo", 3, "O"));
189+
}
190+
141191
@Test
142192
public void testCountOccurrencesOf() {
143193
assertTrue("nullx2 = 0",
@@ -579,23 +629,6 @@ private void doTestCommaDelimitedListToStringArrayLegalMatch(String[] components
579629
assertTrue("Output equals input", Arrays.equals(sa, components));
580630
}
581631

582-
@Test
583-
public void testEndsWithIgnoreCase() {
584-
String suffix = "fOo";
585-
assertTrue(StringUtils.endsWithIgnoreCase("foo", suffix));
586-
assertTrue(StringUtils.endsWithIgnoreCase("Foo", suffix));
587-
assertTrue(StringUtils.endsWithIgnoreCase("barfoo", suffix));
588-
assertTrue(StringUtils.endsWithIgnoreCase("barbarfoo", suffix));
589-
assertTrue(StringUtils.endsWithIgnoreCase("barFoo", suffix));
590-
assertTrue(StringUtils.endsWithIgnoreCase("barBarFoo", suffix));
591-
assertTrue(StringUtils.endsWithIgnoreCase("barfoO", suffix));
592-
assertTrue(StringUtils.endsWithIgnoreCase("barFOO", suffix));
593-
assertTrue(StringUtils.endsWithIgnoreCase("barfOo", suffix));
594-
assertFalse(StringUtils.endsWithIgnoreCase(null, suffix));
595-
assertFalse(StringUtils.endsWithIgnoreCase("barfOo", null));
596-
assertFalse(StringUtils.endsWithIgnoreCase("b", suffix));
597-
}
598-
599632

600633
@Test
601634
public void testParseLocaleStringSunnyDay() {

0 commit comments

Comments
 (0)