Skip to content

Commit 291550a

Browse files
committed
AntPathMatcher allows for case-insensitive matching
Issue: SPR-13286
1 parent 965fca8 commit 291550a

File tree

2 files changed

+50
-22
lines changed

2 files changed

+50
-22
lines changed

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

Lines changed: 34 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,8 @@ public class AntPathMatcher implements PathMatcher {
7979

8080
private PathSeparatorPatternCache pathSeparatorPatternCache;
8181

82+
private boolean caseSensitive = true;
83+
8284
private boolean trimTokens = true;
8385

8486
private volatile Boolean cachePatterns;
@@ -117,6 +119,15 @@ public void setPathSeparator(String pathSeparator) {
117119
this.pathSeparatorPatternCache = new PathSeparatorPatternCache(this.pathSeparator);
118120
}
119121

122+
/**
123+
* Specify whether to perform pattern matching in a case-sensitive fashion.
124+
* <p>Default is {@code true}. Switch this to {@code false} for case-insensitive matching.
125+
* @since 4.2
126+
*/
127+
public void setCaseSensitive(boolean caseSensitive) {
128+
this.caseSensitive = caseSensitive;
129+
}
130+
120131
/**
121132
* Specify whether to trim tokenized paths and patterns.
122133
* <p>Default is {@code true}.
@@ -134,6 +145,7 @@ public void setTrimTokens(boolean trimTokens) {
134145
* turn it off when encountering too many patterns to cache at runtime
135146
* (the threshold is 65536), assuming that arbitrary permutations of patterns
136147
* are coming in, with little chance for encountering a recurring pattern.
148+
* @since 4.0.1
137149
* @see #getStringMatcher(String)
138150
*/
139151
public void setCachePatterns(boolean cachePatterns) {
@@ -363,7 +375,7 @@ protected AntPathStringMatcher getStringMatcher(String pattern) {
363375
matcher = this.stringMatcherCache.get(pattern);
364376
}
365377
if (matcher == null) {
366-
matcher = new AntPathStringMatcher(pattern);
378+
matcher = new AntPathStringMatcher(pattern, this.caseSensitive);
367379
if (cachePatterns == null && this.stringMatcherCache.size() >= CACHE_TURNOFF_THRESHOLD) {
368380
// Try to adapt to the runtime situation that we're encountering:
369381
// There are obviously too many different patterns coming in here...
@@ -418,7 +430,9 @@ public String extractPathWithinPattern(String pattern, String path) {
418430
public Map<String, String> extractUriTemplateVariables(String pattern, String path) {
419431
Map<String, String> variables = new LinkedHashMap<String, String>();
420432
boolean result = doMatch(pattern, path, true, variables);
421-
Assert.state(result, "Pattern \"" + pattern + "\" is not a match for \"" + path + "\"");
433+
if (!result) {
434+
throw new IllegalStateException("Pattern \"" + pattern + "\" is not a match for \"" + path + "\"");
435+
}
422436
return variables;
423437
}
424438

@@ -553,12 +567,16 @@ protected static class AntPathStringMatcher {
553567
private final List<String> variableNames = new LinkedList<String>();
554568

555569
public AntPathStringMatcher(String pattern) {
570+
this(pattern, true);
571+
}
572+
573+
public AntPathStringMatcher(String pattern, boolean caseSensitive) {
556574
StringBuilder patternBuilder = new StringBuilder();
557-
Matcher m = GLOB_PATTERN.matcher(pattern);
575+
Matcher matcher = GLOB_PATTERN.matcher(pattern);
558576
int end = 0;
559-
while (m.find()) {
560-
patternBuilder.append(quote(pattern, end, m.start()));
561-
String match = m.group();
577+
while (matcher.find()) {
578+
patternBuilder.append(quote(pattern, end, matcher.start()));
579+
String match = matcher.group();
562580
if ("?".equals(match)) {
563581
patternBuilder.append('.');
564582
}
@@ -569,7 +587,7 @@ else if (match.startsWith("{") && match.endsWith("}")) {
569587
int colonIdx = match.indexOf(':');
570588
if (colonIdx == -1) {
571589
patternBuilder.append(DEFAULT_VARIABLE_PATTERN);
572-
this.variableNames.add(m.group(1));
590+
this.variableNames.add(matcher.group(1));
573591
}
574592
else {
575593
String variablePattern = match.substring(colonIdx + 1, match.length() - 1);
@@ -580,10 +598,11 @@ else if (match.startsWith("{") && match.endsWith("}")) {
580598
this.variableNames.add(variableName);
581599
}
582600
}
583-
end = m.end();
601+
end = matcher.end();
584602
}
585603
patternBuilder.append(quote(pattern, end, pattern.length()));
586-
this.pattern = Pattern.compile(patternBuilder.toString());
604+
this.pattern = (caseSensitive ? Pattern.compile(patternBuilder.toString()) :
605+
Pattern.compile(patternBuilder.toString(), Pattern.CASE_INSENSITIVE));
587606
}
588607

589608
private String quote(String s, int start, int end) {
@@ -602,10 +621,12 @@ public boolean matchStrings(String str, Map<String, String> uriTemplateVariables
602621
if (matcher.matches()) {
603622
if (uriTemplateVariables != null) {
604623
// SPR-8455
605-
Assert.isTrue(this.variableNames.size() == matcher.groupCount(),
606-
"The number of capturing groups in the pattern segment " + this.pattern +
607-
" does not match the number of URI template variables it defines, which can occur if " +
608-
" capturing groups are used in a URI template regex. Use non-capturing groups instead.");
624+
if (this.variableNames.size() != matcher.groupCount()) {
625+
throw new IllegalArgumentException("The number of capturing groups in the pattern segment " +
626+
this.pattern + " does not match the number of URI template variables it defines, " +
627+
"which can occur if capturing groups are used in a URI template regex. " +
628+
"Use non-capturing groups instead.");
629+
}
609630
for (int i = 1; i <= matcher.groupCount(); i++) {
610631
String name = this.variableNames.get(i - 1);
611632
String value = matcher.group(i);

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

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -298,7 +298,7 @@ public void extractPathWithinPattern() throws Exception {
298298
assertEquals("/docs/commit.html", pathMatcher.extractPathWithinPattern("*.html", "/docs/commit.html"));
299299
assertEquals("/docs/commit.html", pathMatcher.extractPathWithinPattern("**/*.*", "/docs/commit.html"));
300300
assertEquals("/docs/commit.html", pathMatcher.extractPathWithinPattern("*", "/docs/commit.html"));
301-
//SPR-10515
301+
// SPR-10515
302302
assertEquals("/docs/cvs/other/commit.html", pathMatcher.extractPathWithinPattern("**/commit.html", "/docs/cvs/other/commit.html"));
303303
assertEquals("cvs/other/commit.html", pathMatcher.extractPathWithinPattern("/docs/**/commit.html", "/docs/cvs/other/commit.html"));
304304
assertEquals("cvs/other/commit.html", pathMatcher.extractPathWithinPattern("/docs/**/**/**/**", "/docs/cvs/other/commit.html"));
@@ -452,7 +452,7 @@ public void patternComparator() {
452452
assertEquals(-1, comparator.compare("/hotels/{hotel}/booking", "/hotels/{hotel}/bookings/{booking}"));
453453
assertEquals(1, comparator.compare("/hotels/{hotel}/bookings/{booking}", "/hotels/{hotel}/booking"));
454454

455-
//SPR-10550
455+
// SPR-10550
456456
assertEquals(-1, comparator.compare("/hotels/{hotel}/bookings/{booking}/cutomers/{customer}", "/**"));
457457
assertEquals(1, comparator.compare("/**","/hotels/{hotel}/bookings/{booking}/cutomers/{customer}"));
458458
assertEquals(0, comparator.compare("/**","/**"));
@@ -466,21 +466,21 @@ public void patternComparator() {
466466
assertEquals(-1, comparator.compare("/hotels/new", "/hotels/new.*"));
467467
assertEquals(2, comparator.compare("/hotels/{hotel}", "/hotels/{hotel}.*"));
468468

469-
//SPR-6741
469+
// SPR-6741
470470
assertEquals(-1, comparator.compare("/hotels/{hotel}/bookings/{booking}/cutomers/{customer}", "/hotels/**"));
471471
assertEquals(1, comparator.compare("/hotels/**", "/hotels/{hotel}/bookings/{booking}/cutomers/{customer}"));
472472
assertEquals(1, comparator.compare("/hotels/foo/bar/**", "/hotels/{hotel}"));
473473
assertEquals(-1, comparator.compare("/hotels/{hotel}", "/hotels/foo/bar/**"));
474474
assertEquals(2, comparator.compare("/hotels/**/bookings/**", "/hotels/**"));
475475
assertEquals(-2, comparator.compare("/hotels/**", "/hotels/**/bookings/**"));
476476

477-
//SPR-8683
477+
// SPR-8683
478478
assertEquals(1, comparator.compare("/**", "/hotels/{hotel}"));
479479

480480
// longer is better
481481
assertEquals(1, comparator.compare("/hotels", "/hotels2"));
482482

483-
//SPR-13139
483+
// SPR-13139
484484
assertEquals(-1, comparator.compare("*", "*/**"));
485485
assertEquals(1, comparator.compare("*/**", "*"));
486486
}
@@ -581,15 +581,22 @@ public void patternComparatorSort() {
581581
paths.clear();
582582
}
583583

584-
/**
585-
* SPR-8687
586-
*/
587-
@Test
584+
@Test // SPR-8687
588585
public void trimTokensOff() {
589586
pathMatcher.setTrimTokens(false);
590587

591588
assertTrue(pathMatcher.match("/group/{groupName}/members", "/group/sales/members"));
592589
assertTrue(pathMatcher.match("/group/{groupName}/members", "/group/ sales/members"));
590+
assertFalse(pathMatcher.match("/group/{groupName}/members", "/Group/ Sales/Members"));
591+
}
592+
593+
@Test // SPR-13286
594+
public void caseInsensitive() {
595+
pathMatcher.setCaseSensitive(false);
596+
597+
assertTrue(pathMatcher.match("/group/{groupName}/members", "/group/sales/members"));
598+
assertTrue(pathMatcher.match("/group/{groupName}/members", "/Group/Sales/Members"));
599+
assertTrue(pathMatcher.match("/Group/{groupName}/Members", "/group/Sales/members"));
593600
}
594601

595602
@Test

0 commit comments

Comments
 (0)