From 976f920db2773864e657ff5e5f6c0cf71f27665a Mon Sep 17 00:00:00 2001 From: Arjen Poutsma Date: Mon, 7 Dec 2009 13:59:07 +0000 Subject: [PATCH] SPR-6482 - @RequestMapping handled incorrectly when value contains "." (dot) character --- .../springframework/util/AntPathMatcher.java | 47 +++++++++++++++++-- .../util/AntPathStringMatcher.java | 2 +- .../util/AntPathMatcherTests.java | 14 ++++-- ...plateServletAnnotationControllerTests.java | 2 +- 4 files changed, 55 insertions(+), 10 deletions(-) diff --git a/org.springframework.core/src/main/java/org/springframework/util/AntPathMatcher.java b/org.springframework.core/src/main/java/org/springframework/util/AntPathMatcher.java index ac9a524b8e4..cd404f602b2 100644 --- a/org.springframework.core/src/main/java/org/springframework/util/AntPathMatcher.java +++ b/org.springframework.core/src/main/java/org/springframework/util/AntPathMatcher.java @@ -19,6 +19,8 @@ package org.springframework.util; import java.util.Comparator; import java.util.LinkedHashMap; import java.util.Map; +import java.util.regex.Pattern; +import java.util.regex.Matcher; /** * PathMatcher implementation for Ant-style path patterns. Examples are provided below. @@ -44,6 +46,8 @@ import java.util.Map; */ public class AntPathMatcher implements PathMatcher { + private static final Pattern VARIABLE_PATTERN = Pattern.compile("\\{[^/]+?\\}"); + /** Default path separator: "/" */ public static final String DEFAULT_PATH_SEPARATOR = "/"; @@ -396,23 +400,56 @@ public class AntPathMatcher implements PathMatcher { else if (pattern2EqualsPath) { return 1; } - int wildCardCount1 = StringUtils.countOccurrencesOf(pattern1, "*"); - int wildCardCount2 = StringUtils.countOccurrencesOf(pattern2, "*"); + int wildCardCount1 = getWildCardCount(pattern1); + int wildCardCount2 = getWildCardCount(pattern2); + + int bracketCount1 = StringUtils.countOccurrencesOf(pattern1, "{"); + int bracketCount2 = StringUtils.countOccurrencesOf(pattern2, "{"); + + int totalCount1 = wildCardCount1 + bracketCount1; + int totalCount2 = wildCardCount2 + bracketCount2; + + if (totalCount1 != totalCount2) { + return totalCount1 - totalCount2; + } + + int pattern1Length = getPatternLength(pattern1); + int pattern2Length = getPatternLength(pattern2); + + if (pattern1Length != pattern2Length) { + return pattern2Length - pattern1Length; + } + if (wildCardCount1 < wildCardCount2) { return -1; } else if (wildCardCount2 < wildCardCount1) { return 1; } - int bracketCount1 = StringUtils.countOccurrencesOf(pattern1, "{"); - int bracketCount2 = StringUtils.countOccurrencesOf(pattern2, "{"); + if (bracketCount1 < bracketCount2) { return -1; } else if (bracketCount2 < bracketCount1) { return 1; } - return pattern2.length() - pattern1.length(); + + return 0; + } + + private int getWildCardCount(String pattern) { + if (pattern.endsWith(".*")) { + pattern = pattern.substring(0, pattern.length() - 2); + } + return StringUtils.countOccurrencesOf(pattern, "*"); + } + + /** + * Returns the length of the given pattern, where template variables are considered to be 1 long. + */ + private int getPatternLength(String pattern) { + Matcher m = VARIABLE_PATTERN.matcher(pattern); + return m.replaceAll("#").length(); } } diff --git a/org.springframework.core/src/main/java/org/springframework/util/AntPathStringMatcher.java b/org.springframework.core/src/main/java/org/springframework/util/AntPathStringMatcher.java index 6f9bf252669..1f528ba77cb 100644 --- a/org.springframework.core/src/main/java/org/springframework/util/AntPathStringMatcher.java +++ b/org.springframework.core/src/main/java/org/springframework/util/AntPathStringMatcher.java @@ -36,7 +36,7 @@ class AntPathStringMatcher { private static final Pattern GLOB_PATTERN = Pattern.compile("\\?|\\*|\\{([^/]+?)\\}"); - private static final String DEFAULT_VARIABLE_PATTERN = "([^\\.]*)"; + private static final String DEFAULT_VARIABLE_PATTERN = "(.*)"; private final Pattern pattern; diff --git a/org.springframework.core/src/test/java/org/springframework/util/AntPathMatcherTests.java b/org.springframework.core/src/test/java/org/springframework/util/AntPathMatcherTests.java index f662306d285..1b27d5e3a58 100644 --- a/org.springframework.core/src/test/java/org/springframework/util/AntPathMatcherTests.java +++ b/org.springframework.core/src/test/java/org/springframework/util/AntPathMatcherTests.java @@ -393,8 +393,8 @@ public class AntPathMatcherTests { assertEquals(-1, comparator.compare("/hotels/{hotel}", "/hotels/*")); assertEquals(1, comparator.compare("/hotels/*", "/hotels/{hotel}")); - assertEquals(-1, comparator.compare("/hotels/*", "/hotels/*/**")); - assertEquals(1, comparator.compare("/hotels/*/**", "/hotels/*")); + assertEquals(-2, comparator.compare("/hotels/*", "/hotels/*/**")); + assertEquals(2, comparator.compare("/hotels/*/**", "/hotels/*")); assertEquals(-1, comparator.compare("/hotels/new", "/hotels/new.*")); @@ -466,7 +466,6 @@ public class AntPathMatcherTests { paths.add("/hotels/*"); paths.add("/hotels/{hotel}"); paths.add("/hotels/new"); - Collections.shuffle(paths); Collections.sort(paths, comparator); assertEquals("/hotels/new", paths.get(0)); assertEquals("/hotels/{hotel}", paths.get(1)); @@ -480,6 +479,15 @@ public class AntPathMatcherTests { assertEquals("/hotels/ne*", paths.get(0)); assertEquals("/hotels/n*", paths.get(1)); paths.clear(); + + comparator = pathMatcher.getPatternComparator("/hotels/new.html"); + paths.add("/hotels/new.*"); + paths.add("/hotels/{hotel}"); + Collections.shuffle(paths); + Collections.sort(paths, comparator); + assertEquals("/hotels/new.*", paths.get(0)); + assertEquals("/hotels/{hotel}", paths.get(1)); + paths.clear(); } } diff --git a/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/annotation/UriTemplateServletAnnotationControllerTests.java b/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/annotation/UriTemplateServletAnnotationControllerTests.java index 6d2c265734c..0d900cf0952 100644 --- a/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/annotation/UriTemplateServletAnnotationControllerTests.java +++ b/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/annotation/UriTemplateServletAnnotationControllerTests.java @@ -398,7 +398,7 @@ public class UriTemplateServletAnnotationControllerTests { @RequestMapping("hotels") public static class ImplicitSubPathController { - @RequestMapping("{hotel:.*}") + @RequestMapping("{hotel}") public void handleHotel(@PathVariable String hotel, Writer writer) throws IOException { writer.write("test-" + hotel); }