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 226cbd34464..81a3539b368 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 @@ -21,34 +21,20 @@ import java.util.LinkedHashMap; import java.util.Map; /** - * PathMatcher implementation for Ant-style path patterns. - * Examples are provided below. + * PathMatcher implementation for Ant-style path patterns. Examples are provided below. * - *

Part of this mapping code has been kindly borrowed from - * Apache Ant. + *

Part of this mapping code has been kindly borrowed from Apache Ant. * - *

The mapping matches URLs using the following rules:
- *

+ *

The mapping matches URLs using the following rules:

* - *

Some examples:
- *

+ *

Some examples:

* * @author Alef Arendsen * @author Juergen Hoeller @@ -63,16 +49,11 @@ public class AntPathMatcher implements PathMatcher { private String pathSeparator = DEFAULT_PATH_SEPARATOR; - - /** - * Set the path separator to use for pattern parsing. - * Default is "/", as in Ant. - */ + /** Set the path separator to use for pattern parsing. Default is "/", as in Ant. */ public void setPathSeparator(String pathSeparator) { this.pathSeparator = (pathSeparator != null ? pathSeparator : DEFAULT_PATH_SEPARATOR); } - public boolean isPattern(String path) { return (path.indexOf('*') != -1 || path.indexOf('?') != -1); } @@ -85,17 +66,19 @@ public class AntPathMatcher implements PathMatcher { return doMatch(pattern, path, false, null); } - /** * Actually match the given path against the given pattern. + * * @param pattern the pattern to match against * @param path the path String to test - * @param fullMatch whether a full pattern match is required - * (else a pattern match as far as the given base path goes is sufficient) - * @return true if the supplied path matched, - * false if it didn't + * @param fullMatch whether a full pattern match is required (else a pattern match as far as the given base path goes + * is sufficient) + * @return true if the supplied path matched, false if it didn't */ - protected boolean doMatch(String pattern, String path, boolean fullMatch, Map uriTemplateVariables) { + protected boolean doMatch(String pattern, + String path, + boolean fullMatch, + Map uriTemplateVariables) { if (path.startsWith(this.pathSeparator) != pattern.startsWith(this.pathSeparator)) { return false; } @@ -221,16 +204,12 @@ public class AntPathMatcher implements PathMatcher { } /** - * Tests whether or not a string matches against a pattern. - * The pattern may contain two special characters:
- * '*' means zero or more characters
- * '?' means one and only one character - * @param pattern pattern to match against. - * Must not be null. - * @param str string which must be matched against the pattern. - * Must not be null. - * @return true if the string matches against the - * pattern, or false otherwise. + * Tests whether or not a string matches against a pattern. The pattern may contain two special characters:
'*' + * means zero or more characters
'?' means one and only one character + * + * @param pattern pattern to match against. Must not be null. + * @param str string which must be matched against the pattern. Must not be null. + * @return true if the string matches against the pattern, or false otherwise. */ private boolean matchStrings(String pattern, String str, Map uriTemplateVariables) { AntPatchStringMatcher matcher = new AntPatchStringMatcher(pattern, str, uriTemplateVariables); @@ -238,9 +217,7 @@ public class AntPathMatcher implements PathMatcher { } /** - * Given a pattern and a full path, determine the pattern-mapped part. - *

For example: - *

    + * Given a pattern and a full path, determine the pattern-mapped part.

    For example:

      *
    • '/docs/cvs/commit.html' and '/docs/cvs/commit.html -> ''
    • *
    • '/docs/*' and '/docs/cvs/commit -> 'cvs/commit'
    • *
    • '/docs/cvs/*.html' and '/docs/cvs/commit.html -> 'commit.html'
    • @@ -248,10 +225,9 @@ public class AntPathMatcher implements PathMatcher { *
    • '/docs/**\/*.html' and '/docs/cvs/commit.html -> 'cvs/commit.html'
    • *
    • '/*.html' and '/docs/cvs/commit.html -> 'docs/cvs/commit.html'
    • *
    • '*.html' and '/docs/cvs/commit.html -> '/docs/cvs/commit.html'
    • - *
    • '*' and '/docs/cvs/commit.html -> '/docs/cvs/commit.html'
    • - *
    - *

    Assumes that {@link #match} returns true for 'pattern' - * and 'path', but does not enforce this. + *

  • '*' and '/docs/cvs/commit.html -> '/docs/cvs/commit.html'
+ *

Assumes that {@link #match} returns true for 'pattern' and 'path', but + * does not enforce this. */ public String extractPathWithinPattern(String pattern, String path) { String[] patternParts = StringUtils.tokenizeToStringArray(pattern, this.pathSeparator); @@ -291,26 +267,17 @@ public class AntPathMatcher implements PathMatcher { } /** - * Combines two patterns into a new pattern that is returned. - *

This implementation simply concatenates the two patterns, unless the - * first pattern contains a file extension match (such as {@code *.html}. In - * that case, the second pattern should be included in the first, or an - * {@code IllegalArgumentException} is thrown. - *

For example: - * - * - * - * - * - * - * - * - * - * + * Combines two patterns into a new pattern that is returned.

This implementation simply concatenates the two + * patterns, unless the first pattern contains a file extension match (such as {@code *.html}. In that case, the second + * pattern should be included in the first, or an {@code IllegalArgumentException} is thrown.

For example:

Pattern 1Pattern 2Result
/hotels{@code null}/hotels
{@code null}/hotels/hotels
/hotels/bookings/hotels/bookings
/hotelsbookings/hotels/bookings
/hotels/*/bookings/hotels/bookings
/hotels/**/bookings/hotels/**/bookings
/hotels{hotel}/hotels/{hotel}
/hotels/*{hotel}/hotels/{hotel}
+ * + * + * + * * - * - * - *
Pattern 1Pattern 2Result
/hotels{@code + * null}/hotels
{@code null}/hotels/hotels
/hotels/bookings/hotels/bookings
/hotelsbookings/hotels/bookings
/hotels/*/bookings/hotels/bookings
/hotels/**/bookings/hotels/**/bookings
/hotels{hotel}/hotels/{hotel}
/hotels/*{hotel}/hotels/{hotel}
/hotels/**{hotel}/hotels/**/{hotel}
/*.html/hotels.html/hotels.html
/*.html/hotelsIllegalArgumentException
+ * /*.html/hotels.html/hotels.html /*.html/hotels/hotels.html + * /*.html/*.txtIllegalArgumentException * * @param pattern1 the first pattern * @param pattern2 the second pattern @@ -327,6 +294,9 @@ public class AntPathMatcher implements PathMatcher { else if (!StringUtils.hasText(pattern2)) { return pattern1; } + else if (match(pattern1, pattern2)) { + return pattern2; + } else if (pattern1.endsWith("/*")) { if (pattern2.startsWith("/")) { // /hotels/* + /booking -> /hotels/booking @@ -348,9 +318,10 @@ public class AntPathMatcher implements PathMatcher { } } else { - int idx = pattern1.indexOf("."); - if (idx == -1) { - // all other cases: simply concatenate the two patterns + int dotPos1 = pattern1.indexOf('.'); + int dotPos2 = pattern2.indexOf('.'); + if (dotPos1 == -1 && dotPos2 == -1) { + // simply concatenate the two patterns if (pattern1.endsWith("/") || pattern2.startsWith("/")) { return pattern1 + pattern2; } @@ -358,17 +329,28 @@ public class AntPathMatcher implements PathMatcher { return pattern1 + "/" + pattern2; } } - else { - // /*.html + /hotels.html -> /hotels.html - if (match(pattern1, pattern2)) { - return pattern2; - } - else { - // /*.html + /hotels -> exception - throw new IllegalArgumentException( - "Conflicting paths: \"" + pattern1 + "\" does not include \"" + pattern2 + "\""); - } + String fileName1 = ""; + String extension1 = ""; + if (dotPos1 != -1) { + fileName1 = pattern1.substring(0, dotPos1); + extension1 = pattern1.substring(dotPos1); } + else { + fileName1 = pattern1; + } + String fileName2 = ""; + String extension2 = ""; + if (dotPos2 != -1) { + fileName2 = pattern2.substring(0, dotPos2); + extension2 = pattern2.substring(dotPos2); + } + else { + fileName2 = pattern2; + } + String fileName = fileName1.endsWith("*") ? fileName2 : fileName2; + String extension = extension1.startsWith("*") ? extension2 : extension1; + + return fileName + extension; } } 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 40797265f8d..4bf087e3b36 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 @@ -350,27 +350,9 @@ public class AntPathMatcherTests { assertEquals("/hotels/{hotel}", pathMatcher.combine("/hotels", "{hotel}")); assertEquals("/hotels/*/booking/{booking}", pathMatcher.combine("/hotels/*/booking", "{booking}")); assertEquals("/hotel.html", pathMatcher.combine("/*.html", "/hotel.html")); - try { - String result = pathMatcher.combine("/*.html", "/hotel"); - fail("IllegalArgumentException expected; got " + result); - } - catch (IllegalArgumentException ex) { - // expected - } - try { - String result = pathMatcher.combine("/*.html", "/*.txt"); - fail("IllegalArgumentException expected; got " + result); - } - catch (IllegalArgumentException ex) { - // expected - } - try { - String result = pathMatcher.combine("/hotel.html", "/bookings.html"); - fail("IllegalArgumentException expected; got " + result); - } - catch (IllegalArgumentException ex) { - // expected - } + assertEquals("/hotel.html", pathMatcher.combine("/*.html", "/hotel")); + assertEquals("/hotel.html", pathMatcher.combine("/*.html", "/hotel.*")); + assertEquals("/*.html", pathMatcher.combine("/*.*", "/*.html")); } @Test