diff --git a/spring-web/src/main/java/org/springframework/web/util/pattern/CaptureVariablePathElement.java b/spring-web/src/main/java/org/springframework/web/util/pattern/CaptureVariablePathElement.java index 3c30085e1b..db2daaec01 100644 --- a/spring-web/src/main/java/org/springframework/web/util/pattern/CaptureVariablePathElement.java +++ b/spring-web/src/main/java/org/springframework/web/util/pattern/CaptureVariablePathElement.java @@ -103,7 +103,7 @@ class CaptureVariablePathElement extends PathElement { else { // Needs to be at least one character #SPR15264 match = (pathIndex == matchingContext.pathLength); - if (!match && matchingContext.isAllowOptionalTrailingSlash()) { + if (!match && matchingContext.isMatchOptionalTrailingSeparator()) { match = //(nextPos > candidateIndex) && (pathIndex + 1) == matchingContext.pathLength && matchingContext.isSeparator(pathIndex); diff --git a/spring-web/src/main/java/org/springframework/web/util/pattern/InternalPathPatternParser.java b/spring-web/src/main/java/org/springframework/web/util/pattern/InternalPathPatternParser.java index 082a824fe3..6c0e1d3972 100644 --- a/spring-web/src/main/java/org/springframework/web/util/pattern/InternalPathPatternParser.java +++ b/spring-web/src/main/java/org/springframework/web/util/pattern/InternalPathPatternParser.java @@ -35,9 +35,6 @@ class InternalPathPatternParser { private final PathPatternParser parser; - // The expected path separator to split path elements during parsing - private final char separator = '/'; - // The input data for parsing private char[] pathPatternData = new char[0]; @@ -64,37 +61,35 @@ class InternalPathPatternParser { private int variableCaptureCount = 0; // Start of the most recent path element in a particular path element - int pathElementStart; + private int pathElementStart; // Start of the most recent variable capture in a particular path element - int variableCaptureStart; + private int variableCaptureStart; // Variables captures in this path pattern @Nullable - List capturedVariableNames; + private List capturedVariableNames; // The head of the path element chain currently being built @Nullable - PathElement headPE; + private PathElement headPE; // The most recently constructed path element in the chain @Nullable - PathElement currentPE; + private PathElement currentPE; + /** + * Package private constructor for use in {@link PathPatternParser#parse}. + * @param parentParser reference back to the stateless, public parser + */ InternalPathPatternParser(PathPatternParser parentParser) { this.parser = parentParser; } /** - * Process the path pattern data, a character at a time, breaking it into - * path elements around separator boundaries and verifying the structure at each - * stage. Produces a PathPattern object that can be used for fast matching - * against paths. - * @param pathPattern the input path pattern, e.g. /foo/{bar} - * @return a PathPattern for quickly matching paths against the specified path pattern - * @throws PatternParseException in case of parse errors + * Package private delegate for {@link PathPatternParser#parse(String)}. */ public PathPattern parse(String pathPattern) throws PatternParseException { Assert.notNull(pathPattern, "Path pattern must not be null"); @@ -110,16 +105,16 @@ class InternalPathPatternParser { while (this.pos < this.pathPatternLength) { char ch = this.pathPatternData[this.pos]; - if (ch == this.separator) { + if (ch == this.parser.getSeparator()) { if (this.pathElementStart != -1) { pushPathElement(createPathElement()); } if (peekDoubleWildcard()) { - pushPathElement(new WildcardTheRestPathElement(this.pos, this.separator)); + pushPathElement(new WildcardTheRestPathElement(this.pos, this.parser.getSeparator())); this.pos += 2; } else { - pushPathElement(new SeparatorPathElement(this.pos, this.separator)); + pushPathElement(new SeparatorPathElement(this.pos, this.parser.getSeparator())); } } else { @@ -191,8 +186,7 @@ class InternalPathPatternParser { if (this.pathElementStart != -1) { pushPathElement(createPathElement()); } - return new PathPattern(pathPattern, this.parser, this.headPE, this.separator, - this.parser.isCaseSensitive(), this.parser.isMatchOptionalTrailingSlash()); + return new PathPattern(pathPattern, this.parser, this.headPE); } /** @@ -229,7 +223,7 @@ class InternalPathPatternParser { } curlyBracketDepth--; } - if (ch == this.separator && !previousBackslash) { + if (ch == this.parser.getSeparator() && !previousBackslash) { throw new PatternParseException(this.pos, this.pathPatternData, PatternMessage.MISSING_CLOSE_CAPTURE); } @@ -322,13 +316,14 @@ class InternalPathPatternParser { this.pathPatternData[this.pos - 1] == '}') { if (this.isCaptureTheRestVariable) { // It is {*....} - newPE = new CaptureTheRestPathElement(pathElementStart, getPathElementText(), separator); + newPE = new CaptureTheRestPathElement( + this.pathElementStart, getPathElementText(), this.parser.getSeparator()); } else { // It is a full capture of this element (possibly with constraint), for example: /foo/{abc}/ try { newPE = new CaptureVariablePathElement(this.pathElementStart, getPathElementText(), - this.parser.isCaseSensitive(), this.separator); + this.parser.isCaseSensitive(), this.parser.getSeparator()); } catch (PatternSyntaxException pse) { throw new PatternParseException(pse, @@ -346,7 +341,7 @@ class InternalPathPatternParser { } RegexPathElement newRegexSection = new RegexPathElement(this.pathElementStart, getPathElementText(), this.parser.isCaseSensitive(), - this.pathPatternData, this.separator); + this.pathPatternData, this.parser.getSeparator()); for (String variableName : newRegexSection.getVariableNames()) { recordCapturedVariable(this.pathElementStart, variableName); } @@ -356,20 +351,20 @@ class InternalPathPatternParser { else { if (this.wildcard) { if (this.pos - 1 == this.pathElementStart) { - newPE = new WildcardPathElement(this.pathElementStart, this.separator); + newPE = new WildcardPathElement(this.pathElementStart, this.parser.getSeparator()); } else { newPE = new RegexPathElement(this.pathElementStart, getPathElementText(), - this.parser.isCaseSensitive(), this.pathPatternData, this.separator); + this.parser.isCaseSensitive(), this.pathPatternData, this.parser.getSeparator()); } } else if (this.singleCharWildcardCount != 0) { newPE = new SingleCharWildcardedPathElement(this.pathElementStart, getPathElementText(), - this.singleCharWildcardCount, this.parser.isCaseSensitive(), this.separator); + this.singleCharWildcardCount, this.parser.isCaseSensitive(), this.parser.getSeparator()); } else { newPE = new LiteralPathElement(this.pathElementStart, getPathElementText(), - this.parser.isCaseSensitive(), this.separator); + this.parser.isCaseSensitive(), this.parser.getSeparator()); } } diff --git a/spring-web/src/main/java/org/springframework/web/util/pattern/LiteralPathElement.java b/spring-web/src/main/java/org/springframework/web/util/pattern/LiteralPathElement.java index b2656e3792..a6b9b23f58 100644 --- a/spring-web/src/main/java/org/springframework/web/util/pattern/LiteralPathElement.java +++ b/spring-web/src/main/java/org/springframework/web/util/pattern/LiteralPathElement.java @@ -97,7 +97,7 @@ class LiteralPathElement extends PathElement { return true; } else { - return (matchingContext.isAllowOptionalTrailingSlash() && + return (matchingContext.isMatchOptionalTrailingSeparator() && (pathIndex + 1) == matchingContext.pathLength && matchingContext.isSeparator(pathIndex)); } diff --git a/spring-web/src/main/java/org/springframework/web/util/pattern/PathPattern.java b/spring-web/src/main/java/org/springframework/web/util/pattern/PathPattern.java index 22942513cd..d6541d7fc4 100644 --- a/spring-web/src/main/java/org/springframework/web/util/pattern/PathPattern.java +++ b/spring-web/src/main/java/org/springframework/web/util/pattern/PathPattern.java @@ -27,7 +27,6 @@ import org.springframework.http.server.PathContainer.Separator; import org.springframework.lang.Nullable; import org.springframework.util.CollectionUtils; import org.springframework.util.MultiValueMap; -import org.springframework.util.PathMatcher; import org.springframework.util.StringUtils; /** @@ -66,30 +65,32 @@ import org.springframework.util.StringUtils; * * @author Andy Clement * @since 5.0 + * @see PathContainer */ public class PathPattern implements Comparable { private final static PathContainer EMPTY_PATH = PathContainer.parsePath(""); + + /** The text of the parsed pattern */ + private final String patternString; + /** The parser used to construct this pattern */ private final PathPatternParser parser; + /** The separator used when parsing the pattern */ + private final char separator; + + /** If this pattern has no trailing slash, allow candidates to include one and still match successfully */ + private final boolean matchOptionalTrailingSeparator; + + /** Will this match candidates in a case sensitive way? (case sensitivity at parse time) */ + private final boolean caseSensitive; + /** First path element in the parsed chain of path elements for this pattern */ @Nullable private final PathElement head; - /** The text of the parsed pattern */ - private String patternString; - - /** The separator used when parsing the pattern */ - private char separator; - - /** Will this match candidates in a case sensitive way? (case sensitivity at parse time) */ - private boolean caseSensitive; - - /** If this pattern has no trailing slash, allow candidates to include one and still match successfully */ - private boolean allowOptionalTrailingSlash; - /** How many variables are captured in this pattern */ private int capturedVariableCount; @@ -120,15 +121,13 @@ public class PathPattern implements Comparable { private boolean catchAll = false; - PathPattern(String patternText, PathPatternParser parser, @Nullable PathElement head, - char separator, boolean caseSensitive, boolean allowOptionalTrailingSlash) { - + PathPattern(String patternText, PathPatternParser parser, @Nullable PathElement head) { this.patternString = patternText; this.parser = parser; + this.separator = parser.getSeparator(); + this.matchOptionalTrailingSeparator = parser.isMatchOptionalTrailingSeparator(); + this.caseSensitive = parser.isCaseSensitive(); this.head = head; - this.separator = separator; - this.caseSensitive = caseSensitive; - this.allowOptionalTrailingSlash = allowOptionalTrailingSlash; // Compute fields for fast comparison PathElement elem = head; @@ -149,7 +148,7 @@ public class PathPattern implements Comparable { /** - * @return the original pattern string that was parsed to create this PathPattern. + * Return the original String that was parsed to create this PathPattern. */ public String getPatternString() { return this.patternString; @@ -157,8 +156,9 @@ public class PathPattern implements Comparable { /** - * @param pathContainer the candidate path container to attempt to match against this pattern - * @return true if the path matches this pattern + * Whether this pattern matches the given path. + * @param pathContainer the candidate path to attempt to match against + * @return {@code true} if the path matches this pattern */ public boolean matches(PathContainer pathContainer) { if (this.head == null) { @@ -177,10 +177,11 @@ public class PathPattern implements Comparable { } /** - * For a given path return the remaining piece that is not covered by this PathPattern. - * @param pathContainer a path that may or may not match this path pattern - * @return a {@link PathRemainingMatchInfo} describing the match result, - * or {@code null} if the path does not match this pattern + * Match the beginning of the given path and return the remaining portion of + * the path not covered by this pattern. This is useful for matching through + * nested routes where the path is matched incrementally at each level. + * @param pathContainer the candidate path to attempt to match against + * @return info object with the match result or {@code null} for no match */ @Nullable public PathRemainingMatchInfo getPathRemaining(PathContainer pathContainer) { @@ -227,39 +228,48 @@ public class PathPattern implements Comparable { } /** - * @param pathContainer a path that matches this pattern from which to extract variables - * @return a map of extracted variables - an empty map if no variables extracted. + * Match this pattern to the given URI path and extract URI template + * variables as well as path parameters (matrix variables). + * @param pathContainer the candidate path to attempt to match against + * @return info object with the extracted variables * @throws IllegalStateException if the path does not match the pattern */ - public PathMatchResult matchAndExtract(PathContainer pathContainer) { + public PathMatchInfo matchAndExtract(PathContainer pathContainer) { MatchingContext matchingContext = new MatchingContext(pathContainer, true); if (this.head != null && this.head.matches(0, matchingContext)) { return matchingContext.getPathMatchResult(); } else if (!hasLength(pathContainer)) { - return PathMatchResult.EMPTY; + return PathMatchInfo.EMPTY; } else { - throw new IllegalStateException("Pattern \"" + this + "\" is not a match for \"" + pathContainer.value() + "\""); + throw new IllegalStateException( + "Pattern \"" + this + "\" is not a match for \"" + pathContainer.value() + "\""); } } /** - * Given a full path, determine the pattern-mapped part.

For example:

    + * Determine the pattern-mapped part for the given path. + *

    For example:

      *
    • '{@code /docs/cvs/commit.html}' and '{@code /docs/cvs/commit.html} -> ''
    • - *
    • '{@code /docs/*}' and '{@code /docs/cvs/commit} -> '{@code cvs/commit}'
    • + *
    • '{@code /docs/*}' and '{@code /docs/cvs/commit}' -> '{@code cvs/commit}'
    • *
    • '{@code /docs/cvs/*.html}' and '{@code /docs/cvs/commit.html} -> '{@code commit.html}'
    • *
    • '{@code /docs/**}' and '{@code /docs/cvs/commit} -> '{@code cvs/commit}'
    • *
    - *

    Note: Assumes that {@link #matches} returns {@code true} for '{@code pattern}' and '{@code path}', but - * does not enforce this. As per the contract on {@link PathMatcher}, this - * method will trim leading/trailing separators. It will also remove duplicate separators in - * the returned path. + *

    Note: Assumes that {@link #matches} returns {@code true} for + * the same path but does not enforce this. * @param path a path that matches this pattern - * @return the subset of the path that is matched by pattern or "" if none of it is matched by pattern elements + * @return the subset of the path that is matched by pattern or "" if none + * of it is matched by pattern elements */ public PathContainer extractPathWithinPattern(PathContainer path) { + // TODO: implement extractPathWithinPattern for PathContainer + + // TODO: also see this from PathPattern javadoc + align behavior with getPathRemaining instead: + // As per the contract on {@link PathMatcher}, this method will trim leading/trailing + // separators. It will also remove duplicate separators in the returned path. + String result = extractPathWithinPattern(path.value()); return PathContainer.parsePath(result); } @@ -456,16 +466,13 @@ public class PathPattern implements Comparable { /** - * Represents the result of a successful path match. This holds the keys that matched, the - * values that were found for each key and, if any, the path parameters (matrix variables) - * attached to that path element. - * For example: "/{var}" against "/foo;a=b" will return a PathMathResult with 'foo=bar' - * for URI variables and 'a=b' as path parameters for 'foo'. + * Holder for URI variables and path parameters (matrix variables) extracted + * based on the pattern for a given matched path. */ - public static class PathMatchResult { + public static class PathMatchInfo { - private static final PathMatchResult EMPTY = - new PathMatchResult(Collections.emptyMap(), Collections.emptyMap()); + private static final PathMatchInfo EMPTY = + new PathMatchInfo(Collections.emptyMap(), Collections.emptyMap()); private final Map uriVariables; @@ -473,7 +480,7 @@ public class PathPattern implements Comparable { private final Map> matrixVariables; - PathMatchResult(Map uriVars, + PathMatchInfo(Map uriVars, @Nullable Map> matrixVars) { this.uriVariables = Collections.unmodifiableMap(uriVars); @@ -482,39 +489,47 @@ public class PathPattern implements Comparable { } + /** + * Return the extracted URI variables. + */ public Map getUriVariables() { return this.uriVariables; } + /** + * Return maps of matrix variables per path segment, keyed off by URI + * variable name. + */ public Map> getMatrixVariables() { return this.matrixVariables; } @Override public String toString() { - return "PathMatchResult[uriVariables=" + this.uriVariables + ", " + + return "PathMatchInfo[uriVariables=" + this.uriVariables + ", " + "matrixVariables=" + this.matrixVariables + "]"; } } /** - * A holder for the result of a {@link PathPattern#getPathRemaining} call.Holds - * information on the path left after the first part has successfully matched a pattern - * and any variables bound in that first part that matched. + * Holder for the result of a match on the start of a pattern. + * Provides access to the remaining path not matched to the pattern as well + * as any variables bound in that first part that was matched. */ public static class PathRemainingMatchInfo { private final PathContainer pathRemaining; - private final PathMatchResult pathMatchResult; + private final PathMatchInfo pathMatchInfo; + PathRemainingMatchInfo(PathContainer pathRemaining) { - this(pathRemaining, PathMatchResult.EMPTY); + this(pathRemaining, PathMatchInfo.EMPTY); } - PathRemainingMatchInfo(PathContainer pathRemaining, PathMatchResult pathMatchResult) { + PathRemainingMatchInfo(PathContainer pathRemaining, PathMatchInfo pathMatchInfo) { this.pathRemaining = pathRemaining; - this.pathMatchResult = pathMatchResult; + this.pathMatchInfo = pathMatchInfo; } /** @@ -525,18 +540,18 @@ public class PathPattern implements Comparable { } /** - * Return variables that were bound in the part of the path that was successfully matched. - * Will be an empty map if no variables were bound + * Return variables that were bound in the part of the path that was + * successfully matched or an empty map. */ public Map getUriVariables() { - return this.pathMatchResult.getUriVariables(); + return this.pathMatchInfo.getUriVariables(); } /** * Return the path parameters for each bound variable. */ public Map> getMatrixVariables() { - return this.pathMatchResult.getMatrixVariables(); + return this.pathMatchInfo.getMatrixVariables(); } } @@ -635,8 +650,8 @@ public class PathPattern implements Comparable { determineRemainingPath = true; } - public boolean isAllowOptionalTrailingSlash() { - return allowOptionalTrailingSlash; + public boolean isMatchOptionalTrailingSeparator() { + return matchOptionalTrailingSeparator; } public void setMatchStartMatching(boolean b) { @@ -657,12 +672,12 @@ public class PathPattern implements Comparable { } } - public PathMatchResult getPathMatchResult() { + public PathMatchInfo getPathMatchResult() { if (this.extractedUriVariables == null) { - return PathMatchResult.EMPTY; + return PathMatchInfo.EMPTY; } else { - return new PathMatchResult(this.extractedUriVariables, this.extractedMatrixVariables); + return new PathMatchInfo(this.extractedUriVariables, this.extractedMatrixVariables); } } diff --git a/spring-web/src/main/java/org/springframework/web/util/pattern/PathPatternParser.java b/spring-web/src/main/java/org/springframework/web/util/pattern/PathPatternParser.java index 7dfdb06d1f..f2ae9ec277 100644 --- a/spring-web/src/main/java/org/springframework/web/util/pattern/PathPatternParser.java +++ b/spring-web/src/main/java/org/springframework/web/util/pattern/PathPatternParser.java @@ -22,8 +22,8 @@ package org.springframework.web.util.pattern; * *

    The {@link PathPatternParser} and {@link PathPattern} are specifically * designed for use with HTTP URL paths in web applications where a large number - * of URI path patterns continuously matched against incoming requests motivates - * the need for pre-parsing and fast matching. + * of URI path patterns, continuously matched against incoming requests, + * motivates the need for efficient matching. * *

    For details of the path pattern syntax see {@link PathPattern}. * @@ -32,7 +32,7 @@ package org.springframework.web.util.pattern; */ public class PathPatternParser { - private boolean matchOptionalTrailingSlash = true; + private boolean matchOptionalTrailingSeparator = true; private boolean caseSensitive = true; @@ -48,15 +48,15 @@ public class PathPatternParser { * *

    The default is {@code true}. */ - public void setMatchOptionalTrailingSlash(boolean matchOptionalTrailingSlash) { - this.matchOptionalTrailingSlash = matchOptionalTrailingSlash; + public void setMatchOptionalTrailingSeparator(boolean matchOptionalTrailingSeparator) { + this.matchOptionalTrailingSeparator = matchOptionalTrailingSeparator; } /** * Whether optional trailing slashing match is enabled. */ - public boolean isMatchOptionalTrailingSlash() { - return this.matchOptionalTrailingSlash; + public boolean isMatchOptionalTrailingSeparator() { + return this.matchOptionalTrailingSeparator; } /** @@ -74,9 +74,20 @@ public class PathPatternParser { return this.caseSensitive; } + /** + * Accessor used for the separator to use. + *

    Currently not exposed for configuration with URI path patterns and + * mainly for use in InternalPathPatternParser and PathPattern. If required + * in the future, a similar option would also need to be exposed in + * {@link org.springframework.http.server.PathContainer PathContainer}. + */ + char getSeparator() { + return '/'; + } + /** - * Process the path pattern data, a character at a time, breaking it into + * Process the path pattern content, a character at a time, breaking it into * path elements around separator boundaries and verifying the structure at each * stage. Produces a PathPattern object that can be used for fast matching * against paths. Each invocation of this method delegates to a new instance of diff --git a/spring-web/src/main/java/org/springframework/web/util/pattern/RegexPathElement.java b/spring-web/src/main/java/org/springframework/web/util/pattern/RegexPathElement.java index 0315f75aee..3f5eaa028a 100644 --- a/spring-web/src/main/java/org/springframework/web/util/pattern/RegexPathElement.java +++ b/spring-web/src/main/java/org/springframework/web/util/pattern/RegexPathElement.java @@ -145,7 +145,7 @@ class RegexPathElement extends PathElement { // If pattern is capturing variables there must be some actual data to bind to them matches = (pathIndex + 1) >= matchingContext.pathLength && (this.variableNames.isEmpty() || textToMatch.length() > 0); - if (!matches && matchingContext.isAllowOptionalTrailingSlash()) { + if (!matches && matchingContext.isMatchOptionalTrailingSeparator()) { matches = (this.variableNames.isEmpty() || textToMatch.length() > 0) && (pathIndex + 2) >= matchingContext.pathLength && matchingContext.isSeparator(pathIndex + 1); diff --git a/spring-web/src/main/java/org/springframework/web/util/pattern/SingleCharWildcardedPathElement.java b/spring-web/src/main/java/org/springframework/web/util/pattern/SingleCharWildcardedPathElement.java index 8938d66689..681f4dd6d9 100644 --- a/spring-web/src/main/java/org/springframework/web/util/pattern/SingleCharWildcardedPathElement.java +++ b/spring-web/src/main/java/org/springframework/web/util/pattern/SingleCharWildcardedPathElement.java @@ -104,7 +104,7 @@ class SingleCharWildcardedPathElement extends PathElement { return true; } else { - return (matchingContext.isAllowOptionalTrailingSlash() && + return (matchingContext.isMatchOptionalTrailingSeparator() && (pathIndex + 1) == matchingContext.pathLength && matchingContext.isSeparator(pathIndex)); } diff --git a/spring-web/src/main/java/org/springframework/web/util/pattern/WildcardPathElement.java b/spring-web/src/main/java/org/springframework/web/util/pattern/WildcardPathElement.java index ca5696ac91..2b07954e57 100644 --- a/spring-web/src/main/java/org/springframework/web/util/pattern/WildcardPathElement.java +++ b/spring-web/src/main/java/org/springframework/web/util/pattern/WildcardPathElement.java @@ -65,7 +65,7 @@ class WildcardPathElement extends PathElement { return true; } else { - return (matchingContext.isAllowOptionalTrailingSlash() && // if optional slash is on... + return (matchingContext.isMatchOptionalTrailingSeparator() && // if optional slash is on... segmentData != null && segmentData.length() > 0 && // and there is at least one character to match the *... (pathIndex + 1) == matchingContext.pathLength && // and the next path element is the end of the candidate... matchingContext.isSeparator(pathIndex)); // and the final element is a separator diff --git a/spring-web/src/test/java/org/springframework/web/util/pattern/PathPatternParserTests.java b/spring-web/src/test/java/org/springframework/web/util/pattern/PathPatternParserTests.java index 3395d1ed9c..eb40d30ff4 100644 --- a/spring-web/src/test/java/org/springframework/web/util/pattern/PathPatternParserTests.java +++ b/spring-web/src/test/java/org/springframework/web/util/pattern/PathPatternParserTests.java @@ -23,7 +23,6 @@ import java.util.List; import org.junit.Test; import org.springframework.http.server.PathContainer; -import org.springframework.web.util.pattern.PathPattern.PathMatchResult; import org.springframework.web.util.pattern.PatternParseException.PatternMessage; import static org.junit.Assert.assertEquals; @@ -135,7 +134,7 @@ public class PathPatternParserTests { pathPattern = checkStructure("/{var:[^\\/]*}"); assertEquals(CaptureVariablePathElement.class.getName(), pathPattern.getHeadSection().next.getClass().getName()); - PathMatchResult result = matchAndExtract(pathPattern,"/foo"); + PathPattern.PathMatchInfo result = matchAndExtract(pathPattern,"/foo"); assertEquals("foo", result.getUriVariables().get("var")); pathPattern = checkStructure("/{var:\\[*}"); @@ -465,7 +464,7 @@ public class PathPatternParserTests { assertFalse(pp.matches(PathPatternTests.toPathContainer(path))); } - private PathMatchResult matchAndExtract(PathPattern pp, String path) { + private PathPattern.PathMatchInfo matchAndExtract(PathPattern pp, String path) { return pp.matchAndExtract(PathPatternTests.toPathContainer(path)); } diff --git a/spring-web/src/test/java/org/springframework/web/util/pattern/PathPatternTests.java b/spring-web/src/test/java/org/springframework/web/util/pattern/PathPatternTests.java index 4e3fe36a67..c6dcfc4e99 100644 --- a/spring-web/src/test/java/org/springframework/web/util/pattern/PathPatternTests.java +++ b/spring-web/src/test/java/org/springframework/web/util/pattern/PathPatternTests.java @@ -31,7 +31,6 @@ import org.junit.rules.ExpectedException; import org.springframework.http.server.PathContainer; import org.springframework.http.server.PathContainer.Element; import org.springframework.util.AntPathMatcher; -import org.springframework.web.util.pattern.PathPattern.PathMatchResult; import org.springframework.web.util.pattern.PathPattern.PathRemainingMatchInfo; import static org.hamcrest.CoreMatchers.containsString; @@ -186,7 +185,7 @@ public class PathPatternTests { // Now with trailing matching turned OFF PathPatternParser parser = new PathPatternParser(); - parser.setMatchOptionalTrailingSlash(false); + parser.setMatchOptionalTrailingSeparator(false); // LiteralPathElement pp = parser.parse("/resource"); assertMatches(pp,"/resource"); @@ -451,7 +450,7 @@ public class PathPatternTests { checkNoMatch("a/*", "a//"); // no data for * checkMatches("a/*", "a/a/"); // trailing slash, so is allowed PathPatternParser ppp = new PathPatternParser(); - ppp.setMatchOptionalTrailingSlash(false); + ppp.setMatchOptionalTrailingSeparator(false); assertFalse(ppp.parse("a/*").matches(toPathContainer("a//"))); checkMatches("a/*", "a/a"); checkMatches("a/*", "a/a/"); // trailing slash is optional @@ -580,7 +579,7 @@ public class PathPatternTests { @Test public void matchStart() { PathPatternParser ppp = new PathPatternParser(); - ppp.setMatchOptionalTrailingSlash(false); + ppp.setMatchOptionalTrailingSeparator(false); PathPattern pp = ppp.parse("test"); assertFalse(pp.matchStart(PathContainer.parsePath("test/"))); @@ -845,7 +844,7 @@ public class PathPatternTests { // Only patterns not capturing variables cannot match against just / PathPatternParser ppp = new PathPatternParser(); - ppp.setMatchOptionalTrailingSlash(true); + ppp.setMatchOptionalTrailingSeparator(true); pp = ppp.parse("/****"); assertMatches(pp,"/abcdef"); assertMatches(pp,"/"); @@ -900,7 +899,7 @@ public class PathPatternTests { checkCapture("/foo/{bar}/boo/{baz}", "/foo/plum/boo/apple", "bar", "plum", "baz", "apple"); checkCapture("/{bla}.*", "/testing.html", "bla", "testing"); - PathMatchResult extracted = checkCapture("/abc", "/abc"); + PathPattern.PathMatchInfo extracted = checkCapture("/abc", "/abc"); assertEquals(0, extracted.getUriVariables().size()); checkCapture("/{bla}/foo","/a/foo"); } @@ -911,7 +910,7 @@ public class PathPatternTests { PathPattern p = null; p = pp.parse("{symbolicName:[\\w\\.]+}-{version:[\\w\\.]+}.jar"); - PathMatchResult result = matchAndExtract(p, "com.example-1.0.0.jar"); + PathPattern.PathMatchInfo result = matchAndExtract(p, "com.example-1.0.0.jar"); assertEquals("com.example", result.getUriVariables().get("symbolicName")); assertEquals("1.0.0", result.getUriVariables().get("version")); @@ -926,7 +925,7 @@ public class PathPatternTests { PathPatternParser pp = new PathPatternParser(); PathPattern p = pp.parse("{symbolicName:[\\p{L}\\.]+}-sources-{version:[\\p{N}\\.]+}.jar"); - PathMatchResult result = p.matchAndExtract(toPathContainer("com.example-sources-1.0.0.jar")); + PathPattern.PathMatchInfo result = p.matchAndExtract(toPathContainer("com.example-sources-1.0.0.jar")); assertEquals("com.example", result.getUriVariables().get("symbolicName")); assertEquals("1.0.0", result.getUriVariables().get("version")); @@ -1074,8 +1073,8 @@ public class PathPatternTests { PathPatternParser parser = new PathPatternParser(); PathPattern p1 = parser.parse("/{foo}"); PathPattern p2 = parser.parse("/{foo}.*"); - PathMatchResult r1 = matchAndExtract(p1, "/file.txt"); - PathMatchResult r2 = matchAndExtract(p2, "/file.txt"); + PathPattern.PathMatchInfo r1 = matchAndExtract(p1, "/file.txt"); + PathPattern.PathMatchInfo r2 = matchAndExtract(p2, "/file.txt"); // works fine assertEquals("file.txt", r1.getUriVariables().get("foo")); @@ -1204,7 +1203,7 @@ public class PathPatternTests { @Test public void parameters() { // CaptureVariablePathElement - PathMatchResult result = matchAndExtract("/abc/{var}","/abc/one;two=three;four=five"); + PathPattern.PathMatchInfo result = matchAndExtract("/abc/{var}","/abc/one;two=three;four=five"); assertEquals("one",result.getUriVariables().get("var")); assertEquals("three",result.getMatrixVariables().get("var").getFirst("two")); assertEquals("five",result.getMatrixVariables().get("var").getFirst("four")); @@ -1234,13 +1233,13 @@ public class PathPatternTests { } - private PathMatchResult matchAndExtract(String pattern, String path) { + private PathPattern.PathMatchInfo matchAndExtract(String pattern, String path) { return parse(pattern).matchAndExtract(PathPatternTests.toPathContainer(path)); } private PathPattern parse(String path) { PathPatternParser pp = new PathPatternParser(); - pp.setMatchOptionalTrailingSlash(true); + pp.setMatchOptionalTrailingSeparator(true); return pp.parse(path); } @@ -1253,7 +1252,7 @@ public class PathPatternTests { private void checkMatches(String uriTemplate, String path) { PathPatternParser parser = new PathPatternParser(); - parser.setMatchOptionalTrailingSlash(true); + parser.setMatchOptionalTrailingSeparator(true); PathPattern p = parser.parse(uriTemplate); PathContainer pc = toPathContainer(path); assertTrue(p.matches(pc)); @@ -1261,14 +1260,14 @@ public class PathPatternTests { private void checkStartNoMatch(String uriTemplate, String path) { PathPatternParser p = new PathPatternParser(); - p.setMatchOptionalTrailingSlash(true); + p.setMatchOptionalTrailingSeparator(true); PathPattern pattern = p.parse(uriTemplate); assertFalse(pattern.matchStart(toPathContainer(path))); } private void checkStartMatches(String uriTemplate, String path) { PathPatternParser p = new PathPatternParser(); - p.setMatchOptionalTrailingSlash(true); + p.setMatchOptionalTrailingSeparator(true); PathPattern pattern = p.parse(uriTemplate); assertTrue(pattern.matchStart(toPathContainer(path))); } @@ -1280,10 +1279,10 @@ public class PathPatternTests { assertFalse(pattern.matches(PathContainer)); } - private PathMatchResult checkCapture(String uriTemplate, String path, String... keyValues) { + private PathPattern.PathMatchInfo checkCapture(String uriTemplate, String path, String... keyValues) { PathPatternParser parser = new PathPatternParser(); PathPattern pattern = parser.parse(uriTemplate); - PathMatchResult matchResult = pattern.matchAndExtract(toPathContainer(path)); + PathPattern.PathMatchInfo matchResult = pattern.matchAndExtract(toPathContainer(path)); Map expectedKeyValues = new HashMap<>(); for (int i = 0; i < keyValues.length; i += 2) { expectedKeyValues.put(keyValues[i], keyValues[i + 1]); @@ -1317,7 +1316,7 @@ public class PathPatternTests { return pattern.getPathRemaining(toPathContainer(path)); } - private PathMatchResult matchAndExtract(PathPattern p, String path) { + private PathPattern.PathMatchInfo matchAndExtract(PathPattern p, String path) { return p.matchAndExtract(toPathContainer(path)); } diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/handler/AbstractHandlerMapping.java b/spring-webflux/src/main/java/org/springframework/web/reactive/handler/AbstractHandlerMapping.java index 152fb50cb0..3aa515d626 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/handler/AbstractHandlerMapping.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/handler/AbstractHandlerMapping.java @@ -92,7 +92,7 @@ public abstract class AbstractHandlerMapping extends ApplicationObjectSupport im *

    The default value is {@code true}. */ public void setUseTrailingSlashMatch(boolean trailingSlashMatch) { - this.patternParser.setMatchOptionalTrailingSlash(trailingSlashMatch); + this.patternParser.setMatchOptionalTrailingSeparator(trailingSlashMatch); } /** diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/RequestMappingInfoHandlerMapping.java b/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/RequestMappingInfoHandlerMapping.java index fbd7e55d01..369fef0e9a 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/RequestMappingInfoHandlerMapping.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/RequestMappingInfoHandlerMapping.java @@ -112,7 +112,7 @@ public abstract class RequestMappingInfoHandlerMapping extends AbstractHandlerMe } else { bestPattern = patterns.iterator().next(); - PathPattern.PathMatchResult result = bestPattern.matchAndExtract(lookupPath); + PathPattern.PathMatchInfo result = bestPattern.matchAndExtract(lookupPath); uriVariables = result.getUriVariables(); matrixVariables = result.getMatrixVariables(); } diff --git a/spring-webflux/src/test/java/org/springframework/web/reactive/result/condition/PatternsRequestConditionTests.java b/spring-webflux/src/test/java/org/springframework/web/reactive/result/condition/PatternsRequestConditionTests.java index 5e10baaf91..08e0bade87 100644 --- a/spring-webflux/src/test/java/org/springframework/web/reactive/result/condition/PatternsRequestConditionTests.java +++ b/spring-webflux/src/test/java/org/springframework/web/reactive/result/condition/PatternsRequestConditionTests.java @@ -121,7 +121,7 @@ public class PatternsRequestConditionTests { "/foo", match.getPatterns().iterator().next().getPatternString()); PathPatternParser parser = new PathPatternParser(); - parser.setMatchOptionalTrailingSlash(false); + parser.setMatchOptionalTrailingSeparator(false); condition = new PatternsRequestCondition(parser.parse("/foo")); match = condition.getMatchingCondition(get("/foo/").toExchange());