From 5d41e0f0121d710cf2e0f90fac3920057004d254 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Sun, 22 Oct 2017 14:31:17 +0200 Subject: [PATCH] Avoid temporary String creation in StringUtils.starts/endsWithIgnoreCase Issue: SPR-16095 --- .../org/springframework/util/StringUtils.java | 41 +++--------- .../util/StringUtilsTests.java | 67 ++++++++++++++----- 2 files changed, 60 insertions(+), 48 deletions(-) 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 a692e383a0e..fffd8d4968f 100644 --- a/spring-core/src/main/java/org/springframework/util/StringUtils.java +++ b/spring-core/src/main/java/org/springframework/util/StringUtils.java @@ -164,7 +164,7 @@ public abstract class StringUtils { } } return false; -} + } /** * Check whether the given {@code CharSequence} contains any whitespace characters. @@ -314,7 +314,6 @@ public abstract class StringUtils { return sb.toString(); } - /** * Test if the given {@code String} starts with the specified prefix, * ignoring upper/lower case. @@ -323,19 +322,8 @@ public abstract class StringUtils { * @see java.lang.String#startsWith */ public static boolean startsWithIgnoreCase(@Nullable String str, @Nullable String prefix) { - if (str == null || prefix == null) { - return false; - } - if (str.startsWith(prefix)) { - return true; - } - if (str.length() < prefix.length()) { - return false; - } - - String lcStr = str.substring(0, prefix.length()).toLowerCase(); - String lcPrefix = prefix.toLowerCase(); - return lcStr.equals(lcPrefix); + return (str != null && prefix != null && str.length() >= prefix.length() && + str.regionMatches(true, 0, prefix, 0, prefix.length())); } /** @@ -346,19 +334,8 @@ public abstract class StringUtils { * @see java.lang.String#endsWith */ public static boolean endsWithIgnoreCase(@Nullable String str, @Nullable String suffix) { - if (str == null || suffix == null) { - return false; - } - if (str.endsWith(suffix)) { - return true; - } - if (str.length() < suffix.length()) { - return false; - } - - String lcStr = str.substring(str.length() - suffix.length()).toLowerCase(); - String lcSuffix = suffix.toLowerCase(); - return lcStr.equals(lcSuffix); + return (str != null && suffix != null && str.length() >= suffix.length() && + str.regionMatches(true, str.length() - suffix.length(), suffix, 0, suffix.length())); } /** @@ -369,9 +346,11 @@ public abstract class StringUtils { * @param substring the substring to match at the given index */ public static boolean substringMatch(CharSequence str, int index, CharSequence substring) { - for (int j = 0; j < substring.length(); j++) { - int i = index + j; - if (i >= str.length() || str.charAt(i) != substring.charAt(j)) { + if (index + substring.length() > str.length()) { + return false; + } + for (int i = 0; i < substring.length(); i++) { + if (str.charAt(index + i) != substring.charAt(i)) { return false; } } 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 f65d65026ec..45f5201ed7b 100644 --- a/spring-core/src/test/java/org/springframework/util/StringUtilsTests.java +++ b/spring-core/src/test/java/org/springframework/util/StringUtilsTests.java @@ -138,6 +138,56 @@ public class StringUtilsTests { assertEquals(" a b c", StringUtils.trimTrailingCharacter(" a b c ", ' ')); } + @Test + public void testStartsWithIgnoreCase() { + String suffix = "fOo"; + assertTrue(StringUtils.startsWithIgnoreCase("foo", suffix)); + assertTrue(StringUtils.startsWithIgnoreCase("Foo", suffix)); + assertTrue(StringUtils.startsWithIgnoreCase("foobar", suffix)); + assertTrue(StringUtils.startsWithIgnoreCase("foobarbar", suffix)); + assertTrue(StringUtils.startsWithIgnoreCase("Foobar", suffix)); + assertTrue(StringUtils.startsWithIgnoreCase("FoobarBar", suffix)); + assertTrue(StringUtils.startsWithIgnoreCase("foObar", suffix)); + assertTrue(StringUtils.startsWithIgnoreCase("FOObar", suffix)); + assertTrue(StringUtils.startsWithIgnoreCase("fOobar", suffix)); + assertFalse(StringUtils.startsWithIgnoreCase(null, suffix)); + assertFalse(StringUtils.startsWithIgnoreCase("fOobar", null)); + assertFalse(StringUtils.startsWithIgnoreCase("b", suffix)); + } + + @Test + public void testEndsWithIgnoreCase() { + String suffix = "fOo"; + assertTrue(StringUtils.endsWithIgnoreCase("foo", suffix)); + assertTrue(StringUtils.endsWithIgnoreCase("Foo", suffix)); + assertTrue(StringUtils.endsWithIgnoreCase("barfoo", suffix)); + assertTrue(StringUtils.endsWithIgnoreCase("barbarfoo", suffix)); + assertTrue(StringUtils.endsWithIgnoreCase("barFoo", suffix)); + assertTrue(StringUtils.endsWithIgnoreCase("barBarFoo", suffix)); + assertTrue(StringUtils.endsWithIgnoreCase("barfoO", suffix)); + assertTrue(StringUtils.endsWithIgnoreCase("barFOO", suffix)); + assertTrue(StringUtils.endsWithIgnoreCase("barfOo", suffix)); + assertFalse(StringUtils.endsWithIgnoreCase(null, suffix)); + assertFalse(StringUtils.endsWithIgnoreCase("barfOo", null)); + assertFalse(StringUtils.endsWithIgnoreCase("b", suffix)); + } + + @Test + public void testSubstringMatch() { + assertTrue(StringUtils.substringMatch("foo", 0, "foo")); + assertTrue(StringUtils.substringMatch("foo", 1, "oo")); + assertTrue(StringUtils.substringMatch("foo", 2, "o")); + assertFalse(StringUtils.substringMatch("foo", 0, "fOo")); + assertFalse(StringUtils.substringMatch("foo", 1, "fOo")); + assertFalse(StringUtils.substringMatch("foo", 2, "fOo")); + assertFalse(StringUtils.substringMatch("foo", 3, "fOo")); + assertFalse(StringUtils.substringMatch("foo", 1, "Oo")); + assertFalse(StringUtils.substringMatch("foo", 2, "Oo")); + assertFalse(StringUtils.substringMatch("foo", 3, "Oo")); + assertFalse(StringUtils.substringMatch("foo", 2, "O")); + assertFalse(StringUtils.substringMatch("foo", 3, "O")); + } + @Test public void testCountOccurrencesOf() { assertTrue("nullx2 = 0", @@ -579,23 +629,6 @@ public class StringUtilsTests { assertTrue("Output equals input", Arrays.equals(sa, components)); } - @Test - public void testEndsWithIgnoreCase() { - String suffix = "fOo"; - assertTrue(StringUtils.endsWithIgnoreCase("foo", suffix)); - assertTrue(StringUtils.endsWithIgnoreCase("Foo", suffix)); - assertTrue(StringUtils.endsWithIgnoreCase("barfoo", suffix)); - assertTrue(StringUtils.endsWithIgnoreCase("barbarfoo", suffix)); - assertTrue(StringUtils.endsWithIgnoreCase("barFoo", suffix)); - assertTrue(StringUtils.endsWithIgnoreCase("barBarFoo", suffix)); - assertTrue(StringUtils.endsWithIgnoreCase("barfoO", suffix)); - assertTrue(StringUtils.endsWithIgnoreCase("barFOO", suffix)); - assertTrue(StringUtils.endsWithIgnoreCase("barfOo", suffix)); - assertFalse(StringUtils.endsWithIgnoreCase(null, suffix)); - assertFalse(StringUtils.endsWithIgnoreCase("barfOo", null)); - assertFalse(StringUtils.endsWithIgnoreCase("b", suffix)); - } - @Test public void testParseLocaleStringSunnyDay() {