Skip to content

Commit 10312fa

Browse files
rstoyanchevcesarhernandezgt
authored andcommitted
Add ignoreCase variants to PatternMatchUtils
See spring-projectsgh-34801
1 parent efa28d5 commit 10312fa

File tree

2 files changed

+145
-48
lines changed

2 files changed

+145
-48
lines changed

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

Lines changed: 52 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -36,13 +36,24 @@ public abstract class PatternMatchUtils {
3636
* @return whether the String matches the given pattern
3737
*/
3838
public static boolean simpleMatch(@Nullable String pattern, @Nullable String str) {
39+
return simpleMatch(pattern, str, false);
40+
}
41+
42+
/**
43+
* Variant of {@link #simpleMatch(String, String)} that ignores upper/lower case.
44+
*/
45+
public static boolean simpleMatchIgnoreCase(@Nullable String pattern, @Nullable String str) {
46+
return simpleMatch(pattern, str, true);
47+
}
48+
49+
private static boolean simpleMatch(@Nullable String pattern, @Nullable String str, boolean ignoreCase) {
3950
if (pattern == null || str == null) {
4051
return false;
4152
}
4253

4354
int firstIndex = pattern.indexOf('*');
4455
if (firstIndex == -1) {
45-
return pattern.equals(str);
56+
return (ignoreCase ? pattern.equalsIgnoreCase(str) : pattern.equals(str));
4657
}
4758

4859
if (firstIndex == 0) {
@@ -51,25 +62,43 @@ public static boolean simpleMatch(@Nullable String pattern, @Nullable String str
5162
}
5263
int nextIndex = pattern.indexOf('*', 1);
5364
if (nextIndex == -1) {
54-
return str.endsWith(pattern.substring(1));
65+
String part = pattern.substring(1);
66+
return (ignoreCase ? StringUtils.endsWithIgnoreCase(str, part) : str.endsWith(part));
5567
}
5668
String part = pattern.substring(1, nextIndex);
5769
if (part.isEmpty()) {
58-
return simpleMatch(pattern.substring(nextIndex), str);
70+
return simpleMatch(pattern.substring(nextIndex), str, ignoreCase);
5971
}
60-
int partIndex = str.indexOf(part);
72+
int partIndex = indexOf(str, part, 0, ignoreCase);
6173
while (partIndex != -1) {
62-
if (simpleMatch(pattern.substring(nextIndex), str.substring(partIndex + part.length()))) {
74+
if (simpleMatch(pattern.substring(nextIndex), str.substring(partIndex + part.length()), ignoreCase)) {
6375
return true;
6476
}
65-
partIndex = str.indexOf(part, partIndex + 1);
77+
partIndex = indexOf(str, part, partIndex + 1, ignoreCase);
6678
}
6779
return false;
6880
}
6981

7082
return (str.length() >= firstIndex &&
71-
pattern.startsWith(str.substring(0, firstIndex)) &&
72-
simpleMatch(pattern.substring(firstIndex), str.substring(firstIndex)));
83+
checkStartsWith(pattern, str, firstIndex, ignoreCase) &&
84+
simpleMatch(pattern.substring(firstIndex), str.substring(firstIndex), ignoreCase));
85+
}
86+
87+
private static boolean checkStartsWith(String pattern, String str, int index, boolean ignoreCase) {
88+
String part = str.substring(0, index);
89+
return (ignoreCase ? StringUtils.startsWithIgnoreCase(pattern, part) : pattern.startsWith(part));
90+
}
91+
92+
private static int indexOf(String str, String otherStr, int startIndex, boolean ignoreCase) {
93+
if (!ignoreCase) {
94+
return str.indexOf(otherStr, startIndex);
95+
}
96+
for (int i = startIndex; i <= (str.length() - otherStr.length()); i++) {
97+
if (str.regionMatches(true, i, otherStr, 0, otherStr.length())) {
98+
return i;
99+
}
100+
}
101+
return -1;
73102
}
74103

75104
/**
@@ -80,7 +109,7 @@ public static boolean simpleMatch(@Nullable String pattern, @Nullable String str
80109
* @param str the String to match
81110
* @return whether the String matches any of the given patterns
82111
*/
83-
public static boolean simpleMatch(@Nullable String[] patterns, String str) {
112+
public static boolean simpleMatch(@Nullable String[] patterns, @Nullable String str) {
84113
if (patterns != null) {
85114
for (String pattern : patterns) {
86115
if (simpleMatch(pattern, str)) {
@@ -91,4 +120,18 @@ public static boolean simpleMatch(@Nullable String[] patterns, String str) {
91120
return false;
92121
}
93122

123+
/**
124+
* Variant of {@link #simpleMatch(String[], String)} that ignores upper/lower case.
125+
*/
126+
public static boolean simpleMatchIgnoreCase(@Nullable String[] patterns, @Nullable String str) {
127+
if (patterns != null) {
128+
for (String pattern : patterns) {
129+
if (simpleMatch(pattern, str, true)) {
130+
return true;
131+
}
132+
}
133+
}
134+
return false;
135+
}
136+
94137
}

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

Lines changed: 93 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -26,81 +26,135 @@
2626
*/
2727
class PatternMatchUtilsTests {
2828

29+
@Test
30+
void nullAndEmptyValues() {
31+
assertDoesNotMatch((String) null, null);
32+
assertDoesNotMatch((String) null, "");
33+
assertDoesNotMatch("123", null);
34+
35+
assertDoesNotMatch((String[]) null, null);
36+
assertDoesNotMatch((String[]) null, "");
37+
assertDoesNotMatch(new String[] {}, null);
38+
}
39+
2940
@Test
3041
void trivial() {
3142
assertThat(PatternMatchUtils.simpleMatch((String) null, "")).isFalse();
3243
assertThat(PatternMatchUtils.simpleMatch("1", null)).isFalse();
3344
doTest("*", "123", true);
3445
doTest("123", "123", true);
46+
testMixedCaseMatch("abC", "Abc");
47+
assertMatches("", "");
48+
assertMatches("123", "123");
49+
assertMatches("*", "123");
50+
51+
assertMatches(new String[] { "" }, "");
52+
assertMatches(new String[] { "123" }, "123");
53+
assertMatches(new String[] { "*" }, "123");
54+
55+
assertMatches(new String[] { null, "" }, "");
56+
assertMatches(new String[] { null, "123" }, "123");
57+
assertMatches(new String[] { null, "*" }, "123");
58+
59+
testMixedCaseMatch("abC", "Abc");
3560
}
3661

3762
@Test
3863
void startsWith() {
39-
doTest("get*", "getMe", true);
40-
doTest("get*", "setMe", false);
64+
assertMatches("get*", "getMe");
65+
assertDoesNotMatch("get*", "setMe");
66+
testMixedCaseMatch("geT*", "GetMe");
4167
}
4268

4369
@Test
4470
void endsWith() {
45-
doTest("*Test", "getMeTest", true);
46-
doTest("*Test", "setMe", false);
71+
assertMatches("*Test", "getMeTest");
72+
assertDoesNotMatch("*Test", "setMe");
73+
testMixedCaseMatch("*TeSt", "getMeTesT");
4774
}
4875

4976
@Test
5077
void between() {
51-
doTest("*stuff*", "getMeTest", false);
52-
doTest("*stuff*", "getstuffTest", true);
53-
doTest("*stuff*", "stuffTest", true);
54-
doTest("*stuff*", "getstuff", true);
55-
doTest("*stuff*", "stuff", true);
78+
assertDoesNotMatch("*stuff*", "getMeTest");
79+
assertMatches("*stuff*", "getstuffTest");
80+
assertMatches("*stuff*", "stuffTest");
81+
assertMatches("*stuff*", "getstuff");
82+
assertMatches("*stuff*", "stuff");
83+
testMixedCaseMatch("*stuff*", "getStuffTest");
84+
testMixedCaseMatch("*stuff*", "StuffTest");
85+
testMixedCaseMatch("*stuff*", "getStuff");
86+
testMixedCaseMatch("*stuff*", "Stuff");
5687
}
5788

5889
@Test
5990
void startsEnds() {
60-
doTest("on*Event", "onMyEvent", true);
61-
doTest("on*Event", "onEvent", true);
62-
doTest("3*3", "3", false);
63-
doTest("3*3", "33", true);
91+
assertMatches("on*Event", "onMyEvent");
92+
assertMatches("on*Event", "onEvent");
93+
assertDoesNotMatch("3*3", "3");
94+
assertMatches("3*3", "33");
95+
testMixedCaseMatch("on*Event", "OnMyEvenT");
96+
testMixedCaseMatch("on*Event", "OnEvenT");
6497
}
6598

6699
@Test
67100
void startsEndsBetween() {
68-
doTest("12*45*78", "12345678", true);
69-
doTest("12*45*78", "123456789", false);
70-
doTest("12*45*78", "012345678", false);
71-
doTest("12*45*78", "124578", true);
72-
doTest("12*45*78", "1245457878", true);
73-
doTest("3*3*3", "33", false);
74-
doTest("3*3*3", "333", true);
101+
assertMatches("12*45*78", "12345678");
102+
assertDoesNotMatch("12*45*78", "123456789");
103+
assertDoesNotMatch("12*45*78", "012345678");
104+
assertMatches("12*45*78", "124578");
105+
assertMatches("12*45*78", "1245457878");
106+
assertDoesNotMatch("3*3*3", "33");
107+
assertMatches("3*3*3", "333");
75108
}
76109

77110
@Test
78111
void ridiculous() {
79-
doTest("*1*2*3*", "0011002001010030020201030", true);
80-
doTest("1*2*3*4", "10300204", false);
81-
doTest("1*2*3*3", "10300203", false);
82-
doTest("*1*2*3*", "123", true);
83-
doTest("*1*2*3*", "132", false);
112+
assertMatches("*1*2*3*", "0011002001010030020201030");
113+
assertDoesNotMatch("1*2*3*4", "10300204");
114+
assertDoesNotMatch("1*2*3*3", "10300203");
115+
assertMatches("*1*2*3*", "123");
116+
assertDoesNotMatch("*1*2*3*", "132");
84117
}
85118

86119
@Test
87120
void patternVariants() {
88-
doTest("*a", "*", false);
89-
doTest("*a", "a", true);
90-
doTest("*a", "b", false);
91-
doTest("*a", "aa", true);
92-
doTest("*a", "ba", true);
93-
doTest("*a", "ab", false);
94-
doTest("**a", "*", false);
95-
doTest("**a", "a", true);
96-
doTest("**a", "b", false);
97-
doTest("**a", "aa", true);
98-
doTest("**a", "ba", true);
99-
doTest("**a", "ab", false);
121+
assertDoesNotMatch("*a", "*");
122+
assertMatches("*a", "a");
123+
assertDoesNotMatch("*a", "b");
124+
assertMatches("*a", "aa");
125+
assertMatches("*a", "ba");
126+
assertDoesNotMatch("*a", "ab");
127+
assertDoesNotMatch("**a", "*");
128+
assertMatches("**a", "a");
129+
assertDoesNotMatch("**a", "b");
130+
assertMatches("**a", "aa");
131+
assertMatches("**a", "ba");
132+
assertDoesNotMatch("**a", "ab");
133+
}
134+
135+
private void assertMatches(String pattern, String str) {
136+
assertThat(PatternMatchUtils.simpleMatch(pattern, str)).isTrue();
137+
assertThat(PatternMatchUtils.simpleMatchIgnoreCase(pattern, str)).isTrue();
138+
}
139+
140+
private void assertDoesNotMatch(String pattern, String str) {
141+
assertThat(PatternMatchUtils.simpleMatch(pattern, str)).isFalse();
142+
assertThat(PatternMatchUtils.simpleMatchIgnoreCase(pattern, str)).isFalse();
143+
}
144+
145+
private void testMixedCaseMatch(String pattern, String str) {
146+
assertThat(PatternMatchUtils.simpleMatch(pattern, str)).isFalse();
147+
assertThat(PatternMatchUtils.simpleMatchIgnoreCase(pattern, str)).isTrue();
148+
}
149+
150+
private void assertMatches(String[] patterns, String str) {
151+
assertThat(PatternMatchUtils.simpleMatch(patterns, str)).isTrue();
152+
assertThat(PatternMatchUtils.simpleMatchIgnoreCase(patterns, str)).isTrue();
100153
}
101154

102-
private void doTest(String pattern, String str, boolean shouldMatch) {
103-
assertThat(PatternMatchUtils.simpleMatch(pattern, str)).isEqualTo(shouldMatch);
155+
private void assertDoesNotMatch(String[] patterns, String str) {
156+
assertThat(PatternMatchUtils.simpleMatch(patterns, str)).isFalse();
157+
assertThat(PatternMatchUtils.simpleMatchIgnoreCase(patterns, str)).isFalse();
104158
}
105159

106160
}

0 commit comments

Comments
 (0)