diff --git a/spring-core/src/main/java/org/springframework/util/AntPathMatcher.java b/spring-core/src/main/java/org/springframework/util/AntPathMatcher.java
index 3132b1c46e0..635f1234f1d 100644
--- a/spring-core/src/main/java/org/springframework/util/AntPathMatcher.java
+++ b/spring-core/src/main/java/org/springframework/util/AntPathMatcher.java
@@ -79,6 +79,8 @@ public class AntPathMatcher implements PathMatcher {
private PathSeparatorPatternCache pathSeparatorPatternCache;
+ private boolean caseSensitive = true;
+
private boolean trimTokens = true;
private volatile Boolean cachePatterns;
@@ -117,6 +119,15 @@ public class AntPathMatcher implements PathMatcher {
this.pathSeparatorPatternCache = new PathSeparatorPatternCache(this.pathSeparator);
}
+ /**
+ * Specify whether to perform pattern matching in a case-sensitive fashion.
+ *
Default is {@code true}. Switch this to {@code false} for case-insensitive matching.
+ * @since 4.2
+ */
+ public void setCaseSensitive(boolean caseSensitive) {
+ this.caseSensitive = caseSensitive;
+ }
+
/**
* Specify whether to trim tokenized paths and patterns.
*
Default is {@code true}.
@@ -134,6 +145,7 @@ public class AntPathMatcher implements PathMatcher {
* turn it off when encountering too many patterns to cache at runtime
* (the threshold is 65536), assuming that arbitrary permutations of patterns
* are coming in, with little chance for encountering a recurring pattern.
+ * @since 4.0.1
* @see #getStringMatcher(String)
*/
public void setCachePatterns(boolean cachePatterns) {
@@ -363,7 +375,7 @@ public class AntPathMatcher implements PathMatcher {
matcher = this.stringMatcherCache.get(pattern);
}
if (matcher == null) {
- matcher = new AntPathStringMatcher(pattern);
+ matcher = new AntPathStringMatcher(pattern, this.caseSensitive);
if (cachePatterns == null && this.stringMatcherCache.size() >= CACHE_TURNOFF_THRESHOLD) {
// Try to adapt to the runtime situation that we're encountering:
// There are obviously too many different patterns coming in here...
@@ -418,7 +430,9 @@ public class AntPathMatcher implements PathMatcher {
public Map extractUriTemplateVariables(String pattern, String path) {
Map variables = new LinkedHashMap();
boolean result = doMatch(pattern, path, true, variables);
- Assert.state(result, "Pattern \"" + pattern + "\" is not a match for \"" + path + "\"");
+ if (!result) {
+ throw new IllegalStateException("Pattern \"" + pattern + "\" is not a match for \"" + path + "\"");
+ }
return variables;
}
@@ -553,12 +567,16 @@ public class AntPathMatcher implements PathMatcher {
private final List variableNames = new LinkedList();
public AntPathStringMatcher(String pattern) {
+ this(pattern, true);
+ }
+
+ public AntPathStringMatcher(String pattern, boolean caseSensitive) {
StringBuilder patternBuilder = new StringBuilder();
- Matcher m = GLOB_PATTERN.matcher(pattern);
+ Matcher matcher = GLOB_PATTERN.matcher(pattern);
int end = 0;
- while (m.find()) {
- patternBuilder.append(quote(pattern, end, m.start()));
- String match = m.group();
+ while (matcher.find()) {
+ patternBuilder.append(quote(pattern, end, matcher.start()));
+ String match = matcher.group();
if ("?".equals(match)) {
patternBuilder.append('.');
}
@@ -569,7 +587,7 @@ public class AntPathMatcher implements PathMatcher {
int colonIdx = match.indexOf(':');
if (colonIdx == -1) {
patternBuilder.append(DEFAULT_VARIABLE_PATTERN);
- this.variableNames.add(m.group(1));
+ this.variableNames.add(matcher.group(1));
}
else {
String variablePattern = match.substring(colonIdx + 1, match.length() - 1);
@@ -580,10 +598,11 @@ public class AntPathMatcher implements PathMatcher {
this.variableNames.add(variableName);
}
}
- end = m.end();
+ end = matcher.end();
}
patternBuilder.append(quote(pattern, end, pattern.length()));
- this.pattern = Pattern.compile(patternBuilder.toString());
+ this.pattern = (caseSensitive ? Pattern.compile(patternBuilder.toString()) :
+ Pattern.compile(patternBuilder.toString(), Pattern.CASE_INSENSITIVE));
}
private String quote(String s, int start, int end) {
@@ -602,10 +621,12 @@ public class AntPathMatcher implements PathMatcher {
if (matcher.matches()) {
if (uriTemplateVariables != null) {
// SPR-8455
- Assert.isTrue(this.variableNames.size() == matcher.groupCount(),
- "The number of capturing groups in the pattern segment " + this.pattern +
- " does not match the number of URI template variables it defines, which can occur if " +
- " capturing groups are used in a URI template regex. Use non-capturing groups instead.");
+ if (this.variableNames.size() != matcher.groupCount()) {
+ throw new IllegalArgumentException("The number of capturing groups in the pattern segment " +
+ this.pattern + " does not match the number of URI template variables it defines, " +
+ "which can occur if capturing groups are used in a URI template regex. " +
+ "Use non-capturing groups instead.");
+ }
for (int i = 1; i <= matcher.groupCount(); i++) {
String name = this.variableNames.get(i - 1);
String value = matcher.group(i);
diff --git a/spring-core/src/test/java/org/springframework/util/AntPathMatcherTests.java b/spring-core/src/test/java/org/springframework/util/AntPathMatcherTests.java
index 8cbd3cd4db8..29a2486a290 100644
--- a/spring-core/src/test/java/org/springframework/util/AntPathMatcherTests.java
+++ b/spring-core/src/test/java/org/springframework/util/AntPathMatcherTests.java
@@ -298,7 +298,7 @@ public class AntPathMatcherTests {
assertEquals("/docs/commit.html", pathMatcher.extractPathWithinPattern("*.html", "/docs/commit.html"));
assertEquals("/docs/commit.html", pathMatcher.extractPathWithinPattern("**/*.*", "/docs/commit.html"));
assertEquals("/docs/commit.html", pathMatcher.extractPathWithinPattern("*", "/docs/commit.html"));
- //SPR-10515
+ // SPR-10515
assertEquals("/docs/cvs/other/commit.html", pathMatcher.extractPathWithinPattern("**/commit.html", "/docs/cvs/other/commit.html"));
assertEquals("cvs/other/commit.html", pathMatcher.extractPathWithinPattern("/docs/**/commit.html", "/docs/cvs/other/commit.html"));
assertEquals("cvs/other/commit.html", pathMatcher.extractPathWithinPattern("/docs/**/**/**/**", "/docs/cvs/other/commit.html"));
@@ -452,7 +452,7 @@ public class AntPathMatcherTests {
assertEquals(-1, comparator.compare("/hotels/{hotel}/booking", "/hotels/{hotel}/bookings/{booking}"));
assertEquals(1, comparator.compare("/hotels/{hotel}/bookings/{booking}", "/hotels/{hotel}/booking"));
- //SPR-10550
+ // SPR-10550
assertEquals(-1, comparator.compare("/hotels/{hotel}/bookings/{booking}/cutomers/{customer}", "/**"));
assertEquals(1, comparator.compare("/**","/hotels/{hotel}/bookings/{booking}/cutomers/{customer}"));
assertEquals(0, comparator.compare("/**","/**"));
@@ -466,7 +466,7 @@ public class AntPathMatcherTests {
assertEquals(-1, comparator.compare("/hotels/new", "/hotels/new.*"));
assertEquals(2, comparator.compare("/hotels/{hotel}", "/hotels/{hotel}.*"));
- //SPR-6741
+ // SPR-6741
assertEquals(-1, comparator.compare("/hotels/{hotel}/bookings/{booking}/cutomers/{customer}", "/hotels/**"));
assertEquals(1, comparator.compare("/hotels/**", "/hotels/{hotel}/bookings/{booking}/cutomers/{customer}"));
assertEquals(1, comparator.compare("/hotels/foo/bar/**", "/hotels/{hotel}"));
@@ -474,13 +474,13 @@ public class AntPathMatcherTests {
assertEquals(2, comparator.compare("/hotels/**/bookings/**", "/hotels/**"));
assertEquals(-2, comparator.compare("/hotels/**", "/hotels/**/bookings/**"));
- //SPR-8683
+ // SPR-8683
assertEquals(1, comparator.compare("/**", "/hotels/{hotel}"));
// longer is better
assertEquals(1, comparator.compare("/hotels", "/hotels2"));
- //SPR-13139
+ // SPR-13139
assertEquals(-1, comparator.compare("*", "*/**"));
assertEquals(1, comparator.compare("*/**", "*"));
}
@@ -581,15 +581,22 @@ public class AntPathMatcherTests {
paths.clear();
}
- /**
- * SPR-8687
- */
- @Test
+ @Test // SPR-8687
public void trimTokensOff() {
pathMatcher.setTrimTokens(false);
assertTrue(pathMatcher.match("/group/{groupName}/members", "/group/sales/members"));
assertTrue(pathMatcher.match("/group/{groupName}/members", "/group/ sales/members"));
+ assertFalse(pathMatcher.match("/group/{groupName}/members", "/Group/ Sales/Members"));
+ }
+
+ @Test // SPR-13286
+ public void caseInsensitive() {
+ pathMatcher.setCaseSensitive(false);
+
+ assertTrue(pathMatcher.match("/group/{groupName}/members", "/group/sales/members"));
+ assertTrue(pathMatcher.match("/group/{groupName}/members", "/Group/Sales/Members"));
+ assertTrue(pathMatcher.match("/Group/{groupName}/Members", "/group/Sales/members"));
}
@Test