diff --git a/spring-web/src/main/java/org/springframework/web/cors/reactive/UrlBasedCorsConfigurationSource.java b/spring-web/src/main/java/org/springframework/web/cors/reactive/UrlBasedCorsConfigurationSource.java
index 3dcad864dab..9fb2c74d547 100644
--- a/spring-web/src/main/java/org/springframework/web/cors/reactive/UrlBasedCorsConfigurationSource.java
+++ b/spring-web/src/main/java/org/springframework/web/cors/reactive/UrlBasedCorsConfigurationSource.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2016 the original author or authors.
+ * Copyright 2002-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -25,7 +25,7 @@ import org.springframework.util.PathMatcher;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.support.HttpRequestPathHelper;
-import org.springframework.web.util.ParsingPathMatcher;
+import org.springframework.web.util.pattern.ParsingPathMatcher;
/**
* Provide a per reactive request {@link CorsConfiguration} instance based on a
diff --git a/spring-web/src/main/java/org/springframework/web/util/patterns/CaptureTheRestPathElement.java b/spring-web/src/main/java/org/springframework/web/util/pattern/CaptureTheRestPathElement.java
similarity index 87%
rename from spring-web/src/main/java/org/springframework/web/util/patterns/CaptureTheRestPathElement.java
rename to spring-web/src/main/java/org/springframework/web/util/pattern/CaptureTheRestPathElement.java
index 21d466096f0..3ac6bdaf081 100644
--- a/spring-web/src/main/java/org/springframework/web/util/patterns/CaptureTheRestPathElement.java
+++ b/spring-web/src/main/java/org/springframework/web/util/pattern/CaptureTheRestPathElement.java
@@ -14,19 +14,21 @@
* limitations under the License.
*/
-package org.springframework.web.util.patterns;
+package org.springframework.web.util.pattern;
-import org.springframework.web.util.patterns.PathPattern.MatchingContext;
+import org.springframework.web.util.pattern.PathPattern.MatchingContext;
/**
* A path element representing capturing the rest of a path. In the pattern
* '/foo/{*foobar}' the /{*foobar} is represented as a {@link CaptureTheRestPathElement}.
*
* @author Andy Clement
+ * @since 5.0
*/
class CaptureTheRestPathElement extends PathElement {
- private String variableName;
+ private final String variableName;
+
/**
* @param pos position of the path element within the path pattern text
@@ -35,9 +37,10 @@ class CaptureTheRestPathElement extends PathElement {
*/
CaptureTheRestPathElement(int pos, char[] captureDescriptor, char separator) {
super(pos, separator);
- variableName = new String(captureDescriptor, 2, captureDescriptor.length - 3);
+ this.variableName = new String(captureDescriptor, 2, captureDescriptor.length - 3);
}
+
@Override
public boolean matches(int candidateIndex, MatchingContext matchingContext) {
// No need to handle 'match start' checking as this captures everything
@@ -59,10 +62,6 @@ class CaptureTheRestPathElement extends PathElement {
return true;
}
- public String toString() {
- return "CaptureTheRest(/{*" + variableName + "})";
- }
-
@Override
public int getNormalizedLength() {
return 1;
@@ -77,4 +76,10 @@ class CaptureTheRestPathElement extends PathElement {
public int getCaptureCount() {
return 1;
}
-}
\ No newline at end of file
+
+
+ public String toString() {
+ return "CaptureTheRest(/{*" + this.variableName + "})";
+ }
+
+}
diff --git a/spring-web/src/main/java/org/springframework/web/util/patterns/CaptureVariablePathElement.java b/spring-web/src/main/java/org/springframework/web/util/pattern/CaptureVariablePathElement.java
similarity index 70%
rename from spring-web/src/main/java/org/springframework/web/util/patterns/CaptureVariablePathElement.java
rename to spring-web/src/main/java/org/springframework/web/util/pattern/CaptureVariablePathElement.java
index b26660d0f67..43a3c85f2a8 100644
--- a/spring-web/src/main/java/org/springframework/web/util/patterns/CaptureVariablePathElement.java
+++ b/spring-web/src/main/java/org/springframework/web/util/pattern/CaptureVariablePathElement.java
@@ -14,11 +14,10 @@
* limitations under the License.
*/
-package org.springframework.web.util.patterns;
+package org.springframework.web.util.pattern;
import java.util.regex.Matcher;
-
-import org.springframework.web.util.patterns.PathPattern.MatchingContext;
+import java.util.regex.Pattern;
/**
* A path element representing capturing a piece of the path as a variable. In the pattern
@@ -26,12 +25,14 @@ import org.springframework.web.util.patterns.PathPattern.MatchingContext;
* must be at least one character to bind to the variable.
*
* @author Andy Clement
+ * @since 5.0
*/
class CaptureVariablePathElement extends PathElement {
- private String variableName;
+ private final String variableName;
+
+ private Pattern constraintPattern;
- private java.util.regex.Pattern constraintPattern;
/**
* @param pos the position in the pattern of this capture element
@@ -48,43 +49,47 @@ class CaptureVariablePathElement extends PathElement {
}
if (colon == -1) {
// no constraint
- variableName = new String(captureDescriptor, 1, captureDescriptor.length - 2);
+ this.variableName = new String(captureDescriptor, 1, captureDescriptor.length - 2);
}
else {
- variableName = new String(captureDescriptor, 1, colon - 1);
+ this.variableName = new String(captureDescriptor, 1, colon - 1);
if (caseSensitive) {
- constraintPattern = java.util.regex.Pattern
- .compile(new String(captureDescriptor, colon + 1, captureDescriptor.length - colon - 2));
+ this.constraintPattern = Pattern.compile(
+ new String(captureDescriptor, colon + 1, captureDescriptor.length - colon - 2));
}
else {
- constraintPattern = java.util.regex.Pattern.compile(
+ this.constraintPattern = Pattern.compile(
new String(captureDescriptor, colon + 1, captureDescriptor.length - colon - 2),
- java.util.regex.Pattern.CASE_INSENSITIVE);
+ Pattern.CASE_INSENSITIVE);
}
}
}
+
@Override
- public boolean matches(int candidateIndex, MatchingContext matchingContext) {
+ public boolean matches(int candidateIndex, PathPattern.MatchingContext matchingContext) {
int nextPos = matchingContext.scanAhead(candidateIndex);
// There must be at least one character to capture:
if (nextPos == candidateIndex) {
return false;
}
+
CharSequence candidateCapture = null;
- if (constraintPattern != null) {
+ if (this.constraintPattern != null) {
// TODO possible optimization - only regex match if rest of pattern matches? Benefit likely to vary pattern to pattern
candidateCapture = new SubSequence(matchingContext.candidate, candidateIndex, nextPos);
- Matcher m = constraintPattern.matcher(candidateCapture);
- if (m.groupCount() != 0) {
- throw new IllegalArgumentException("No capture groups allowed in the constraint regex: " + constraintPattern.pattern());
+ Matcher matcher = constraintPattern.matcher(candidateCapture);
+ if (matcher.groupCount() != 0) {
+ throw new IllegalArgumentException(
+ "No capture groups allowed in the constraint regex: " + this.constraintPattern.pattern());
}
- if (!m.matches()) {
+ if (!matcher.matches()) {
return false;
}
}
+
boolean match = false;
- if (next == null) {
+ if (this.next == null) {
if (matchingContext.determineRemainingPath && nextPos > candidateIndex) {
matchingContext.remainingPathIndex = nextPos;
match = true;
@@ -101,14 +106,16 @@ class CaptureVariablePathElement extends PathElement {
}
else {
if (matchingContext.isMatchStartMatching && nextPos == matchingContext.candidateLength) {
- match = true; // no more data but matches up to this point
+ match = true; // no more data but matches up to this point
}
else {
- match = next.matches(nextPos, matchingContext);
+ match = this.next.matches(nextPos, matchingContext);
}
}
+
if (match && matchingContext.extractingVariables) {
- matchingContext.set(variableName, new String(matchingContext.candidate, candidateIndex, nextPos - candidateIndex));
+ matchingContext.set(this.variableName,
+ new String(matchingContext.candidate, candidateIndex, nextPos - candidateIndex));
}
return match;
}
@@ -117,10 +124,6 @@ class CaptureVariablePathElement extends PathElement {
return this.variableName;
}
- public String toString() {
- return "CaptureVariable({" + variableName + (constraintPattern == null ? "" : ":" + constraintPattern.pattern()) + "})";
- }
-
@Override
public int getNormalizedLength() {
return 1;
@@ -140,4 +143,11 @@ class CaptureVariablePathElement extends PathElement {
public int getScore() {
return CAPTURE_VARIABLE_WEIGHT;
}
-}
\ No newline at end of file
+
+
+ public String toString() {
+ return "CaptureVariable({" + this.variableName +
+ (this.constraintPattern != null ? ":" + this.constraintPattern.pattern() : "") + "})";
+ }
+
+}
diff --git a/spring-web/src/main/java/org/springframework/web/util/patterns/InternalPathPatternParser.java b/spring-web/src/main/java/org/springframework/web/util/pattern/InternalPathPatternParser.java
similarity index 53%
rename from spring-web/src/main/java/org/springframework/web/util/patterns/InternalPathPatternParser.java
rename to spring-web/src/main/java/org/springframework/web/util/pattern/InternalPathPatternParser.java
index 1c090a34e5c..5223254b89e 100644
--- a/spring-web/src/main/java/org/springframework/web/util/patterns/InternalPathPatternParser.java
+++ b/spring-web/src/main/java/org/springframework/web/util/pattern/InternalPathPatternParser.java
@@ -14,12 +14,14 @@
* limitations under the License.
*/
-package org.springframework.web.util.patterns;
+package org.springframework.web.util.pattern;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.PatternSyntaxException;
+import org.springframework.web.util.pattern.PatternParseException.PatternMessage;
+
/**
* Parser for URI template patterns. It breaks the path pattern into a number of
* {@link PathElement}s in a linked list. Instances are reusable but are not thread-safe.
@@ -27,7 +29,7 @@ import java.util.regex.PatternSyntaxException;
* @author Andy Clement
* @since 5.0
*/
-public class InternalPathPatternParser {
+class InternalPathPatternParser {
// The expected path separator to split path elements during parsing
char separator = PathPatternParser.DEFAULT_SEPARATOR;
@@ -80,13 +82,12 @@ public class InternalPathPatternParser {
// The most recently constructed path element in the chain
PathElement currentPE;
+
/**
- * Create a PatternParser that will use the specified separator instead of
- * the default.
- *
* @param separator the path separator to look for when parsing
* @param caseSensitive true if PathPatterns should be sensitive to case
- * @param matchOptionalTrailingSlash true if patterns without a trailing slash can match paths that do have a trailing slash
+ * @param matchOptionalTrailingSlash true if patterns without a trailing slash
+ * can match paths that do have a trailing slash
*/
public InternalPathPatternParser(char separator, boolean caseSensitive, boolean matchOptionalTrailingSlash) {
this.separator = separator;
@@ -94,108 +95,111 @@ public class InternalPathPatternParser {
this.matchOptionalTrailingSlash = matchOptionalTrailingSlash;
}
+
/**
* 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
*/
- public PathPattern parse(String pathPattern) {
- if (pathPattern == null) {
- pathPattern = "";
- }
- pathPatternData = pathPattern.toCharArray();
- pathPatternLength = pathPatternData.length;
- headPE = null;
- currentPE = null;
- capturedVariableNames = null;
- pathElementStart = -1;
- pos = 0;
+ public PathPattern parse(String pathPattern) throws PatternParseException {
+ this.pathPatternData = pathPattern.toCharArray();
+ this.pathPatternLength = pathPatternData.length;
+ this.headPE = null;
+ this.currentPE = null;
+ this.capturedVariableNames = null;
+ this.pathElementStart = -1;
+ this.pos = 0;
resetPathElementState();
- while (pos < pathPatternLength) {
- char ch = pathPatternData[pos];
- if (ch == separator) {
- if (pathElementStart != -1) {
+
+ while (this.pos < this.pathPatternLength) {
+ char ch = this.pathPatternData[this.pos];
+ if (ch == this.separator) {
+ if (this.pathElementStart != -1) {
pushPathElement(createPathElement());
}
if (peekDoubleWildcard()) {
- pushPathElement(new WildcardTheRestPathElement(pos, separator));
- pos += 2;
+ pushPathElement(new WildcardTheRestPathElement(this.pos, this.separator));
+ this.pos += 2;
}
else {
- pushPathElement(new SeparatorPathElement(pos, separator));
+ pushPathElement(new SeparatorPathElement(this.pos, this.separator));
}
}
else {
- if (pathElementStart == -1) {
- pathElementStart = pos;
+ if (this.pathElementStart == -1) {
+ this.pathElementStart = this.pos;
}
if (ch == '?') {
- singleCharWildcardCount++;
+ this.singleCharWildcardCount++;
}
else if (ch == '{') {
- if (insideVariableCapture) {
- throw new PatternParseException(pos, pathPatternData, PatternMessage.ILLEGAL_NESTED_CAPTURE);
- // If we enforced that adjacent captures weren't allowed,
- // // this would do it (this would be an error: /foo/{bar}{boo}/)
-// } else if (pos > 0 && pathPatternData[pos - 1] == '}') {
-// throw new PatternParseException(pos, pathPatternData,
-// PatternMessage.CANNOT_HAVE_ADJACENT_CAPTURES);
+ if (this.insideVariableCapture) {
+ throw new PatternParseException(this.pos, this.pathPatternData,
+ PatternMessage.ILLEGAL_NESTED_CAPTURE);
}
- insideVariableCapture = true;
- variableCaptureStart = pos;
+ // If we enforced that adjacent captures weren't allowed,
+ // this would do it (this would be an error: /foo/{bar}{boo}/)
+ // } else if (pos > 0 && pathPatternData[pos - 1] == '}') {
+ // throw new PatternParseException(pos, pathPatternData,
+ // PatternMessage.CANNOT_HAVE_ADJACENT_CAPTURES);
+ this.insideVariableCapture = true;
+ this.variableCaptureStart = pos;
}
else if (ch == '}') {
- if (!insideVariableCapture) {
- throw new PatternParseException(pos, pathPatternData, PatternMessage.MISSING_OPEN_CAPTURE);
+ if (!this.insideVariableCapture) {
+ throw new PatternParseException(this.pos, this.pathPatternData,
+ PatternMessage.MISSING_OPEN_CAPTURE);
}
- insideVariableCapture = false;
- if (isCaptureTheRestVariable && (pos + 1) < pathPatternLength) {
- throw new PatternParseException(pos + 1, pathPatternData,
+ this.insideVariableCapture = false;
+ if (this.isCaptureTheRestVariable && (this.pos + 1) < this.pathPatternLength) {
+ throw new PatternParseException(this.pos + 1, this.pathPatternData,
PatternMessage.NO_MORE_DATA_EXPECTED_AFTER_CAPTURE_THE_REST);
}
- variableCaptureCount++;
+ this.variableCaptureCount++;
}
else if (ch == ':') {
- if (insideVariableCapture) {
+ if (this.insideVariableCapture) {
skipCaptureRegex();
- insideVariableCapture = false;
- variableCaptureCount++;
+ this.insideVariableCapture = false;
+ this.variableCaptureCount++;
}
}
else if (ch == '*') {
- if (insideVariableCapture) {
- if (variableCaptureStart == pos - 1) {
- isCaptureTheRestVariable = true;
+ if (this.insideVariableCapture) {
+ if (this.variableCaptureStart == pos - 1) {
+ this.isCaptureTheRestVariable = true;
}
}
- wildcard = true;
+ this.wildcard = true;
}
// Check that the characters used for captured variable names are like java identifiers
- if (insideVariableCapture) {
- if ((variableCaptureStart + 1 + (isCaptureTheRestVariable ? 1 : 0)) == pos
- && !Character.isJavaIdentifierStart(ch)) {
- throw new PatternParseException(pos, pathPatternData,
+ if (this.insideVariableCapture) {
+ if ((this.variableCaptureStart + 1 + (this.isCaptureTheRestVariable ? 1 : 0)) == this.pos &&
+ !Character.isJavaIdentifierStart(ch)) {
+ throw new PatternParseException(this.pos, this.pathPatternData,
PatternMessage.ILLEGAL_CHARACTER_AT_START_OF_CAPTURE_DESCRIPTOR,
Character.toString(ch));
}
- else if ((pos > (variableCaptureStart + 1 + (isCaptureTheRestVariable ? 1 : 0))
- && !Character.isJavaIdentifierPart(ch))) {
- throw new PatternParseException(pos, pathPatternData,
- PatternMessage.ILLEGAL_CHARACTER_IN_CAPTURE_DESCRIPTOR, Character.toString(ch));
+ else if ((this.pos > (this.variableCaptureStart + 1 + (this.isCaptureTheRestVariable ? 1 : 0)) &&
+ !Character.isJavaIdentifierPart(ch))) {
+ throw new PatternParseException(this.pos, this.pathPatternData,
+ PatternMessage.ILLEGAL_CHARACTER_IN_CAPTURE_DESCRIPTOR,
+ Character.toString(ch));
}
}
}
- pos++;
+ this.pos++;
}
- if (pathElementStart != -1) {
+ if (this.pathElementStart != -1) {
pushPathElement(createPathElement());
}
- return new PathPattern(pathPattern, headPE, separator, caseSensitive, matchOptionalTrailingSlash);
+ return new PathPattern(
+ pathPattern, this.headPE, this.separator, this.caseSensitive, this.matchOptionalTrailingSlash);
}
/**
@@ -207,14 +211,15 @@ public class InternalPathPatternParser {
*
A separator that should not indicate the end of the regex can be escaped:
*/
private void skipCaptureRegex() {
- pos++;
- int regexStart = pos;
+ this.pos++;
+ int regexStart = this.pos;
int curlyBracketDepth = 0; // how deep in nested {...} pairs
boolean previousBackslash = false;
- while (pos < pathPatternLength) {
- char ch = pathPatternData[pos];
+
+ while (this.pos < this.pathPatternLength) {
+ char ch = this.pathPatternData[pos];
if (ch == '\\' && !previousBackslash) {
- pos++;
+ this.pos++;
previousBackslash = true;
continue;
}
@@ -223,21 +228,24 @@ public class InternalPathPatternParser {
}
else if (ch == '}' && !previousBackslash) {
if (curlyBracketDepth == 0) {
- if (regexStart == pos) {
- throw new PatternParseException(regexStart, pathPatternData,
+ if (regexStart == this.pos) {
+ throw new PatternParseException(regexStart, this.pathPatternData,
PatternMessage.MISSING_REGEX_CONSTRAINT);
}
return;
}
curlyBracketDepth--;
}
- if (ch == separator && !previousBackslash) {
- throw new PatternParseException(pos, pathPatternData, PatternMessage.MISSING_CLOSE_CAPTURE);
+ if (ch == this.separator && !previousBackslash) {
+ throw new PatternParseException(this.pos, this.pathPatternData,
+ PatternMessage.MISSING_CLOSE_CAPTURE);
}
- pos++;
+ this.pos++;
previousBackslash = false;
}
- throw new PatternParseException(pos - 1, pathPatternData, PatternMessage.MISSING_CLOSE_CAPTURE);
+
+ throw new PatternParseException(this.pos - 1, this.pathPatternData,
+ PatternMessage.MISSING_CLOSE_CAPTURE);
}
/**
@@ -245,13 +253,13 @@ public class InternalPathPatternParser {
* (and only ** before the end of the pattern or the next separator)
*/
private boolean peekDoubleWildcard() {
- if ((pos + 2) >= pathPatternLength) {
+ if ((this.pos + 2) >= this.pathPatternLength) {
return false;
}
- if (pathPatternData[pos + 1] != '*' || pathPatternData[pos + 2] != '*') {
+ if (this.pathPatternData[this.pos + 1] != '*' || this.pathPatternData[this.pos + 2] != '*') {
return false;
}
- return (pos + 3 == pathPatternLength);
+ return (this.pos + 3 == this.pathPatternLength);
}
/**
@@ -261,38 +269,39 @@ public class InternalPathPatternParser {
if (newPathElement instanceof CaptureTheRestPathElement) {
// There must be a separator ahead of this thing
// currentPE SHOULD be a SeparatorPathElement
- if (currentPE == null) {
- headPE = newPathElement;
- currentPE = newPathElement;
+ if (this.currentPE == null) {
+ this.headPE = newPathElement;
+ this.currentPE = newPathElement;
}
- else if (currentPE instanceof SeparatorPathElement) {
- PathElement peBeforeSeparator = currentPE.prev;
+ else if (this.currentPE instanceof SeparatorPathElement) {
+ PathElement peBeforeSeparator = this.currentPE.prev;
if (peBeforeSeparator == null) {
// /{*foobar} is at the start
- headPE = newPathElement;
- newPathElement.prev = peBeforeSeparator;
+ this.headPE = newPathElement;
+ newPathElement.prev = null;
}
else {
peBeforeSeparator.next = newPathElement;
newPathElement.prev = peBeforeSeparator;
}
- currentPE = newPathElement;
+ this.currentPE = newPathElement;
}
else {
- throw new IllegalStateException("Expected SeparatorPathElement but was " + currentPE);
+ throw new IllegalStateException("Expected SeparatorPathElement but was " + this.currentPE);
}
}
else {
- if (headPE == null) {
- headPE = newPathElement;
- currentPE = newPathElement;
+ if (this.headPE == null) {
+ this.headPE = newPathElement;
+ this.currentPE = newPathElement;
}
else {
- currentPE.next = newPathElement;
- newPathElement.prev = currentPE;
- currentPE = newPathElement;
+ this.currentPE.next = newPathElement;
+ newPathElement.prev = this.currentPE;
+ this.currentPE = newPathElement;
}
}
+
resetPathElementState();
}
@@ -302,61 +311,70 @@ public class InternalPathPatternParser {
* @return the new path element
*/
private PathElement createPathElement() {
- if (insideVariableCapture) {
- throw new PatternParseException(pos, pathPatternData, PatternMessage.MISSING_CLOSE_CAPTURE);
+ if (this.insideVariableCapture) {
+ throw new PatternParseException(this.pos, this.pathPatternData, PatternMessage.MISSING_CLOSE_CAPTURE);
}
- char[] pathElementText = new char[pos - pathElementStart];
- System.arraycopy(pathPatternData, pathElementStart, pathElementText, 0, pos - pathElementStart);
+
+ char[] pathElementText = new char[this.pos - this.pathElementStart];
+ System.arraycopy(this.pathPatternData, this.pathElementStart, pathElementText, 0,
+ this.pos - this.pathElementStart);
PathElement newPE = null;
- if (variableCaptureCount > 0) {
- if (variableCaptureCount == 1
- && pathElementStart == variableCaptureStart && pathPatternData[pos - 1] == '}') {
- if (isCaptureTheRestVariable) {
+
+ if (this.variableCaptureCount > 0) {
+ if (this.variableCaptureCount == 1 && this.pathElementStart == this.variableCaptureStart &&
+ this.pathPatternData[this.pos - 1] == '}') {
+ if (this.isCaptureTheRestVariable) {
// It is {*....}
newPE = new CaptureTheRestPathElement(pathElementStart, pathElementText, separator);
}
else {
// It is a full capture of this element (possibly with constraint), for example: /foo/{abc}/
try {
- newPE = new CaptureVariablePathElement(pathElementStart, pathElementText, caseSensitive, separator);
+ newPE = new CaptureVariablePathElement(this.pathElementStart, pathElementText,
+ this.caseSensitive, this.separator);
}
catch (PatternSyntaxException pse) {
- throw new PatternParseException(pse, findRegexStart(pathPatternData, pathElementStart)
- + pse.getIndex(), pathPatternData, PatternMessage.JDK_PATTERN_SYNTAX_EXCEPTION);
+ throw new PatternParseException(pse,
+ findRegexStart(this.pathPatternData, this.pathElementStart) + pse.getIndex(),
+ this.pathPatternData, PatternMessage.REGEX_PATTERN_SYNTAX_EXCEPTION);
}
- recordCapturedVariable(pathElementStart, ((CaptureVariablePathElement) newPE).getVariableName());
+ recordCapturedVariable(this.pathElementStart,
+ ((CaptureVariablePathElement) newPE).getVariableName());
}
}
else {
- if (isCaptureTheRestVariable) {
- throw new PatternParseException(pathElementStart, pathPatternData,
+ if (this.isCaptureTheRestVariable) {
+ throw new PatternParseException(this.pathElementStart, this.pathPatternData,
PatternMessage.CAPTURE_ALL_IS_STANDALONE_CONSTRUCT);
}
- RegexPathElement newRegexSection = new RegexPathElement(pathElementStart, pathElementText,
- caseSensitive, pathPatternData, separator);
+ RegexPathElement newRegexSection = new RegexPathElement(this.pathElementStart, pathElementText,
+ this.caseSensitive, this.pathPatternData, this.separator);
for (String variableName : newRegexSection.getVariableNames()) {
- recordCapturedVariable(pathElementStart, variableName);
+ recordCapturedVariable(this.pathElementStart, variableName);
}
newPE = newRegexSection;
}
}
else {
- if (wildcard) {
- if (pos - 1 == pathElementStart) {
- newPE = new WildcardPathElement(pathElementStart, separator);
+ if (this.wildcard) {
+ if (this.pos - 1 == this.pathElementStart) {
+ newPE = new WildcardPathElement(this.pathElementStart, this.separator);
}
else {
- newPE = new RegexPathElement(pathElementStart, pathElementText, caseSensitive, pathPatternData, separator);
+ newPE = new RegexPathElement(this.pathElementStart, pathElementText,
+ this.caseSensitive, this.pathPatternData, this.separator);
}
}
- else if (singleCharWildcardCount != 0) {
- newPE = new SingleCharWildcardedPathElement(pathElementStart, pathElementText,
- singleCharWildcardCount, caseSensitive, separator);
+ else if (this.singleCharWildcardCount != 0) {
+ newPE = new SingleCharWildcardedPathElement(this.pathElementStart, pathElementText,
+ this.singleCharWildcardCount, this.caseSensitive, this.separator);
}
else {
- newPE = new LiteralPathElement(pathElementStart, pathElementText, caseSensitive, separator);
+ newPE = new LiteralPathElement(this.pathElementStart, pathElementText,
+ this.caseSensitive, this.separator);
}
}
+
return newPE;
}
@@ -383,26 +401,27 @@ public class InternalPathPatternParser {
* Reset all the flags and position markers computed during path element processing.
*/
private void resetPathElementState() {
- pathElementStart = -1;
- singleCharWildcardCount = 0;
- insideVariableCapture = false;
- variableCaptureCount = 0;
- wildcard = false;
- isCaptureTheRestVariable = false;
- variableCaptureStart = -1;
+ this.pathElementStart = -1;
+ this.singleCharWildcardCount = 0;
+ this.insideVariableCapture = false;
+ this.variableCaptureCount = 0;
+ this.wildcard = false;
+ this.isCaptureTheRestVariable = false;
+ this.variableCaptureStart = -1;
}
/**
* Record a new captured variable. If it clashes with an existing one then report an error.
*/
private void recordCapturedVariable(int pos, String variableName) {
- if (capturedVariableNames == null) {
- capturedVariableNames = new ArrayList<>();
+ if (this.capturedVariableNames == null) {
+ this.capturedVariableNames = new ArrayList<>();
}
- if (capturedVariableNames.contains(variableName)) {
+ if (this.capturedVariableNames.contains(variableName)) {
throw new PatternParseException(pos, this.pathPatternData,
PatternMessage.ILLEGAL_DOUBLE_CAPTURE, variableName);
}
- capturedVariableNames.add(variableName);
+ this.capturedVariableNames.add(variableName);
}
-}
\ No newline at end of file
+
+}
diff --git a/spring-web/src/main/java/org/springframework/web/util/patterns/LiteralPathElement.java b/spring-web/src/main/java/org/springframework/web/util/pattern/LiteralPathElement.java
similarity index 76%
rename from spring-web/src/main/java/org/springframework/web/util/patterns/LiteralPathElement.java
rename to spring-web/src/main/java/org/springframework/web/util/pattern/LiteralPathElement.java
index 5a07ce13f7f..c72e4550963 100644
--- a/spring-web/src/main/java/org/springframework/web/util/patterns/LiteralPathElement.java
+++ b/spring-web/src/main/java/org/springframework/web/util/pattern/LiteralPathElement.java
@@ -14,9 +14,9 @@
* limitations under the License.
*/
-package org.springframework.web.util.patterns;
+package org.springframework.web.util.pattern;
-import org.springframework.web.util.patterns.PathPattern.MatchingContext;
+import org.springframework.web.util.pattern.PathPattern.MatchingContext;
/**
* A literal path element. In the pattern '/foo/bar/goo' there are three
@@ -51,11 +51,12 @@ class LiteralPathElement extends PathElement {
@Override
public boolean matches(int candidateIndex, MatchingContext matchingContext) {
if ((candidateIndex + text.length) > matchingContext.candidateLength) {
- return false; // not enough data, cannot be a match
+ return false; // not enough data, cannot be a match
}
- if (caseSensitive) {
+
+ if (this.caseSensitive) {
for (int i = 0; i < len; i++) {
- if (matchingContext.candidate[candidateIndex++] != text[i]) {
+ if (matchingContext.candidate[candidateIndex++] != this.text[i]) {
return false;
}
}
@@ -63,12 +64,13 @@ class LiteralPathElement extends PathElement {
else {
for (int i = 0; i < len; i++) {
// TODO revisit performance if doing a lot of case insensitive matching
- if (Character.toLowerCase(matchingContext.candidate[candidateIndex++]) != text[i]) {
+ if (Character.toLowerCase(matchingContext.candidate[candidateIndex++]) != this.text[i]) {
return false;
}
}
}
- if (next == null) {
+
+ if (this.next == null) {
if (matchingContext.determineRemainingPath && nextIfExistsIsSeparator(candidateIndex, matchingContext)) {
matchingContext.remainingPathIndex = candidateIndex;
return true;
@@ -78,17 +80,17 @@ class LiteralPathElement extends PathElement {
return true;
}
else {
- return matchingContext.isAllowOptionalTrailingSlash() &&
- (candidateIndex + 1) == matchingContext.candidateLength &&
- matchingContext.candidate[candidateIndex] == separator;
+ return (matchingContext.isAllowOptionalTrailingSlash() &&
+ (candidateIndex + 1) == matchingContext.candidateLength &&
+ matchingContext.candidate[candidateIndex] == separator);
}
}
}
else {
if (matchingContext.isMatchStartMatching && candidateIndex == matchingContext.candidateLength) {
- return true; // no more data but everything matched so far
+ return true; // no more data but everything matched so far
}
- return next.matches(candidateIndex, matchingContext);
+ return this.next.matches(candidateIndex, matchingContext);
}
}
@@ -97,8 +99,9 @@ class LiteralPathElement extends PathElement {
return len;
}
+
public String toString() {
- return "Literal(" + new String(text) + ")";
+ return "Literal(" + String.valueOf(this.text) + ")";
}
-}
\ No newline at end of file
+}
diff --git a/spring-web/src/main/java/org/springframework/web/util/ParsingPathMatcher.java b/spring-web/src/main/java/org/springframework/web/util/pattern/ParsingPathMatcher.java
similarity index 66%
rename from spring-web/src/main/java/org/springframework/web/util/ParsingPathMatcher.java
rename to spring-web/src/main/java/org/springframework/web/util/pattern/ParsingPathMatcher.java
index 4344e7183c5..e78ce390165 100644
--- a/spring-web/src/main/java/org/springframework/web/util/ParsingPathMatcher.java
+++ b/spring-web/src/main/java/org/springframework/web/util/pattern/ParsingPathMatcher.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package org.springframework.web.util;
+package org.springframework.web.util.pattern;
import java.util.Comparator;
import java.util.Map;
@@ -22,10 +22,6 @@ import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import org.springframework.util.PathMatcher;
-import org.springframework.web.util.patterns.PathPattern;
-import org.springframework.web.util.patterns.PathPatternParser;
-import org.springframework.web.util.patterns.PatternComparatorConsideringPath;
-
/**
* {@link PathMatcher} implementation for path patterns parsed
@@ -35,38 +31,50 @@ import org.springframework.web.util.patterns.PatternComparatorConsideringPath;
* and quick comparison.
*
* @author Andy Clement
+ * @author Juergen Hoeller
* @since 5.0
* @see PathPattern
*/
public class ParsingPathMatcher implements PathMatcher {
- private final ConcurrentMap cache =
- new ConcurrentHashMap<>(64);
-
private final PathPatternParser parser = new PathPatternParser();
+ private final ConcurrentMap cache = new ConcurrentHashMap<>(256);
+
+
+ @Override
+ public boolean isPattern(String path) {
+ // TODO crude, should be smarter, lookup pattern and ask it
+ return (path.indexOf('*') != -1 || path.indexOf('?') != -1);
+ }
+
@Override
public boolean match(String pattern, String path) {
- PathPattern p = getPathPattern(pattern);
- return p.matches(path);
+ PathPattern pathPattern = getPathPattern(pattern);
+ return pathPattern.matches(path);
}
@Override
public boolean matchStart(String pattern, String path) {
- PathPattern p = getPathPattern(pattern);
- return p.matchStart(path);
+ PathPattern pathPattern = getPathPattern(pattern);
+ return pathPattern.matchStart(path);
}
@Override
public String extractPathWithinPattern(String pattern, String path) {
- PathPattern p = getPathPattern(pattern);
- return p.extractPathWithinPattern(path);
+ PathPattern pathPattern = getPathPattern(pattern);
+ return pathPattern.extractPathWithinPattern(path);
}
@Override
public Map extractUriTemplateVariables(String pattern, String path) {
- PathPattern p = getPathPattern(pattern);
- return p.matchAndExtract(path);
+ PathPattern pathPattern = getPathPattern(pattern);
+ return pathPattern.matchAndExtract(path);
+ }
+
+ @Override
+ public Comparator getPatternComparator(String path) {
+ return new PathPatternStringComparatorConsideringPath(path);
}
@Override
@@ -75,12 +83,17 @@ public class ParsingPathMatcher implements PathMatcher {
return pathPattern.combine(pattern2);
}
- @Override
- public Comparator getPatternComparator(String path) {
- return new PathPatternStringComparatorConsideringPath(path);
+ private PathPattern getPathPattern(String pattern) {
+ PathPattern pathPattern = this.cache.get(pattern);
+ if (pathPattern == null) {
+ pathPattern = this.parser.parse(pattern);
+ this.cache.put(pattern, pathPattern);
+ }
+ return pathPattern;
}
- class PathPatternStringComparatorConsideringPath implements Comparator {
+
+ private class PathPatternStringComparatorConsideringPath implements Comparator {
private final PatternComparatorConsideringPath ppcp;
@@ -100,22 +113,38 @@ public class ParsingPathMatcher implements PathMatcher {
PathPattern p2 = getPathPattern(o2);
return this.ppcp.compare(p1, p2);
}
-
}
- @Override
- public boolean isPattern(String path) {
- // TODO crude, should be smarter, lookup pattern and ask it
- return (path.indexOf('*') != -1 || path.indexOf('?') != -1);
- }
- private PathPattern getPathPattern(String pattern) {
- PathPattern pathPattern = this.cache.get(pattern);
- if (pathPattern == null) {
- pathPattern = this.parser.parse(pattern);
- this.cache.put(pattern, pathPattern);
+ /**
+ * {@link PathPattern} comparator that takes account of a specified
+ * path and sorts anything that exactly matches it to be first.
+ */
+ static class PatternComparatorConsideringPath implements Comparator {
+
+ private final String path;
+
+ public PatternComparatorConsideringPath(String path) {
+ this.path = path;
+ }
+
+ @Override
+ public int compare(PathPattern o1, PathPattern o2) {
+ // Nulls get sorted to the end
+ if (o1 == null) {
+ return (o2 == null ? 0 : +1);
+ }
+ else if (o2 == null) {
+ return -1;
+ }
+ if (o1.getPatternString().equals(this.path)) {
+ return (o2.getPatternString().equals(this.path)) ? 0 : -1;
+ }
+ else if (o2.getPatternString().equals(this.path)) {
+ return +1;
+ }
+ return o1.compareTo(o2);
}
- return pathPattern;
}
}
diff --git a/spring-web/src/main/java/org/springframework/web/util/patterns/PathElement.java b/spring-web/src/main/java/org/springframework/web/util/pattern/PathElement.java
similarity index 67%
rename from spring-web/src/main/java/org/springframework/web/util/patterns/PathElement.java
rename to spring-web/src/main/java/org/springframework/web/util/pattern/PathElement.java
index fc308b3f7f3..83509a497e6 100644
--- a/spring-web/src/main/java/org/springframework/web/util/patterns/PathElement.java
+++ b/spring-web/src/main/java/org/springframework/web/util/pattern/PathElement.java
@@ -14,9 +14,9 @@
* limitations under the License.
*/
-package org.springframework.web.util.patterns;
+package org.springframework.web.util.pattern;
-import org.springframework.web.util.patterns.PathPattern.MatchingContext;
+import org.springframework.web.util.pattern.PathPattern.MatchingContext;
/**
* Common supertype for the Ast nodes created to represent a path pattern.
@@ -31,25 +31,19 @@ abstract class PathElement {
protected static final int CAPTURE_VARIABLE_WEIGHT = 1;
- /**
- * Position in the pattern where this path element starts
- */
- protected int pos;
- /**
- * The next path element in the chain
- */
+ // Position in the pattern where this path element starts
+ protected final int pos;
+
+ // The separator used in this path pattern
+ protected final char separator;
+
+ // The next path element in the chain
protected PathElement next;
- /**
- * The previous path element in the chain
- */
+ // The previous path element in the chain
protected PathElement prev;
-
- /**
- * The separator used in this path pattern
- */
- protected char separator;
+
/**
* Create a new path element.
@@ -61,46 +55,47 @@ abstract class PathElement {
this.separator = separator;
}
+
/**
* Attempt to match this path element.
- *
* @param candidatePos the current position within the candidate path
* @param matchingContext encapsulates context for the match including the candidate
- * @return true if matches, otherwise false
+ * @return {@code true} if it matches, otherwise {@code false}
*/
public abstract boolean matches(int candidatePos, MatchingContext matchingContext);
/**
- * @return the length of the path element where captures are considered to be one character long
+ * Return the length of the path element where captures are considered to be one character long.
*/
public abstract int getNormalizedLength();
/**
- * @return the number of variables captured by the path element
+ * Return the number of variables captured by the path element.
*/
public int getCaptureCount() {
return 0;
}
/**
- * @return the number of wildcard elements (*, ?) in the path element
+ * Return the number of wildcard elements (*, ?) in the path element.
*/
public int getWildcardCount() {
return 0;
}
/**
- * @return the score for this PathElement, combined score is used to compare parsed patterns.
+ * Return the score for this PathElement, combined score is used to compare parsed patterns.
*/
public int getScore() {
return 0;
}
/**
- * @return true if there is no next character, or if there is then it is a separator
+ * Return {@code true} if there is no next character, or if there is then it is a separator.
*/
protected boolean nextIfExistsIsSeparator(int nextIndex, MatchingContext matchingContext) {
return (nextIndex >= matchingContext.candidateLength ||
- matchingContext.candidate[nextIndex] == separator);
+ matchingContext.candidate[nextIndex] == this.separator);
}
-}
\ No newline at end of file
+
+}
diff --git a/spring-web/src/main/java/org/springframework/web/util/patterns/PathPattern.java b/spring-web/src/main/java/org/springframework/web/util/pattern/PathPattern.java
similarity index 72%
rename from spring-web/src/main/java/org/springframework/web/util/patterns/PathPattern.java
rename to spring-web/src/main/java/org/springframework/web/util/pattern/PathPattern.java
index d31868057b3..9c17ce67966 100644
--- a/spring-web/src/main/java/org/springframework/web/util/patterns/PathPattern.java
+++ b/spring-web/src/main/java/org/springframework/web/util/pattern/PathPattern.java
@@ -14,14 +14,15 @@
* limitations under the License.
*/
-package org.springframework.web.util.patterns;
+package org.springframework.web.util.pattern;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import org.springframework.util.PathMatcher;
-import static org.springframework.util.StringUtils.hasLength;
+
+import static org.springframework.util.StringUtils.*;
/**
* Represents a parsed path pattern. Includes a chain of path elements
@@ -62,8 +63,6 @@ import static org.springframework.util.StringUtils.hasLength;
*/
public class PathPattern implements Comparable {
- private final static Map NO_VARIABLES_MAP = Collections.emptyMap();
-
/** First path element in the parsed chain of path elements for this pattern */
private PathElement head;
@@ -88,12 +87,12 @@ public class PathPattern implements Comparable {
* your variable name lengths isn't going to change the length of the active part of the pattern.
* Useful when comparing two patterns.
*/
- int normalizedLength;
+ private int normalizedLength;
/**
* Does the pattern end with '<separator>*'
*/
- boolean endsWithSeparatorWildcard = false;
+ private boolean endsWithSeparatorWildcard = false;
/**
* Score is used to quickly compare patterns. Different pattern components are given different
@@ -106,41 +105,58 @@ public class PathPattern implements Comparable {
private int score;
/** Does the pattern end with {*...} */
- private boolean isCatchAll = false;
+ private boolean catchAll = false;
+
+
+ PathPattern(String patternText, PathElement head, char separator, boolean caseSensitive,
+ boolean allowOptionalTrailingSlash) {
- public PathPattern(String patternText, PathElement head, char separator, boolean caseSensitive, boolean allowOptionalTrailingSlash) {
- this.head = head;
this.patternString = patternText;
+ this.head = head;
this.separator = separator;
this.caseSensitive = caseSensitive;
this.allowOptionalTrailingSlash = allowOptionalTrailingSlash;
+
// Compute fields for fast comparison
- PathElement s = head;
- while (s != null) {
- this.capturedVariableCount += s.getCaptureCount();
- this.normalizedLength += s.getNormalizedLength();
- this.score += s.getScore();
- if (s instanceof CaptureTheRestPathElement || s instanceof WildcardTheRestPathElement) {
- this.isCatchAll = true;
+ PathElement elem = head;
+ while (elem != null) {
+ this.capturedVariableCount += elem.getCaptureCount();
+ this.normalizedLength += elem.getNormalizedLength();
+ this.score += elem.getScore();
+ if (elem instanceof CaptureTheRestPathElement || elem instanceof WildcardTheRestPathElement) {
+ this.catchAll = true;
}
- if (s instanceof SeparatorPathElement && s.next != null
- && s.next instanceof WildcardPathElement && s.next.next == null) {
+ if (elem instanceof SeparatorPathElement && elem.next != null &&
+ elem.next instanceof WildcardPathElement && elem.next.next == null) {
this.endsWithSeparatorWildcard = true;
}
- s = s.next;
+ elem = elem.next;
}
}
+
+ /**
+ * Return the original pattern string that was parsed to create this PathPattern.
+ */
+ public String getPatternString() {
+ return this.patternString;
+ }
+
+ PathElement getHeadSection() {
+ return this.head;
+ }
+
+
/**
* @param path the candidate path to attempt to match against this pattern
* @return true if the path matches this pattern
*/
public boolean matches(String path) {
- if (head == null) {
+ if (this.head == null) {
return !hasLength(path);
}
else if (!hasLength(path)) {
- if (head instanceof WildcardTheRestPathElement || head instanceof CaptureTheRestPathElement) {
+ if (this.head instanceof WildcardTheRestPathElement || this.head instanceof CaptureTheRestPathElement) {
path = ""; // Will allow CaptureTheRest to bind the variable to empty
}
else {
@@ -148,31 +164,31 @@ public class PathPattern implements Comparable {
}
}
MatchingContext matchingContext = new MatchingContext(path, false);
- return head.matches(0, matchingContext);
+ return this.head.matches(0, matchingContext);
}
/**
* For a given path return the remaining piece that is not covered by this PathPattern.
- *
* @param path a path that may or may not match this path pattern
- * @return a {@link PathRemainingMatchInfo} describing the match result or null if the path does not match
- * this pattern
+ * @return a {@link PathRemainingMatchInfo} describing the match result or null if
+ * the path does not match this pattern
*/
public PathRemainingMatchInfo getPathRemaining(String path) {
- if (head == null) {
+ if (this.head == null) {
if (path == null) {
- return new PathRemainingMatchInfo(path);
+ return new PathRemainingMatchInfo(null);
}
else {
- return new PathRemainingMatchInfo(hasLength(path)?path:"");
+ return new PathRemainingMatchInfo(hasLength(path) ? path : "");
}
}
else if (!hasLength(path)) {
return null;
}
+
MatchingContext matchingContext = new MatchingContext(path, true);
matchingContext.setMatchAllowExtraPath();
- boolean matches = head.matches(0, matchingContext);
+ boolean matches = this.head.matches(0, matchingContext);
if (!matches) {
return null;
}
@@ -194,7 +210,7 @@ public class PathPattern implements Comparable {
* @return true if the pattern matches as much of the path as is supplied
*/
public boolean matchStart(String path) {
- if (head == null) {
+ if (this.head == null) {
return !hasLength(path);
}
else if (!hasLength(path)) {
@@ -202,7 +218,7 @@ public class PathPattern implements Comparable {
}
MatchingContext matchingContext = new MatchingContext(path, false);
matchingContext.setMatchStartMatching(true);
- return head.matches(0, matchingContext);
+ return this.head.matches(0, matchingContext);
}
/**
@@ -212,31 +228,19 @@ public class PathPattern implements Comparable {
*/
public Map matchAndExtract(String path) {
MatchingContext matchingContext = new MatchingContext(path, true);
- if (head != null && head.matches(0, matchingContext)) {
+ if (this.head != null && this.head.matches(0, matchingContext)) {
return matchingContext.getExtractedVariables();
}
else {
if (!hasLength(path)) {
- return NO_VARIABLES_MAP;
+ return Collections.emptyMap();
}
else {
- throw new IllegalStateException("Pattern \"" + this.toString()
- + "\" is not a match for \"" + path + "\"");
+ throw new IllegalStateException("Pattern \"" + this + "\" is not a match for \"" + path + "\"");
}
}
}
- /**
- * @return the original pattern string that was parsed to create this PathPattern
- */
- public String getPatternString() {
- return patternString;
- }
-
- public PathElement getHeadSection() {
- return head;
- }
-
/**
* Given a full path, determine the pattern-mapped part. For example:
* - '{@code /docs/cvs/commit.html}' and '{@code /docs/cvs/commit.html} -> ''
@@ -253,26 +257,29 @@ public class PathPattern implements Comparable {
*/
public String extractPathWithinPattern(String path) {
// assert this.matches(path)
- PathElement s = head;
+ PathElement elem = head;
int separatorCount = 0;
boolean matchTheRest = false;
+
// Find first path element that is pattern based
- while (s != null) {
- if (s instanceof SeparatorPathElement || s instanceof CaptureTheRestPathElement
- || s instanceof WildcardTheRestPathElement) {
+ while (elem != null) {
+ if (elem instanceof SeparatorPathElement || elem instanceof CaptureTheRestPathElement ||
+ elem instanceof WildcardTheRestPathElement) {
separatorCount++;
- if (s instanceof WildcardTheRestPathElement || s instanceof CaptureTheRestPathElement) {
+ if (elem instanceof WildcardTheRestPathElement || elem instanceof CaptureTheRestPathElement) {
matchTheRest = true;
}
}
- if (s.getWildcardCount() != 0 || s.getCaptureCount() != 0) {
+ if (elem.getWildcardCount() != 0 || elem.getCaptureCount() != 0) {
break;
}
- s = s.next;
+ elem = elem.next;
}
- if (s == null) {
- return ""; // There is no pattern mapped section
+
+ if (elem == null) {
+ return ""; // there is no pattern mapped section
}
+
// Now separatorCount indicates how many sections of the path to skip
char[] pathChars = path.toCharArray();
int len = pathChars.length;
@@ -289,6 +296,7 @@ public class PathPattern implements Comparable {
end--;
}
}
+
// Check if multiple separators embedded in the resulting path, if so trim them out.
// Example: aaa////bbb//ccc/d -> aaa/bbb/ccc/d
// The stringWithDuplicateSeparatorsRemoved is only computed if necessary
@@ -314,28 +322,31 @@ public class PathPattern implements Comparable {
}
c++;
}
+
if (stringWithDuplicateSeparatorsRemoved != null) {
return stringWithDuplicateSeparatorsRemoved.toString();
}
- return pos == len ? "" : path.substring(pos, end);
+ return (pos == len ? "" : path.substring(pos, end));
}
+
/**
- * Compare this pattern with a supplied pattern. Return -1,0,+1 if this pattern
+ * Compare this pattern with a supplied pattern: return -1,0,+1 if this pattern
* is more specific, the same or less specific than the supplied pattern.
* The aim is to sort more specific patterns first.
*/
@Override
- public int compareTo(PathPattern p) {
+ public int compareTo(PathPattern otherPattern) {
// 1) null is sorted last
- if (p == null) {
+ if (otherPattern == null) {
return -1;
}
+
// 2) catchall patterns are sorted last. If both catchall then the
// length is considered
if (isCatchAll()) {
- if (p.isCatchAll()) {
- int lenDifference = this.getNormalizedLength() - p.getNormalizedLength();
+ if (otherPattern.isCatchAll()) {
+ int lenDifference = getNormalizedLength() - otherPattern.getNormalizedLength();
if (lenDifference != 0) {
return (lenDifference < 0) ? +1 : -1;
}
@@ -344,26 +355,28 @@ public class PathPattern implements Comparable {
return +1;
}
}
- else if (p.isCatchAll()) {
+ else if (otherPattern.isCatchAll()) {
return -1;
}
+
// 3) This will sort such that if they differ in terms of wildcards or
// captured variable counts, the one with the most will be sorted last
- int score = this.getScore() - p.getScore();
+ int score = getScore() - otherPattern.getScore();
if (score != 0) {
return (score < 0) ? -1 : +1;
}
+
// 4) longer is better
- int lenDifference = this.getNormalizedLength() - p.getNormalizedLength();
+ int lenDifference = getNormalizedLength() - otherPattern.getNormalizedLength();
return (lenDifference < 0) ? +1 : (lenDifference == 0 ? 0 : -1);
}
- public int getScore() {
- return score;
+ int getScore() {
+ return this.score;
}
- public boolean isCatchAll() {
- return isCatchAll;
+ boolean isCatchAll() {
+ return this.catchAll;
}
/**
@@ -371,28 +384,118 @@ public class PathPattern implements Comparable {
* by assuming all capture variables have a normalized length of 1. Effectively this means changing
* your variable name lengths isn't going to change the length of the active part of the pattern.
* Useful when comparing two patterns.
- * @return the normalized length of the pattern
*/
- public int getNormalizedLength() {
- return normalizedLength;
+ int getNormalizedLength() {
+ return this.normalizedLength;
+ }
+
+ char getSeparator() {
+ return this.separator;
+ }
+
+ int getCapturedVariableCount() {
+ return this.capturedVariableCount;
+ }
+
+
+ /**
+ * Combine this pattern with another. Currently does not produce a new PathPattern, just produces a new string.
+ */
+ public String combine(String pattern2string) {
+ // If one of them is empty the result is the other. If both empty the result is ""
+ if (!hasLength(this.patternString)) {
+ if (!hasLength(pattern2string)) {
+ return "";
+ }
+ else {
+ return pattern2string;
+ }
+ }
+ else if (!hasLength(pattern2string)) {
+ return this.patternString;
+ }
+
+ // /* + /hotel => /hotel
+ // /*.* + /*.html => /*.html
+ // However:
+ // /usr + /user => /usr/user
+ // /{foo} + /bar => /{foo}/bar
+ if (!this.patternString.equals(pattern2string) &&this. capturedVariableCount == 0 && matches(pattern2string)) {
+ return pattern2string;
+ }
+
+ // /hotels/* + /booking => /hotels/booking
+ // /hotels/* + booking => /hotels/booking
+ if (this.endsWithSeparatorWildcard) {
+ return concat(this.patternString.substring(0, this.patternString.length() - 2), pattern2string);
+ }
+
+ // /hotels + /booking => /hotels/booking
+ // /hotels + booking => /hotels/booking
+ int starDotPos1 = this.patternString.indexOf("*."); // Are there any file prefix/suffix things to consider?
+ if (this.capturedVariableCount != 0 || starDotPos1 == -1 || this.separator == '.') {
+ return concat(this.patternString, pattern2string);
+ }
+
+ // /*.html + /hotel => /hotel.html
+ // /*.html + /hotel.* => /hotel.html
+ String firstExtension = this.patternString.substring(starDotPos1 + 1); // looking for the first extension
+ int dotPos2 = pattern2string.indexOf('.');
+ String file2 = (dotPos2 == -1 ? pattern2string : pattern2string.substring(0, dotPos2));
+ String secondExtension = (dotPos2 == -1 ? "" : pattern2string.substring(dotPos2));
+ boolean firstExtensionWild = (firstExtension.equals(".*") || firstExtension.equals(""));
+ boolean secondExtensionWild = (secondExtension.equals(".*") || secondExtension.equals(""));
+ if (!firstExtensionWild && !secondExtensionWild) {
+ throw new IllegalArgumentException(
+ "Cannot combine patterns: " + this.patternString + " and " + pattern2string);
+ }
+ return file2 + (firstExtensionWild ? secondExtension : firstExtension);
+ }
+
+ /**
+ * Join two paths together including a separator if necessary.
+ * Extraneous separators are removed (if the first path
+ * ends with one and the second path starts with one).
+ * @param path1 first path
+ * @param path2 second path
+ * @return joined path that may include separator if necessary
+ */
+ private String concat(String path1, String path2) {
+ boolean path1EndsWithSeparator = (path1.charAt(path1.length() - 1) == this.separator);
+ boolean path2StartsWithSeparator = (path2.charAt(0) == this.separator);
+ if (path1EndsWithSeparator && path2StartsWithSeparator) {
+ return path1 + path2.substring(1);
+ }
+ else if (path1EndsWithSeparator || path2StartsWithSeparator) {
+ return path1 + path2;
+ }
+ else {
+ return path1 + this.separator + path2;
+ }
}
- public boolean equals(Object o) {
- if (!(o instanceof PathPattern)) {
+
+ public boolean equals(Object other) {
+ if (!(other instanceof PathPattern)) {
return false;
}
- PathPattern p = (PathPattern) o;
- return patternString.equals(p.getPatternString()) && separator == p.getSeparator()
- && caseSensitive == p.caseSensitive;
+ PathPattern otherPattern = (PathPattern) other;
+ return (this.patternString.equals(otherPattern.getPatternString()) &&
+ this.separator == otherPattern.getSeparator() &&
+ this.caseSensitive == otherPattern.caseSensitive);
}
public int hashCode() {
- return (patternString.hashCode() * 17 + separator) * 17 + (caseSensitive ? 1 : 0);
+ return (this.patternString.hashCode() + this.separator) * 17 + (this.caseSensitive ? 1 : 0);
+ }
+
+ public String toString() {
+ return this.patternString;
}
- public String toChainString() {
+ String toChainString() {
StringBuilder buf = new StringBuilder();
- PathElement pe = head;
+ PathElement pe = this.head;
while (pe != null) {
buf.append(pe.toString()).append(" ");
pe = pe.next;
@@ -400,18 +503,44 @@ public class PathPattern implements Comparable {
return buf.toString().trim();
}
- public char getSeparator() {
- return separator;
- }
- public int getCapturedVariableCount() {
- return capturedVariableCount;
- }
+ /**
+ * A holder for the result of a {@link PathPattern#getPathRemaining(String)} 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.
+ */
+ public static class PathRemainingMatchInfo {
- public String toString() {
- return patternString;
+ private final String pathRemaining;
+
+ private final Map matchingVariables;
+
+ PathRemainingMatchInfo(String pathRemaining) {
+ this(pathRemaining, Collections.emptyMap());
+ }
+
+ PathRemainingMatchInfo(String pathRemaining, Map matchingVariables) {
+ this.pathRemaining = pathRemaining;
+ this.matchingVariables = matchingVariables;
+ }
+
+ /**
+ * Return the part of a path that was not matched by a pattern.
+ */
+ public String getPathRemaining() {
+ return this.pathRemaining;
+ }
+
+ /**
+ * 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
+ */
+ public Map getMatchingVariables() {
+ return this.matchingVariables;
+ }
}
+
/**
* Encapsulates context when attempting a match. Includes some fixed state like the
* candidate currently being considered for a match but also some accumulators for
@@ -430,9 +559,9 @@ public class PathPattern implements Comparable {
private Map extractedVariables;
boolean extractingVariables;
-
+
boolean determineRemainingPath = false;
-
+
// if determineRemaining is true, this is set to the position in
// the candidate where the pattern finished matching - i.e. it
// points to the remaining path that wasn't consumed
@@ -447,7 +576,7 @@ public class PathPattern implements Comparable {
public void setMatchAllowExtraPath() {
determineRemainingPath = true;
}
-
+
public boolean isAllowOptionalTrailingSlash() {
return allowOptionalTrailingSlash;
}
@@ -465,7 +594,7 @@ public class PathPattern implements Comparable {
public Map getExtractedVariables() {
if (this.extractedVariables == null) {
- return NO_VARIABLES_MAP;
+ return Collections.emptyMap();
}
else {
return this.extractedVariables;
@@ -475,7 +604,6 @@ public class PathPattern implements Comparable {
/**
* Scan ahead from the specified position for either the next separator
* character or the end of the candidate.
- *
* @param pos the starting position for the scan
* @return the position of the next separator or the end of the candidate
*/
@@ -490,79 +618,4 @@ public class PathPattern implements Comparable {
}
}
- /**
- * Combine this pattern with another. Currently does not produce a new PathPattern, just produces a new string.
- */
- public String combine(String pattern2string) {
- // If one of them is empty the result is the other. If both empty the result is ""
- if (!hasLength(patternString)) {
- if (!hasLength(pattern2string)) {
- return "";
- }
- else {
- return pattern2string;
- }
- }
- else if (!hasLength(pattern2string)) {
- return patternString;
- }
-
- // /* + /hotel => /hotel
- // /*.* + /*.html => /*.html
- // However:
- // /usr + /user => /usr/user
- // /{foo} + /bar => /{foo}/bar
- if (!patternString.equals(pattern2string) && capturedVariableCount == 0 && matches(pattern2string)) {
- return pattern2string;
- }
-
- // /hotels/* + /booking => /hotels/booking
- // /hotels/* + booking => /hotels/booking
- if (endsWithSeparatorWildcard) {
- return concat(patternString.substring(0, patternString.length() - 2), pattern2string);
- }
-
- // /hotels + /booking => /hotels/booking
- // /hotels + booking => /hotels/booking
- int starDotPos1 = patternString.indexOf("*."); // Are there any file prefix/suffix things to consider?
- if (capturedVariableCount != 0 || starDotPos1 == -1 || separator == '.') {
- return concat(patternString, pattern2string);
- }
-
- // /*.html + /hotel => /hotel.html
- // /*.html + /hotel.* => /hotel.html
- String firstExtension = patternString.substring(starDotPos1 + 1); // looking for the first extension
- int dotPos2 = pattern2string.indexOf('.');
- String file2 = (dotPos2 == -1 ? pattern2string : pattern2string.substring(0, dotPos2));
- String secondExtension = (dotPos2 == -1 ? "" : pattern2string.substring(dotPos2));
- boolean firstExtensionWild = (firstExtension.equals(".*") || firstExtension.equals(""));
- boolean secondExtensionWild = (secondExtension.equals(".*") || secondExtension.equals(""));
- if (!firstExtensionWild && !secondExtensionWild) {
- throw new IllegalArgumentException("Cannot combine patterns: " + patternString + " and " + pattern2string);
- }
- return file2 + (firstExtensionWild ? secondExtension : firstExtension);
- }
-
- /**
- * Join two paths together including a separator if necessary.
- * Extraneous separators are removed (if the first path
- * ends with one and the second path starts with one).
- * @param path1 First path
- * @param path2 Second path
- * @return joined path that may include separator if necessary
- */
- private String concat(String path1, String path2) {
- boolean path1EndsWithSeparator = path1.charAt(path1.length() - 1) == separator;
- boolean path2StartsWithSeparator = path2.charAt(0) == separator;
- if (path1EndsWithSeparator && path2StartsWithSeparator) {
- return path1 + path2.substring(1);
- }
- else if (path1EndsWithSeparator || path2StartsWithSeparator) {
- return path1 + path2;
- }
- else {
- return path1 + separator + path2;
- }
- }
-
}
diff --git a/spring-web/src/main/java/org/springframework/web/util/patterns/PathPatternParser.java b/spring-web/src/main/java/org/springframework/web/util/pattern/PathPatternParser.java
similarity index 68%
rename from spring-web/src/main/java/org/springframework/web/util/patterns/PathPatternParser.java
rename to spring-web/src/main/java/org/springframework/web/util/pattern/PathPatternParser.java
index 4f0970a39ac..b3b0d6a6c67 100644
--- a/spring-web/src/main/java/org/springframework/web/util/patterns/PathPatternParser.java
+++ b/spring-web/src/main/java/org/springframework/web/util/pattern/PathPatternParser.java
@@ -14,11 +14,11 @@
* limitations under the License.
*/
-package org.springframework.web.util.patterns;
+package org.springframework.web.util.pattern;
/**
- * Parser for URI template patterns. It breaks the path pattern into a number of
- * {@link PathElement}s in a linked list.
+ * Parser for URI template patterns. It breaks the path pattern into a number
+ * of {@link PathElement}s in a linked list.
*
* @author Andy Clement
* @since 5.0
@@ -27,62 +27,69 @@ public class PathPatternParser {
public final static char DEFAULT_SEPARATOR = '/';
- // Is the parser producing case sensitive PathPattern matchers, default true
- private boolean caseSensitive = true;
- // The expected path separator to split path elements during parsing, default '/'
+ // The expected path separator to split path elements during parsing.
private char separator = DEFAULT_SEPARATOR;
- // If true the PathPatterns produced by the parser will allow patterns
- // that don't have a trailing slash to match paths that may or may not
- // have a trailing slash
+ // Whether the PathPatterns produced by the parser will allow patterns that don't
+ // have a trailing slash to match paths that may or may not have a trailing slash.
private boolean matchOptionalTrailingSlash = false;
+ // If the parser produces case-sensitive PathPattern matchers.
+ private boolean caseSensitive = true;
+
+
/**
- * Create a path pattern parser that will use the default separator '/' when
- * parsing patterns.
+ * Create a path pattern parser that will use the default separator '/'
+ * when parsing patterns.
+ * @see #DEFAULT_SEPARATOR
*/
public PathPatternParser() {
}
-
- /**
- * Control behavior of the path patterns produced by this parser. The default
- * value for matchOptionalTrailingSlash is true but here it can be set to false.
- * If true then PathPatterns without a trailing slash will match paths with or
- * without a trailing slash.
- *
- * @param matchOptionalTrailingSlash boolean value to override the default value of true
- */
- public void setMatchOptionalTrailingSlash(boolean matchOptionalTrailingSlash) {
- this.matchOptionalTrailingSlash = matchOptionalTrailingSlash;
- }
-
+
/**
* Create a path pattern parser that will use the supplied separator when
* parsing patterns.
- * @param separator the separator expected to divide pattern elements parsed by this parser
+ * @param separator the separator expected to divide pattern elements
*/
public PathPatternParser(char separator) {
this.separator = separator;
}
+
+ /**
+ * Control behavior of the path patterns produced by this parser: if {@code true}
+ * then PathPatterns without a trailing slash will match paths with or without
+ * a trailing slash.
+ * The default is {@code true} but here this flag can be set to {@code false}.
+ */
+ public void setMatchOptionalTrailingSlash(boolean matchOptionalTrailingSlash) {
+ this.matchOptionalTrailingSlash = matchOptionalTrailingSlash;
+ }
+
+ /**
+ * Set whether path patterns are case-sensitive.
+ *
The default is {@code true}.
+ */
public void setCaseSensitive(boolean caseSensitive) {
this.caseSensitive = caseSensitive;
}
+
/**
* 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. Each invocation of this method delegates to a new instance of
* the {@link InternalPathPatternParser} because that class is not thread-safe.
- *
* @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
*/
- public PathPattern parse(String pathPattern) {
- InternalPathPatternParser ippp = new InternalPathPatternParser(separator, caseSensitive, matchOptionalTrailingSlash);
- return ippp.parse(pathPattern);
+ public PathPattern parse(String pathPattern) throws PatternParseException {
+ InternalPathPatternParser parserDelegate =
+ new InternalPathPatternParser(this.separator, this.caseSensitive, this.matchOptionalTrailingSlash);
+ return parserDelegate.parse(pathPattern);
}
}
diff --git a/spring-web/src/main/java/org/springframework/web/util/pattern/PatternParseException.java b/spring-web/src/main/java/org/springframework/web/util/pattern/PatternParseException.java
new file mode 100644
index 00000000000..77e99f57ac8
--- /dev/null
+++ b/spring-web/src/main/java/org/springframework/web/util/pattern/PatternParseException.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright 2002-2017 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.web.util.pattern;
+
+import java.text.MessageFormat;
+
+/**
+ * Exception that is thrown when there is a problem with the pattern being parsed.
+ *
+ * @author Andy Clement
+ * @since 5.0
+ */
+@SuppressWarnings("serial")
+public class PatternParseException extends IllegalArgumentException {
+
+ private final int position;
+
+ private final char[] pattern;
+
+ private final PatternMessage messageType;
+
+ private final Object[] inserts;
+
+
+ PatternParseException(int pos, char[] pattern, PatternMessage messageType, Object... inserts) {
+ super(messageType.formatMessage(inserts));
+ this.position = pos;
+ this.pattern = pattern;
+ this.messageType = messageType;
+ this.inserts = inserts;
+ }
+
+ PatternParseException(Throwable cause, int pos, char[] pattern, PatternMessage messageType, Object... inserts) {
+ super(messageType.formatMessage(inserts), cause);
+ this.position = pos;
+ this.pattern = pattern;
+ this.messageType = messageType;
+ this.inserts = inserts;
+ }
+
+
+ /**
+ * Return a formatted message with inserts applied.
+ */
+ @Override
+ public String getMessage() {
+ return this.messageType.formatMessage(this.inserts);
+ }
+
+ /**
+ * Return a detailed message that includes the original pattern text
+ * with a pointer to the error position, as well as the error message.
+ */
+ public String toDetailedString() {
+ StringBuilder buf = new StringBuilder();
+ buf.append(this.pattern).append('\n');
+ for (int i = 0; i < this.position; i++) {
+ buf.append(' ');
+ }
+ buf.append("^\n");
+ buf.append(getMessage());
+ return buf.toString();
+ }
+
+ public int getPosition() {
+ return this.position;
+ }
+
+ public PatternMessage getMessageType() {
+ return this.messageType;
+ }
+
+ public Object[] getInserts() {
+ return this.inserts;
+ }
+
+
+ /**
+ * The messages that can be included in a {@link PatternParseException} when there is a parse failure.
+ */
+ public enum PatternMessage {
+
+ MISSING_CLOSE_CAPTURE("Expected close capture character after variable name '}'"),
+ MISSING_OPEN_CAPTURE("Missing preceeding open capture character before variable name'{'"),
+ ILLEGAL_NESTED_CAPTURE("Not allowed to nest variable captures"),
+ CANNOT_HAVE_ADJACENT_CAPTURES("Adjacent captures are not allowed"),
+ ILLEGAL_CHARACTER_AT_START_OF_CAPTURE_DESCRIPTOR("Character ''{0}'' is not allowed at start of captured variable name"),
+ ILLEGAL_CHARACTER_IN_CAPTURE_DESCRIPTOR("Character ''{0}'' is not allowed in a captured variable name"),
+ NO_MORE_DATA_EXPECTED_AFTER_CAPTURE_THE_REST("No more pattern data allowed after '{*...}' pattern element"),
+ BADLY_FORMED_CAPTURE_THE_REST("Expected form when capturing the rest of the path is simply '{*...}'"),
+ MISSING_REGEX_CONSTRAINT("Missing regex constraint on capture"),
+ ILLEGAL_DOUBLE_CAPTURE("Not allowed to capture ''{0}'' twice in the same pattern"),
+ REGEX_PATTERN_SYNTAX_EXCEPTION("Exception occurred in regex pattern compilation"),
+ CAPTURE_ALL_IS_STANDALONE_CONSTRUCT("'{*...}' can only be preceeded by a path separator");
+
+ private final String message;
+
+ PatternMessage(String message) {
+ this.message = message;
+ }
+
+ public String formatMessage(Object... inserts) {
+ return MessageFormat.format(this.message, inserts);
+ }
+ }
+
+}
diff --git a/spring-web/src/main/java/org/springframework/web/util/patterns/RegexPathElement.java b/spring-web/src/main/java/org/springframework/web/util/pattern/RegexPathElement.java
similarity index 61%
rename from spring-web/src/main/java/org/springframework/web/util/patterns/RegexPathElement.java
rename to spring-web/src/main/java/org/springframework/web/util/pattern/RegexPathElement.java
index 0c0b9ae0acd..56e83d5fa41 100644
--- a/spring-web/src/main/java/org/springframework/web/util/patterns/RegexPathElement.java
+++ b/spring-web/src/main/java/org/springframework/web/util/pattern/RegexPathElement.java
@@ -14,14 +14,15 @@
* limitations under the License.
*/
-package org.springframework.web.util.patterns;
+package org.springframework.web.util.pattern;
import java.util.LinkedList;
import java.util.List;
import java.util.regex.Matcher;
+import java.util.regex.Pattern;
import org.springframework.util.AntPathMatcher;
-import org.springframework.web.util.patterns.PathPattern.MatchingContext;
+import org.springframework.web.util.pattern.PathPattern.MatchingContext;
/**
* A regex path element. Used to represent any complicated element of the path.
@@ -33,33 +34,36 @@ import org.springframework.web.util.patterns.PathPattern.MatchingContext;
*/
class RegexPathElement extends PathElement {
- private final java.util.regex.Pattern GLOB_PATTERN = java.util.regex.Pattern
- .compile("\\?|\\*|\\{((?:\\{[^/]+?\\}|[^/{}]|\\\\[{}])+?)\\}");
+ private final Pattern GLOB_PATTERN = Pattern.compile("\\?|\\*|\\{((?:\\{[^/]+?\\}|[^/{}]|\\\\[{}])+?)\\}");
private final String DEFAULT_VARIABLE_PATTERN = "(.*)";
- private final List variableNames = new LinkedList<>();
- private char[] regex;
+ private final char[] regex;
- private java.util.regex.Pattern pattern;
+ private final boolean caseSensitive;
- private boolean caseSensitive;
+ private final Pattern pattern;
private int wildcardCount;
+ private final List variableNames = new LinkedList<>();
+
+
RegexPathElement(int pos, char[] regex, boolean caseSensitive, char[] completePattern, char separator) {
super(pos, separator);
this.regex = regex;
this.caseSensitive = caseSensitive;
- buildPattern(regex, completePattern);
+ this.pattern = buildPattern(regex, completePattern);
}
- public void buildPattern(char[] regex, char[] completePattern) {
+
+ public Pattern buildPattern(char[] regex, char[] completePattern) {
StringBuilder patternBuilder = new StringBuilder();
String text = new String(regex);
Matcher matcher = GLOB_PATTERN.matcher(text);
int end = 0;
+
while (matcher.find()) {
patternBuilder.append(quote(text, end, matcher.start()));
String match = matcher.group();
@@ -68,16 +72,16 @@ class RegexPathElement extends PathElement {
}
else if ("*".equals(match)) {
patternBuilder.append(".*");
- wildcardCount++;
+ this.wildcardCount++;
}
else if (match.startsWith("{") && match.endsWith("}")) {
int colonIdx = match.indexOf(':');
if (colonIdx == -1) {
patternBuilder.append(DEFAULT_VARIABLE_PATTERN);
String variableName = matcher.group(1);
- if (variableNames.contains(variableName)) {
- throw new PatternParseException(pos, completePattern, PatternMessage.ILLEGAL_DOUBLE_CAPTURE,
- variableName);
+ if (this.variableNames.contains(variableName)) {
+ throw new PatternParseException(this.pos, completePattern,
+ PatternParseException.PatternMessage.ILLEGAL_DOUBLE_CAPTURE, variableName);
}
this.variableNames.add(variableName);
}
@@ -87,109 +91,112 @@ class RegexPathElement extends PathElement {
patternBuilder.append(variablePattern);
patternBuilder.append(')');
String variableName = match.substring(1, colonIdx);
- if (variableNames.contains(variableName)) {
- throw new PatternParseException(pos, completePattern, PatternMessage.ILLEGAL_DOUBLE_CAPTURE,
- variableName);
+ if (this.variableNames.contains(variableName)) {
+ throw new PatternParseException(this.pos, completePattern,
+ PatternParseException.PatternMessage.ILLEGAL_DOUBLE_CAPTURE, variableName);
}
this.variableNames.add(variableName);
}
}
end = matcher.end();
}
+
patternBuilder.append(quote(text, end, text.length()));
- if (caseSensitive) {
- pattern = java.util.regex.Pattern.compile(patternBuilder.toString());
+ if (this.caseSensitive) {
+ return Pattern.compile(patternBuilder.toString());
}
else {
- pattern = java.util.regex.Pattern.compile(patternBuilder.toString(),
- java.util.regex.Pattern.CASE_INSENSITIVE);
+ return Pattern.compile(patternBuilder.toString(), Pattern.CASE_INSENSITIVE);
}
}
public List getVariableNames() {
- return variableNames;
+ return this.variableNames;
}
private String quote(String s, int start, int end) {
if (start == end) {
return "";
}
- return java.util.regex.Pattern.quote(s.substring(start, end));
+ return Pattern.quote(s.substring(start, end));
}
@Override
public boolean matches(int candidateIndex, MatchingContext matchingContext) {
- int p = matchingContext.scanAhead(candidateIndex);
- Matcher m = pattern.matcher(new SubSequence(matchingContext.candidate, candidateIndex, p));
- boolean matches = m.matches();
+ int pos = matchingContext.scanAhead(candidateIndex);
+ Matcher matcher = this.pattern.matcher(new SubSequence(matchingContext.candidate, candidateIndex, pos));
+ boolean matches = matcher.matches();
+
if (matches) {
- if (next == null) {
+ if (this.next == null) {
if (matchingContext.determineRemainingPath &&
- ((this.variableNames.size() == 0) ? true : p > candidateIndex)) {
- matchingContext.remainingPathIndex = p;
+ ((this.variableNames.size() == 0) ? true : pos > candidateIndex)) {
+ matchingContext.remainingPathIndex = pos;
matches = true;
}
else {
// No more pattern, is there more data?
// If pattern is capturing variables there must be some actual data to bind to them
- matches = (p == matchingContext.candidateLength &&
- ((this.variableNames.size() == 0) ? true : p > candidateIndex));
+ matches = (pos == matchingContext.candidateLength &&
+ ((this.variableNames.size() == 0) ? true : pos > candidateIndex));
if (!matches && matchingContext.isAllowOptionalTrailingSlash()) {
- matches = ((this.variableNames.size() == 0) ? true : p > candidateIndex) &&
- (p + 1) == matchingContext.candidateLength &&
- matchingContext.candidate[p] == separator;
+ matches = ((this.variableNames.size() == 0) ? true : pos > candidateIndex) &&
+ (pos + 1) == matchingContext.candidateLength &&
+ matchingContext.candidate[pos] == separator;
}
}
}
else {
- if (matchingContext.isMatchStartMatching && p == matchingContext.candidateLength) {
+ if (matchingContext.isMatchStartMatching && pos == matchingContext.candidateLength) {
return true; // no more data but matches up to this point
}
- matches = next.matches(p, matchingContext);
+ matches = this.next.matches(pos, matchingContext);
}
}
+
if (matches && matchingContext.extractingVariables) {
// Process captures
- if (this.variableNames.size() != m.groupCount()) { // SPR-8455
+ if (this.variableNames.size() != matcher.groupCount()) { // SPR-8455
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 <= m.groupCount(); i++) {
+ for (int i = 1; i <= matcher.groupCount(); i++) {
String name = this.variableNames.get(i - 1);
- String value = m.group(i);
+ String value = matcher.group(i);
matchingContext.set(name, value);
}
}
return matches;
}
- public String toString() {
- return "Regex(" + new String(regex) + ")";
- }
-
@Override
public int getNormalizedLength() {
int varsLength = 0;
- for (String variableName : variableNames) {
+ for (String variableName : this.variableNames) {
varsLength += variableName.length();
}
- return regex.length - varsLength - variableNames.size();
+ return (this.regex.length - varsLength - this.variableNames.size());
}
public int getCaptureCount() {
- return variableNames.size();
+ return this.variableNames.size();
}
@Override
public int getWildcardCount() {
- return wildcardCount;
+ return this.wildcardCount;
}
@Override
public int getScore() {
- return getCaptureCount() * CAPTURE_VARIABLE_WEIGHT + getWildcardCount() * WILDCARD_WEIGHT;
+ return (getCaptureCount() * CAPTURE_VARIABLE_WEIGHT + getWildcardCount() * WILDCARD_WEIGHT);
+ }
+
+
+ public String toString() {
+ return "Regex(" + String.valueOf(this.regex) + ")";
}
-}
\ No newline at end of file
+}
diff --git a/spring-web/src/main/java/org/springframework/web/util/patterns/SeparatorPathElement.java b/spring-web/src/main/java/org/springframework/web/util/pattern/SeparatorPathElement.java
similarity index 87%
rename from spring-web/src/main/java/org/springframework/web/util/patterns/SeparatorPathElement.java
rename to spring-web/src/main/java/org/springframework/web/util/pattern/SeparatorPathElement.java
index ce972b95dc4..2d394795e43 100644
--- a/spring-web/src/main/java/org/springframework/web/util/patterns/SeparatorPathElement.java
+++ b/spring-web/src/main/java/org/springframework/web/util/pattern/SeparatorPathElement.java
@@ -14,9 +14,9 @@
* limitations under the License.
*/
-package org.springframework.web.util.patterns;
+package org.springframework.web.util.pattern;
-import org.springframework.web.util.patterns.PathPattern.MatchingContext;
+import org.springframework.web.util.pattern.PathPattern.MatchingContext;
/**
* A separator path element. In the pattern '/foo/bar' the two occurrences
@@ -32,6 +32,7 @@ class SeparatorPathElement extends PathElement {
super(pos, separator);
}
+
/**
* Matching a separator is easy, basically the character at candidateIndex
* must be the separator.
@@ -41,7 +42,7 @@ class SeparatorPathElement extends PathElement {
boolean matched = false;
if (candidateIndex < matchingContext.candidateLength &&
matchingContext.candidate[candidateIndex] == separator) {
- if (next == null) {
+ if (this.next == null) {
if (matchingContext.determineRemainingPath) {
matchingContext.remainingPathIndex = candidateIndex + 1;
matched = true;
@@ -55,19 +56,20 @@ class SeparatorPathElement extends PathElement {
if (matchingContext.isMatchStartMatching && candidateIndex == matchingContext.candidateLength) {
return true; // no more data but matches up to this point
}
- matched = next.matches(candidateIndex, matchingContext);
+ matched = this.next.matches(candidateIndex, matchingContext);
}
}
return matched;
}
- public String toString() {
- return "Separator(" + separator + ")";
- }
-
@Override
public int getNormalizedLength() {
return 1;
}
-}
\ No newline at end of file
+
+ public String toString() {
+ return "Separator(" + this.separator + ")";
+ }
+
+}
diff --git a/spring-web/src/main/java/org/springframework/web/util/patterns/SingleCharWildcardedPathElement.java b/spring-web/src/main/java/org/springframework/web/util/pattern/SingleCharWildcardedPathElement.java
similarity index 68%
rename from spring-web/src/main/java/org/springframework/web/util/patterns/SingleCharWildcardedPathElement.java
rename to spring-web/src/main/java/org/springframework/web/util/pattern/SingleCharWildcardedPathElement.java
index 4ca41b9b237..6d15d202ce0 100644
--- a/spring-web/src/main/java/org/springframework/web/util/patterns/SingleCharWildcardedPathElement.java
+++ b/spring-web/src/main/java/org/springframework/web/util/pattern/SingleCharWildcardedPathElement.java
@@ -14,9 +14,9 @@
* limitations under the License.
*/
-package org.springframework.web.util.patterns;
+package org.springframework.web.util.pattern;
-import org.springframework.web.util.patterns.PathPattern.MatchingContext;
+import org.springframework.web.util.pattern.PathPattern.MatchingContext;
/**
* A literal path element that does includes the single character wildcard '?' one
@@ -27,15 +27,18 @@ import org.springframework.web.util.patterns.PathPattern.MatchingContext;
*/
class SingleCharWildcardedPathElement extends PathElement {
- private char[] text;
+ private final char[] text;
- private int len;
+ private final int len;
- private int questionMarkCount;
+ private final int questionMarkCount;
- private boolean caseSensitive;
+ private final boolean caseSensitive;
+
+
+ public SingleCharWildcardedPathElement(
+ int pos, char[] literalText, int questionMarkCount, boolean caseSensitive, char separator) {
- public SingleCharWildcardedPathElement(int pos, char[] literalText, int questionMarkCount, boolean caseSensitive, char separator) {
super(pos, separator);
this.len = literalText.length;
this.questionMarkCount = questionMarkCount;
@@ -51,15 +54,17 @@ class SingleCharWildcardedPathElement extends PathElement {
}
}
+
@Override
public boolean matches(int candidateIndex, MatchingContext matchingContext) {
if (matchingContext.candidateLength < (candidateIndex + len)) {
- return false; // There isn't enough data to match
+ return false; // there isn't enough data to match
}
+
char[] candidate = matchingContext.candidate;
- if (caseSensitive) {
- for (int i = 0; i < len; i++) {
- char t = text[i];
+ if (this.caseSensitive) {
+ for (int i = 0; i candidateIndex && // and there is at least one character to match the *...
- (nextPos + 1) == matchingContext.candidateLength && // and the nextPos is the end of the candidate...
- matchingContext.candidate[nextPos] == separator; // and the final character is a separator
+ return (matchingContext.isAllowOptionalTrailingSlash() && // if optional slash is on...
+ nextPos > candidateIndex && // and there is at least one character to match the *...
+ (nextPos + 1) == matchingContext.candidateLength && // and the nextPos is the end of the candidate...
+ matchingContext.candidate[nextPos] == separator); // and the final character is a separator
}
}
}
@@ -65,7 +66,7 @@ class WildcardPathElement extends PathElement {
if (nextPos == candidateIndex) {
return false;
}
- return next.matches(nextPos, matchingContext);
+ return this.next.matches(nextPos, matchingContext);
}
}
@@ -74,10 +75,6 @@ class WildcardPathElement extends PathElement {
return 1;
}
- public String toString() {
- return "Wildcard(*)";
- }
-
@Override
public int getWildcardCount() {
return 1;
@@ -88,4 +85,9 @@ class WildcardPathElement extends PathElement {
return WILDCARD_WEIGHT;
}
-}
\ No newline at end of file
+
+ public String toString() {
+ return "Wildcard(*)";
+ }
+
+}
diff --git a/spring-web/src/main/java/org/springframework/web/util/patterns/WildcardTheRestPathElement.java b/spring-web/src/main/java/org/springframework/web/util/pattern/WildcardTheRestPathElement.java
similarity index 85%
rename from spring-web/src/main/java/org/springframework/web/util/patterns/WildcardTheRestPathElement.java
rename to spring-web/src/main/java/org/springframework/web/util/pattern/WildcardTheRestPathElement.java
index ab64fae4546..1e80a4546b3 100644
--- a/spring-web/src/main/java/org/springframework/web/util/patterns/WildcardTheRestPathElement.java
+++ b/spring-web/src/main/java/org/springframework/web/util/pattern/WildcardTheRestPathElement.java
@@ -14,9 +14,7 @@
* limitations under the License.
*/
-package org.springframework.web.util.patterns;
-
-import org.springframework.web.util.patterns.PathPattern.MatchingContext;
+package org.springframework.web.util.pattern;
/**
* A path element representing wildcarding the rest of a path. In the pattern
@@ -31,8 +29,9 @@ class WildcardTheRestPathElement extends PathElement {
super(pos, separator);
}
+
@Override
- public boolean matches(int candidateIndex, MatchingContext matchingContext) {
+ public boolean matches(int candidateIndex, PathPattern.MatchingContext matchingContext) {
// If there is more data, it must start with the separator
if (candidateIndex < matchingContext.candidateLength &&
matchingContext.candidate[candidateIndex] != separator) {
@@ -44,10 +43,6 @@ class WildcardTheRestPathElement extends PathElement {
return true;
}
- public String toString() {
- return "WildcardTheRest(" + separator + "**)";
- }
-
@Override
public int getNormalizedLength() {
return 1;
@@ -58,4 +53,9 @@ class WildcardTheRestPathElement extends PathElement {
return 1;
}
-}
\ No newline at end of file
+
+ public String toString() {
+ return "WildcardTheRest(" + this.separator + "**)";
+ }
+
+}
diff --git a/spring-web/src/main/java/org/springframework/web/util/pattern/package-info.java b/spring-web/src/main/java/org/springframework/web/util/pattern/package-info.java
new file mode 100644
index 00000000000..ad083bec054
--- /dev/null
+++ b/spring-web/src/main/java/org/springframework/web/util/pattern/package-info.java
@@ -0,0 +1,4 @@
+/**
+ * Spring's path pattern parser/matcher.
+ */
+package org.springframework.web.util.pattern;
diff --git a/spring-web/src/main/java/org/springframework/web/util/patterns/PathPatternComparator.java b/spring-web/src/main/java/org/springframework/web/util/patterns/PathPatternComparator.java
deleted file mode 100644
index 9470fc1d7d5..00000000000
--- a/spring-web/src/main/java/org/springframework/web/util/patterns/PathPatternComparator.java
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright 2002-2017 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.springframework.web.util.patterns;
-
-import java.util.Comparator;
-
-/**
- * Basic PathPattern comparator.
- *
- * @author Andy Clement
- */
-public class PathPatternComparator implements Comparator {
-
- @Override
- public int compare(PathPattern o1, PathPattern o2) {
- // Nulls get sorted to the end
- if (o1 == null) {
- return (o2 == null ? 0 : +1);
- }
- else if (o2 == null) {
- return -1;
- }
- return o1.compareTo(o2);
- }
-
-}
diff --git a/spring-web/src/main/java/org/springframework/web/util/patterns/PathRemainingMatchInfo.java b/spring-web/src/main/java/org/springframework/web/util/patterns/PathRemainingMatchInfo.java
deleted file mode 100644
index a823d94244f..00000000000
--- a/spring-web/src/main/java/org/springframework/web/util/patterns/PathRemainingMatchInfo.java
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Copyright 2002-2017 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.springframework.web.util.patterns;
-
-import java.util.Map;
-
-/**
- * A holder for the result of a {@link PathPattern#getPathRemaining(String)} 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.
- *
- * @author Andy Clement
- * @since 5.0
- */
-public class PathRemainingMatchInfo {
-
- private String pathRemaining;
-
- private Map matchingVariables;
-
- PathRemainingMatchInfo(String pathRemaining) {
- this.pathRemaining = pathRemaining;
- }
-
- PathRemainingMatchInfo(String pathRemaining, Map matchingVariables) {
- this.pathRemaining = pathRemaining;
- this.matchingVariables = matchingVariables;
- }
-
- /**
- * @return the part of a path that was not matched by a pattern
- */
- public String getPathRemaining() {
- return pathRemaining;
- }
-
- /**
- * @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
- */
- public Map getMatchingVariables() {
- return matchingVariables;
- }
-}
diff --git a/spring-web/src/main/java/org/springframework/web/util/patterns/PatternComparatorConsideringPath.java b/spring-web/src/main/java/org/springframework/web/util/patterns/PatternComparatorConsideringPath.java
deleted file mode 100644
index 77be7bb0556..00000000000
--- a/spring-web/src/main/java/org/springframework/web/util/patterns/PatternComparatorConsideringPath.java
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright 2002-2017 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.springframework.web.util.patterns;
-
-import java.util.Comparator;
-
-/**
- * Similar to {@link PathPatternComparator} but this takes account of a specified path and
- * sorts anything that exactly matches it to be first.
- *
- * @author Andy Clement
- * @since 5.0
- */
-public class PatternComparatorConsideringPath implements Comparator {
-
- private String path;
-
- public PatternComparatorConsideringPath(String path) {
- this.path = path;
- }
-
- @Override
- public int compare(PathPattern o1, PathPattern o2) {
- // Nulls get sorted to the end
- if (o1 == null) {
- return (o2 == null ? 0 : +1);
- }
- else if (o2 == null) {
- return -1;
- }
- if (o1.getPatternString().equals(path)) {
- return (o2.getPatternString().equals(path)) ? 0 : -1;
- }
- else if (o2.getPatternString().equals(path)) {
- return +1;
- }
- return o1.compareTo(o2);
- }
-
-}
diff --git a/spring-web/src/main/java/org/springframework/web/util/patterns/PatternMessage.java b/spring-web/src/main/java/org/springframework/web/util/patterns/PatternMessage.java
deleted file mode 100644
index 0cb53f80d51..00000000000
--- a/spring-web/src/main/java/org/springframework/web/util/patterns/PatternMessage.java
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright 2002-2017 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.springframework.web.util.patterns;
-
-import java.text.MessageFormat;
-
-/**
- * The messages that can be included in a {@link PatternParseException} when there is a parse failure.
- *
- * @author Andy Clement
- * @since 5.0
- */
-public enum PatternMessage {
-
- // @formatter:off
- MISSING_CLOSE_CAPTURE("Expected close capture character after variable name '}'"),
- MISSING_OPEN_CAPTURE("Missing preceeding open capture character before variable name'{'"),
- ILLEGAL_NESTED_CAPTURE("Not allowed to nest variable captures"),
- CANNOT_HAVE_ADJACENT_CAPTURES("Adjacent captures are not allowed"),
- ILLEGAL_CHARACTER_AT_START_OF_CAPTURE_DESCRIPTOR("Character ''{0}'' is not allowed at start of captured variable name"),
- ILLEGAL_CHARACTER_IN_CAPTURE_DESCRIPTOR("Character ''{0}'' is not allowed in a captured variable name"),
- NO_MORE_DATA_EXPECTED_AFTER_CAPTURE_THE_REST("No more pattern data allowed after '{*...}' pattern element"),
- BADLY_FORMED_CAPTURE_THE_REST("Expected form when capturing the rest of the path is simply '{*...}'"),
- MISSING_REGEX_CONSTRAINT("Missing regex constraint on capture"),
- ILLEGAL_DOUBLE_CAPTURE("Not allowed to capture ''{0}'' twice in the same pattern"),
- JDK_PATTERN_SYNTAX_EXCEPTION("Exception occurred in pattern compilation"),
- CAPTURE_ALL_IS_STANDALONE_CONSTRUCT("'{*...}' can only be preceeded by a path separator");
- // @formatter:on
-
- private final String message;
-
- private PatternMessage(String message) {
- this.message = message;
- }
-
- public String formatMessage(Object... inserts) {
- return MessageFormat.format(this.message, inserts);
- }
-
-}
diff --git a/spring-web/src/main/java/org/springframework/web/util/patterns/PatternParseException.java b/spring-web/src/main/java/org/springframework/web/util/patterns/PatternParseException.java
deleted file mode 100644
index dc8ba525238..00000000000
--- a/spring-web/src/main/java/org/springframework/web/util/patterns/PatternParseException.java
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
- * Copyright 2002-2017 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.springframework.web.util.patterns;
-
-/**
- * Exception that is thrown when there is a problem with the pattern being parsed.
- *
- * @author Andy Clement
- * @since 5.0
- */
-public class PatternParseException extends RuntimeException {
-
- private static final long serialVersionUID = 1L;
-
- private int pos;
-
- private char[] patternText;
-
- private final PatternMessage message;
-
- private final Object[] inserts;
-
- public PatternParseException(int pos, char[] patternText, PatternMessage message, Object... inserts) {
- super(message.formatMessage(inserts));
- this.pos = pos;
- this.patternText = patternText;
- this.message = message;
- this.inserts = inserts;
- }
-
- public PatternParseException(Throwable cause, int pos, char[] patternText, PatternMessage message, Object... inserts) {
- super(message.formatMessage(inserts), cause);
- this.pos = pos;
- this.patternText = patternText;
- this.message = message;
- this.inserts = inserts;
- }
-
- /**
- * @return a formatted message with inserts applied
- */
- @Override
- public String getMessage() {
- return this.message.formatMessage(this.inserts);
- }
-
- /**
- * @return a detailed message that includes the original pattern text with a pointer to the error position,
- * as well as the error message.
- */
- public String toDetailedString() {
- StringBuilder buf = new StringBuilder();
- buf.append(patternText).append('\n');
- for (int i = 0; i < pos; i++) {
- buf.append(' ');
- }
- buf.append("^\n");
- buf.append(getMessage());
- return buf.toString();
- }
-
- public Object[] getInserts() {
- return this.inserts;
- }
-
- public int getPosition() {
- return pos;
- }
-
- public PatternMessage getMessageType() {
- return message;
- }
-
-}
diff --git a/spring-web/src/test/java/org/springframework/web/util/patterns/PathPatternMatcherTests.java b/spring-web/src/test/java/org/springframework/web/util/pattern/PathPatternMatcherTests.java
similarity index 96%
rename from spring-web/src/test/java/org/springframework/web/util/patterns/PathPatternMatcherTests.java
rename to spring-web/src/test/java/org/springframework/web/util/pattern/PathPatternMatcherTests.java
index 6073ca2f1a5..05cd467e99d 100644
--- a/spring-web/src/test/java/org/springframework/web/util/patterns/PathPatternMatcherTests.java
+++ b/spring-web/src/test/java/org/springframework/web/util/pattern/PathPatternMatcherTests.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package org.springframework.web.util.patterns;
+package org.springframework.web.util.pattern;
import java.util.ArrayList;
import java.util.Collections;
@@ -26,7 +26,11 @@ import java.util.Map;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
+
import org.springframework.util.AntPathMatcher;
+import org.springframework.web.util.pattern.ParsingPathMatcher;
+import org.springframework.web.util.pattern.PathPattern;
+import org.springframework.web.util.pattern.PathPatternParser;
import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;
@@ -38,14 +42,14 @@ import static org.junit.Assert.*;
*/
public class PathPatternMatcherTests {
+ private char separator = PathPatternParser.DEFAULT_SEPARATOR;
+
+
@Test
public void basicMatching() {
- checkMatches(null, null);
checkMatches("", "");
checkMatches("", null);
checkNoMatch("/abc", null);
- checkMatches(null, "");
- checkNoMatch(null, "/abc");
checkMatches("/", "/");
checkNoMatch("/", "/a");
checkMatches("f", "f");
@@ -267,7 +271,7 @@ public class PathPatternMatcherTests {
assertNull(parse("/resource/{*foo}").getPathRemaining("/resourceX"));
assertEquals("",parse("/resource/{*foo}").getPathRemaining("/resource").getPathRemaining());
- PathRemainingMatchInfo pri = parse("/aaa/{bbb}/c?d/e*f/*/g").getPathRemaining("/aaa/b/ccd/ef/x/g/i");
+ PathPattern.PathRemainingMatchInfo pri = parse("/aaa/{bbb}/c?d/e*f/*/g").getPathRemaining("/aaa/b/ccd/ef/x/g/i");
assertEquals("/i",pri.getPathRemaining());
assertEquals("b",pri.getMatchingVariables().get("bbb"));
@@ -473,7 +477,7 @@ public class PathPatternMatcherTests {
public void pathRemainingEnhancements_spr15419() {
// It would be nice to partially match a path and get any bound variables in one step
PathPattern pp = parse("/{this}/{one}/{here}");
- PathRemainingMatchInfo pri = pp.getPathRemaining("/foo/bar/goo/boo");
+ PathPattern.PathRemainingMatchInfo pri = pp.getPathRemaining("/foo/bar/goo/boo");
assertEquals("/boo",pri.getPathRemaining());
assertEquals("foo",pri.getMatchingVariables().get("this"));
assertEquals("bar",pri.getMatchingVariables().get("one"));
@@ -602,9 +606,6 @@ public class PathPatternMatcherTests {
checkStartMatches("", "");
checkStartMatches("", null);
checkStartMatches("/abc", null);
- checkStartMatches(null, "");
- checkStartMatches(null, null);
- checkStartNoMatch(null, "/abc");
}
@Test
@@ -691,7 +692,7 @@ public class PathPatternMatcherTests {
@Test
public void alternativeDelimiter() {
try {
- separator = '.';
+ this.separator = '.';
// test exact matching
checkMatches("test", "test");
@@ -746,7 +747,7 @@ public class PathPatternMatcherTests {
checkNoMatch(".*bla.test", "XXXbl.test");
}
finally {
- separator = PathPatternParser.DEFAULT_SEPARATOR;
+ this.separator = PathPatternParser.DEFAULT_SEPARATOR;
}
}
@@ -941,9 +942,9 @@ public class PathPatternMatcherTests {
@Test
public void combine() {
TestPathCombiner pathMatcher = new TestPathCombiner();
- assertEquals("", pathMatcher.combine(null, null));
- assertEquals("/hotels", pathMatcher.combine("/hotels", null));
- assertEquals("/hotels", pathMatcher.combine(null, "/hotels"));
+ assertEquals("", pathMatcher.combine("", ""));
+ assertEquals("/hotels", pathMatcher.combine("/hotels", ""));
+ assertEquals("/hotels", pathMatcher.combine("", "/hotels"));
assertEquals("/hotels/booking", pathMatcher.combine("/hotels/*", "booking"));
assertEquals("/hotels/booking", pathMatcher.combine("/hotels/*", "/booking"));
assertEquals("/hotels/**/booking", pathMatcher.combine("/hotels/**", "booking"));
@@ -973,11 +974,6 @@ public class PathPatternMatcherTests {
// SPR-10554
assertEquals("/hotel", pathMatcher.combine("/", "/hotel")); // SPR-12975
assertEquals("/hotel/booking", pathMatcher.combine("/hotel/", "/booking")); // SPR-12975
- assertEquals("", pathMatcher.combine(null, null));
- assertEquals("", pathMatcher.combine(null, ""));
- assertEquals("", pathMatcher.combine("", null));
- assertEquals("", pathMatcher.combine(null, null));
- assertEquals("", pathMatcher.combine("", ""));
assertEquals("/hotel", pathMatcher.combine("", "/hotel"));
assertEquals("/hotel", pathMatcher.combine("/hotel", null));
assertEquals("/hotel", pathMatcher.combine("/hotel", ""));
@@ -993,8 +989,7 @@ public class PathPatternMatcherTests {
@Test
public void patternComparator() {
- Comparator comparator = new PatternComparatorConsideringPath(
- "/hotels/new");
+ Comparator comparator = new ParsingPathMatcher.PatternComparatorConsideringPath("/hotels/new");
assertEquals(0, comparator.compare(null, null));
assertEquals(1, comparator.compare(null, parse("/hotels/new")));
@@ -1058,15 +1053,6 @@ public class PathPatternMatcherTests {
assertEquals(1, comparator.compare(parse("*/**"), parse("*")));
}
- @Test
- public void pathPatternComparator() {
- PathPatternComparator ppc = new PathPatternComparator();
- assertEquals(0, ppc.compare(null, null));
- assertEquals(1, ppc.compare(null, parse("")));
- assertEquals(-1, ppc.compare(parse(""), null));
- assertEquals(0, ppc.compare(parse(""), parse("")));
- }
-
@Test
public void patternCompareTo() {
PathPatternParser p = new PathPatternParser();
@@ -1076,8 +1062,8 @@ public class PathPatternMatcherTests {
@Test
public void patternComparatorSort() {
- Comparator comparator = new PatternComparatorConsideringPath(
- "/hotels/new");
+ Comparator comparator = new ParsingPathMatcher.PatternComparatorConsideringPath("/hotels/new");
+
List paths = new ArrayList<>(3);
PathPatternParser pp = new PathPatternParser();
paths.add(null);
@@ -1162,7 +1148,7 @@ public class PathPatternMatcherTests {
// assertEquals("/hotels/{hotel}", paths.get(1).toPatternString());
// paths.clear();
- comparator = new PatternComparatorConsideringPath("/web/endUser/action/login.html");
+ comparator = new ParsingPathMatcher.PatternComparatorConsideringPath("/web/endUser/action/login.html");
paths.add(pp.parse("/*/login.*"));
paths.add(pp.parse("/*/endUser/action/login.*"));
Collections.sort(paths, comparator);
@@ -1181,15 +1167,6 @@ public class PathPatternMatcherTests {
assertTrue(p.matches("/group/Sales/members"));
}
- @Test
- public void patternmessage() {
- PatternMessage[] values = PatternMessage.values();
- assertNotNull(values);
- for (PatternMessage pm : values) {
- String name = pm.toString();
- assertEquals(pm.ordinal(), PatternMessage.valueOf(name).ordinal());
- }
- }
private PathPattern parse(String path) {
PathPatternParser pp = new PathPatternParser();
@@ -1197,11 +1174,8 @@ public class PathPatternMatcherTests {
return pp.parse(path);
}
- private char separator = PathPatternParser.DEFAULT_SEPARATOR;
-
private void checkMatches(String uriTemplate, String path) {
- PathPatternParser parser = (separator == PathPatternParser.DEFAULT_SEPARATOR
- ? new PathPatternParser() : new PathPatternParser(separator));
+ PathPatternParser parser = new PathPatternParser(this.separator);
parser.setMatchOptionalTrailingSlash(true);
PathPattern p = parser.parse(uriTemplate);
assertTrue(p.matches(path));
@@ -1259,6 +1233,7 @@ public class PathPatternMatcherTests {
assertEquals(expected, s);
}
+
static class TestPathCombiner {
PathPatternParser pp = new PathPatternParser();
diff --git a/spring-web/src/test/java/org/springframework/web/util/patterns/PathPatternParserTests.java b/spring-web/src/test/java/org/springframework/web/util/pattern/PathPatternParserTests.java
similarity index 73%
rename from spring-web/src/test/java/org/springframework/web/util/patterns/PathPatternParserTests.java
rename to spring-web/src/test/java/org/springframework/web/util/pattern/PathPatternParserTests.java
index 116479e2aba..90434153c75 100644
--- a/spring-web/src/test/java/org/springframework/web/util/patterns/PathPatternParserTests.java
+++ b/spring-web/src/test/java/org/springframework/web/util/pattern/PathPatternParserTests.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package org.springframework.web.util.patterns;
+package org.springframework.web.util.pattern;
import java.util.ArrayList;
import java.util.Collections;
@@ -23,6 +23,20 @@ import java.util.Map;
import org.junit.Test;
+import org.springframework.web.util.pattern.CaptureTheRestPathElement;
+import org.springframework.web.util.pattern.CaptureVariablePathElement;
+import org.springframework.web.util.pattern.LiteralPathElement;
+import org.springframework.web.util.pattern.PathElement;
+import org.springframework.web.util.pattern.PathPattern;
+import org.springframework.web.util.pattern.PathPatternParser;
+import org.springframework.web.util.pattern.PatternParseException;
+import org.springframework.web.util.pattern.PatternParseException.PatternMessage;
+import org.springframework.web.util.pattern.RegexPathElement;
+import org.springframework.web.util.pattern.SeparatorPathElement;
+import org.springframework.web.util.pattern.SingleCharWildcardedPathElement;
+import org.springframework.web.util.pattern.WildcardPathElement;
+import org.springframework.web.util.pattern.WildcardTheRestPathElement;
+
import static org.junit.Assert.*;
/**
@@ -32,7 +46,8 @@ import static org.junit.Assert.*;
*/
public class PathPatternParserTests {
- private PathPattern p;
+ private PathPattern pathPattern;
+
@Test
public void basicPatterns() {
@@ -46,18 +61,18 @@ public class PathPatternParserTests {
@Test
public void singleCharWildcardPatterns() {
- p = checkStructure("?");
- assertPathElements(p, SingleCharWildcardedPathElement.class);
+ pathPattern = checkStructure("?");
+ assertPathElements(pathPattern, SingleCharWildcardedPathElement.class);
checkStructure("/?/");
checkStructure("/?abc?/");
}
@Test
public void multiwildcardPattern() {
- p = checkStructure("/**");
- assertPathElements(p, WildcardTheRestPathElement.class);
- p = checkStructure("/**acb"); // this is not double wildcard use, it is / then **acb (an odd, unnecessary use of double *)
- assertPathElements(p, SeparatorPathElement.class, RegexPathElement.class);
+ pathPattern = checkStructure("/**");
+ assertPathElements(pathPattern, WildcardTheRestPathElement.class);
+ pathPattern = checkStructure("/**acb"); // this is not double wildcard use, it is / then **acb (an odd, unnecessary use of double *)
+ assertPathElements(pathPattern, SeparatorPathElement.class, RegexPathElement.class);
}
@Test
@@ -75,10 +90,10 @@ public class PathPatternParserTests {
@Test
public void captureTheRestPatterns() {
checkError("/{*foobar}x{abc}", 10, PatternMessage.NO_MORE_DATA_EXPECTED_AFTER_CAPTURE_THE_REST);
- p = checkStructure("{*foobar}");
- assertPathElements(p, CaptureTheRestPathElement.class);
- p = checkStructure("/{*foobar}");
- assertPathElements(p, CaptureTheRestPathElement.class);
+ pathPattern = checkStructure("{*foobar}");
+ assertPathElements(pathPattern, CaptureTheRestPathElement.class);
+ pathPattern = checkStructure("/{*foobar}");
+ assertPathElements(pathPattern, CaptureTheRestPathElement.class);
checkError("/{*foobar}/", 10, PatternMessage.NO_MORE_DATA_EXPECTED_AFTER_CAPTURE_THE_REST);
checkError("/{*foobar}abc", 10, PatternMessage.NO_MORE_DATA_EXPECTED_AFTER_CAPTURE_THE_REST);
checkError("/{*f%obar}", 4, PatternMessage.ILLEGAL_CHARACTER_IN_CAPTURE_DESCRIPTOR);
@@ -118,62 +133,62 @@ public class PathPatternParserTests {
public void regexPathElementPatterns() {
checkError("/{var:[^/]*}", 8, PatternMessage.MISSING_CLOSE_CAPTURE);
checkError("/{var:abc", 8, PatternMessage.MISSING_CLOSE_CAPTURE);
- checkError("/{var:a{{1,2}}}", 6, PatternMessage.JDK_PATTERN_SYNTAX_EXCEPTION);
+ checkError("/{var:a{{1,2}}}", 6, PatternMessage.REGEX_PATTERN_SYNTAX_EXCEPTION);
- p = checkStructure("/{var:\\\\}");
- assertEquals(CaptureVariablePathElement.class.getName(), p.getHeadSection().next.getClass().getName());
- assertTrue(p.matches("/\\"));
+ pathPattern = checkStructure("/{var:\\\\}");
+ assertEquals(CaptureVariablePathElement.class.getName(), pathPattern.getHeadSection().next.getClass().getName());
+ assertTrue(pathPattern.matches("/\\"));
- p = checkStructure("/{var:\\/}");
- assertEquals(CaptureVariablePathElement.class.getName(), p.getHeadSection().next.getClass().getName());
- assertFalse(p.matches("/aaa"));
+ pathPattern = checkStructure("/{var:\\/}");
+ assertEquals(CaptureVariablePathElement.class.getName(), pathPattern.getHeadSection().next.getClass().getName());
+ assertFalse(pathPattern.matches("/aaa"));
- p = checkStructure("/{var:a{1,2}}", 1);
- assertEquals(CaptureVariablePathElement.class.getName(), p.getHeadSection().next.getClass().getName());
+ pathPattern = checkStructure("/{var:a{1,2}}", 1);
+ assertEquals(CaptureVariablePathElement.class.getName(), pathPattern.getHeadSection().next.getClass().getName());
- p = checkStructure("/{var:[^\\/]*}", 1);
- assertEquals(CaptureVariablePathElement.class.getName(), p.getHeadSection().next.getClass().getName());
- Map result = p.matchAndExtract("/foo");
+ pathPattern = checkStructure("/{var:[^\\/]*}", 1);
+ assertEquals(CaptureVariablePathElement.class.getName(), pathPattern.getHeadSection().next.getClass().getName());
+ Map result = pathPattern.matchAndExtract("/foo");
assertEquals("foo", result.get("var"));
- p = checkStructure("/{var:\\[*}", 1);
- assertEquals(CaptureVariablePathElement.class.getName(), p.getHeadSection().next.getClass().getName());
- result = p.matchAndExtract("/[[[");
+ pathPattern = checkStructure("/{var:\\[*}", 1);
+ assertEquals(CaptureVariablePathElement.class.getName(), pathPattern.getHeadSection().next.getClass().getName());
+ result = pathPattern.matchAndExtract("/[[[");
assertEquals("[[[", result.get("var"));
- p = checkStructure("/{var:[\\{]*}", 1);
- assertEquals(CaptureVariablePathElement.class.getName(), p.getHeadSection().next.getClass().getName());
- result = p.matchAndExtract("/{{{");
+ pathPattern = checkStructure("/{var:[\\{]*}", 1);
+ assertEquals(CaptureVariablePathElement.class.getName(), pathPattern.getHeadSection().next.getClass().getName());
+ result = pathPattern.matchAndExtract("/{{{");
assertEquals("{{{", result.get("var"));
- p = checkStructure("/{var:[\\}]*}", 1);
- assertEquals(CaptureVariablePathElement.class.getName(), p.getHeadSection().next.getClass().getName());
- result = p.matchAndExtract("/}}}");
+ pathPattern = checkStructure("/{var:[\\}]*}", 1);
+ assertEquals(CaptureVariablePathElement.class.getName(), pathPattern.getHeadSection().next.getClass().getName());
+ result = pathPattern.matchAndExtract("/}}}");
assertEquals("}}}", result.get("var"));
- p = checkStructure("*");
- assertEquals(WildcardPathElement.class.getName(), p.getHeadSection().getClass().getName());
+ pathPattern = checkStructure("*");
+ assertEquals(WildcardPathElement.class.getName(), pathPattern.getHeadSection().getClass().getName());
checkStructure("/*");
checkStructure("/*/");
checkStructure("*/");
checkStructure("/*/");
- p = checkStructure("/*a*/");
- assertEquals(RegexPathElement.class.getName(), p.getHeadSection().next.getClass().getName());
- p = checkStructure("*/");
- assertEquals(WildcardPathElement.class.getName(), p.getHeadSection().getClass().getName());
+ pathPattern = checkStructure("/*a*/");
+ assertEquals(RegexPathElement.class.getName(), pathPattern.getHeadSection().next.getClass().getName());
+ pathPattern = checkStructure("*/");
+ assertEquals(WildcardPathElement.class.getName(), pathPattern.getHeadSection().getClass().getName());
checkError("{foo}_{foo}", 0, PatternMessage.ILLEGAL_DOUBLE_CAPTURE, "foo");
checkError("/{bar}/{bar}", 7, PatternMessage.ILLEGAL_DOUBLE_CAPTURE, "bar");
checkError("/{bar}/{bar}_{foo}", 7, PatternMessage.ILLEGAL_DOUBLE_CAPTURE, "bar");
- p = checkStructure("{symbolicName:[\\p{L}\\.]+}-sources-{version:[\\p{N}\\.]+}.jar");
- assertEquals(RegexPathElement.class.getName(), p.getHeadSection().getClass().getName());
+ pathPattern = checkStructure("{symbolicName:[\\p{L}\\.]+}-sources-{version:[\\p{N}\\.]+}.jar");
+ assertEquals(RegexPathElement.class.getName(), pathPattern.getHeadSection().getClass().getName());
}
@Test
public void completeCapturingPatterns() {
- p = checkStructure("{foo}");
- assertEquals(CaptureVariablePathElement.class.getName(), p.getHeadSection().getClass().getName());
+ pathPattern = checkStructure("{foo}");
+ assertEquals(CaptureVariablePathElement.class.getName(), pathPattern.getHeadSection().getClass().getName());
checkStructure("/{foo}");
checkStructure("/{f}/");
checkStructure("/{foo}/{bar}/{wibble}");
@@ -181,17 +196,17 @@ public class PathPatternParserTests {
@Test
public void completeCaptureWithConstraints() {
- p = checkStructure("{foo:...}");
- assertPathElements(p, CaptureVariablePathElement.class);
- p = checkStructure("{foo:[0-9]*}");
- assertPathElements(p, CaptureVariablePathElement.class);
+ pathPattern = checkStructure("{foo:...}");
+ assertPathElements(pathPattern, CaptureVariablePathElement.class);
+ pathPattern = checkStructure("{foo:[0-9]*}");
+ assertPathElements(pathPattern, CaptureVariablePathElement.class);
checkError("{foo:}", 5, PatternMessage.MISSING_REGEX_CONSTRAINT);
}
@Test
public void partialCapturingPatterns() {
- p = checkStructure("{foo}abc");
- assertEquals(RegexPathElement.class.getName(), p.getHeadSection().getClass().getName());
+ pathPattern = checkStructure("{foo}abc");
+ assertEquals(RegexPathElement.class.getName(), pathPattern.getHeadSection().getClass().getName());
checkStructure("abc{foo}");
checkStructure("/abc{foo}");
checkStructure("{foo}def/");
@@ -293,19 +308,19 @@ public class PathPatternParserTests {
@Test
public void multipleSeparatorPatterns() {
- p = checkStructure("///aaa");
- assertEquals(6, p.getNormalizedLength());
- assertPathElements(p, SeparatorPathElement.class, SeparatorPathElement.class,
+ pathPattern = checkStructure("///aaa");
+ assertEquals(6, pathPattern.getNormalizedLength());
+ assertPathElements(pathPattern, SeparatorPathElement.class, SeparatorPathElement.class,
SeparatorPathElement.class, LiteralPathElement.class);
- p = checkStructure("///aaa////aaa/b");
- assertEquals(15, p.getNormalizedLength());
- assertPathElements(p, SeparatorPathElement.class, SeparatorPathElement.class,
+ pathPattern = checkStructure("///aaa////aaa/b");
+ assertEquals(15, pathPattern.getNormalizedLength());
+ assertPathElements(pathPattern, SeparatorPathElement.class, SeparatorPathElement.class,
SeparatorPathElement.class, LiteralPathElement.class, SeparatorPathElement.class,
SeparatorPathElement.class, SeparatorPathElement.class, SeparatorPathElement.class,
LiteralPathElement.class, SeparatorPathElement.class, LiteralPathElement.class);
- p = checkStructure("/////**");
- assertEquals(5, p.getNormalizedLength());
- assertPathElements(p, SeparatorPathElement.class, SeparatorPathElement.class,
+ pathPattern = checkStructure("/////**");
+ assertEquals(5, pathPattern.getNormalizedLength());
+ assertPathElements(pathPattern, SeparatorPathElement.class, SeparatorPathElement.class,
SeparatorPathElement.class, SeparatorPathElement.class, WildcardTheRestPathElement.class);
}
@@ -344,7 +359,7 @@ public class PathPatternParserTests {
patterns.add(p2);
patterns.add(p3);
patterns.add(p1);
- Collections.sort(patterns, new PathPatternComparator());
+ Collections.sort(patterns);
assertEquals(p1, patterns.get(0));
// Based purely on length
@@ -356,7 +371,7 @@ public class PathPatternParserTests {
patterns.add(p2);
patterns.add(p3);
patterns.add(p1);
- Collections.sort(patterns, new PathPatternComparator());
+ Collections.sort(patterns);
assertEquals(p3, patterns.get(0));
// Based purely on 'wildness'
@@ -368,7 +383,7 @@ public class PathPatternParserTests {
patterns.add(p2);
patterns.add(p3);
patterns.add(p1);
- Collections.sort(patterns, new PathPatternComparator());
+ Collections.sort(patterns);
assertEquals(p1, patterns.get(0));
// Based purely on catchAll
@@ -389,19 +404,11 @@ public class PathPatternParserTests {
patterns.add(p2);
patterns.add(p3);
patterns.add(p1);
- Collections.sort(patterns, new PathPatternComparator());
+ Collections.sort(patterns);
assertEquals(p3, patterns.get(0));
assertEquals(p2, patterns.get(1));
-
- patterns = new ArrayList<>();
- patterns.add(parse("/abc"));
- patterns.add(null);
- patterns.add(parse("/def"));
- Collections.sort(patterns, new PathPatternComparator());
- assertNull(patterns.get(2));
}
- // ---
private PathPattern parse(String pattern) {
PathPatternParser patternParser = new PathPatternParser();
@@ -428,19 +435,18 @@ public class PathPatternParserTests {
}
private PathPattern checkStructure(String pattern, int expectedSeparatorCount) {
- p = parse(pattern);
- assertEquals(pattern, p.getPatternString());
-// assertEquals(expectedSeparatorCount,p.getSeparatorCount());
- return p;
+ pathPattern = parse(pattern);
+ assertEquals(pattern, pathPattern.getPatternString());
+ // assertEquals(expectedSeparatorCount, pathPattern.getSeparatorCount());
+ return pathPattern;
}
private void checkError(String pattern, int expectedPos, PatternMessage expectedMessage, String... expectedInserts) {
try {
- p = parse(pattern);
+ pathPattern = parse(pattern);
fail("Expected to fail");
}
catch (PatternParseException ppe) {
-// System.out.println(ppe.toDetailedString());
assertEquals(ppe.toDetailedString(), expectedPos, ppe.getPosition());
assertEquals(ppe.toDetailedString(), expectedMessage, ppe.getMessageType());
if (expectedInserts.length != 0) {
@@ -455,11 +461,12 @@ public class PathPatternParserTests {
@SafeVarargs
private final void assertPathElements(PathPattern p, Class extends PathElement>... sectionClasses) {
PathElement head = p.getHeadSection();
- for (int i = 0; i < sectionClasses.length; i++) {
+ for (Class extends PathElement> sectionClass : sectionClasses) {
if (head == null) {
fail("Ran out of data in parsed pattern. Pattern is: " + p.toChainString());
}
- assertEquals("Not expected section type. Pattern is: " + p.toChainString(), sectionClasses[i].getSimpleName(), head.getClass().getSimpleName());
+ assertEquals("Not expected section type. Pattern is: " + p.toChainString(),
+ sectionClass.getSimpleName(), head.getClass().getSimpleName());
head = head.next;
}
}
diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/config/PathMatchConfigurer.java b/spring-webflux/src/main/java/org/springframework/web/reactive/config/PathMatchConfigurer.java
index ad248ed3a79..9f256437fef 100644
--- a/spring-webflux/src/main/java/org/springframework/web/reactive/config/PathMatchConfigurer.java
+++ b/spring-webflux/src/main/java/org/springframework/web/reactive/config/PathMatchConfigurer.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2016 the original author or authors.
+ * Copyright 2002-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -82,7 +82,7 @@ public class PathMatchConfigurer {
/**
* Set the PathMatcher for matching URL paths against registered URL patterns.
- * Default is {@link org.springframework.web.util.ParsingPathMatcher ParsingPathMatcher}.
+ *
The default is a {@link org.springframework.web.util.pattern.ParsingPathMatcher}.
*/
public PathMatchConfigurer setPathMatcher(PathMatcher pathMatcher) {
this.pathMatcher = pathMatcher;
diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/config/ResourceHandlerRegistry.java b/spring-webflux/src/main/java/org/springframework/web/reactive/config/ResourceHandlerRegistry.java
index 312d227ce76..640dd1152a3 100644
--- a/spring-webflux/src/main/java/org/springframework/web/reactive/config/ResourceHandlerRegistry.java
+++ b/spring-webflux/src/main/java/org/springframework/web/reactive/config/ResourceHandlerRegistry.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2016 the original author or authors.
+ * Copyright 2002-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -88,10 +88,9 @@ public class ResourceHandlerRegistry {
* Add a resource handler for serving static resources based on the specified
* URL path patterns. The handler will be invoked for every incoming request
* that matches to one of the specified path patterns.
- *
*
Patterns like {@code "/static/**"} or {@code "/css/{filename:\\w+\\.css}"}
- * are allowed. See {@link org.springframework.web.util.ParsingPathMatcher} for more
- * details on the syntax.
+ * are allowed. See {@link org.springframework.web.util.pattern.ParsingPathMatcher}
+ * for more details on the syntax.
* @return A {@link ResourceHandlerRegistration} to use to further
* configure the registered resource handler
*/
diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/PathResourceLookupFunction.java b/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/PathResourceLookupFunction.java
index 12fd6e9b6a8..2bb5e0ac31b 100644
--- a/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/PathResourceLookupFunction.java
+++ b/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/PathResourceLookupFunction.java
@@ -28,8 +28,8 @@ import org.springframework.core.io.Resource;
import org.springframework.core.io.UrlResource;
import org.springframework.util.ResourceUtils;
import org.springframework.util.StringUtils;
-import org.springframework.web.util.patterns.PathPattern;
-import org.springframework.web.util.patterns.PathPatternParser;
+import org.springframework.web.util.pattern.PathPattern;
+import org.springframework.web.util.pattern.PathPatternParser;
/**
* Lookup function used by {@link RouterFunctions#resources(String, Resource)}.
diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/RequestPredicates.java b/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/RequestPredicates.java
index 0165a9ddf45..9c2233d924a 100644
--- a/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/RequestPredicates.java
+++ b/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/RequestPredicates.java
@@ -41,8 +41,8 @@ import org.springframework.util.Assert;
import org.springframework.web.reactive.function.BodyExtractor;
import org.springframework.web.server.WebSession;
import org.springframework.web.util.UriUtils;
-import org.springframework.web.util.patterns.PathPattern;
-import org.springframework.web.util.patterns.PathPatternParser;
+import org.springframework.web.util.pattern.PathPattern;
+import org.springframework.web.util.pattern.PathPatternParser;
/**
* Implementations of {@link RequestPredicate} that implement various useful
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 c1d320100fa..a1fcb235241 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
@@ -34,7 +34,7 @@ import org.springframework.web.reactive.HandlerMapping;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.WebHandler;
import org.springframework.web.server.support.HttpRequestPathHelper;
-import org.springframework.web.util.ParsingPathMatcher;
+import org.springframework.web.util.pattern.ParsingPathMatcher;
/**
* Abstract base class for {@link org.springframework.web.reactive.HandlerMapping}
@@ -103,8 +103,8 @@ public abstract class AbstractHandlerMapping extends ApplicationObjectSupport im
/**
* Set the PathMatcher implementation to use for matching URL paths
- * against registered URL patterns. Default is ParsingPathMatcher.
- * @see org.springframework.web.util.ParsingPathMatcher
+ * against registered URL patterns.
+ *
The default is a {@link ParsingPathMatcher}.
*/
public void setPathMatcher(PathMatcher pathMatcher) {
Assert.notNull(pathMatcher, "PathMatcher must not be null");
diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/handler/AbstractUrlHandlerMapping.java b/spring-webflux/src/main/java/org/springframework/web/reactive/handler/AbstractUrlHandlerMapping.java
index 193e6fa5e4e..7c5bbd5baa2 100644
--- a/spring-webflux/src/main/java/org/springframework/web/reactive/handler/AbstractUrlHandlerMapping.java
+++ b/spring-webflux/src/main/java/org/springframework/web/reactive/handler/AbstractUrlHandlerMapping.java
@@ -37,7 +37,7 @@ import org.springframework.web.server.ServerWebExchange;
* various Ant-style pattern matches, e.g. a registered "/t*" pattern matches
* both "/test" and "/team", "/test/*" matches all paths under "/test",
* "/test/**" matches all paths below "/test". For details, see the
- * {@link org.springframework.web.util.ParsingPathMatcher ParsingPathMatcher} javadoc.
+ * {@link org.springframework.web.util.pattern.ParsingPathMatcher} javadoc.
*
*
Will search all path patterns to find the most exact match for the
* current request path. The most exact match is defined as the longest
@@ -119,18 +119,15 @@ public abstract class AbstractUrlHandlerMapping extends AbstractHandlerMapping {
/**
* Look up a handler instance for the given URL path.
- *
*
Supports direct matches, e.g. a registered "/test" matches "/test",
* and various Ant-style pattern matches, e.g. a registered "/t*" matches
* both "/test" and "/team". For details, see the AntPathMatcher class.
- *
*
Looks for the most exact pattern, where most exact is defined as
* the longest path pattern.
- *
* @param urlPath URL the bean is mapped to
* @param exchange the current exchange
* @return the associated handler instance, or {@code null} if not found
- * @see org.springframework.web.util.ParsingPathMatcher
+ * @see org.springframework.web.util.pattern.ParsingPathMatcher
*/
protected Object lookupHandler(String urlPath, ServerWebExchange exchange) throws Exception {
// Direct match?
diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/handler/SimpleUrlHandlerMapping.java b/spring-webflux/src/main/java/org/springframework/web/reactive/handler/SimpleUrlHandlerMapping.java
index f5ea7c7221e..7845a858406 100644
--- a/spring-webflux/src/main/java/org/springframework/web/reactive/handler/SimpleUrlHandlerMapping.java
+++ b/spring-webflux/src/main/java/org/springframework/web/reactive/handler/SimpleUrlHandlerMapping.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2016 the original author or authors.
+ * Copyright 2002-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -46,7 +46,7 @@ import org.springframework.util.CollectionUtils;
* various Ant-style pattern matches, e.g. a registered "/t*" pattern matches
* both "/test" and "/team", "/test/*" matches all paths under "/test",
* "/test/**" matches all paths below "/test". For details, see the
- * {@link org.springframework.web.util.ParsingPathMatcher ParsingPathMatcher} javadoc.
+ * {@link org.springframework.web.util.pattern.ParsingPathMatcher} javadoc.
*
* @author Rossen Stoyanchev
* @since 5.0
@@ -59,8 +59,8 @@ public class SimpleUrlHandlerMapping extends AbstractUrlHandlerMapping {
/**
* Map URL paths to handler bean names.
* This is the typical way of configuring this HandlerMapping.
- *
Supports direct URL matches and Ant-style pattern matches. For syntax
- * details, see the {@link org.springframework.web.util.ParsingPathMatcher} javadoc.
+ *
Supports direct URL matches and Ant-style pattern matches. For syntax details,
+ * see the {@link org.springframework.web.util.pattern.ParsingPathMatcher} javadoc.
* @param mappings properties with URLs as keys and bean names as values
* @see #setUrlMap
*/
@@ -71,8 +71,8 @@ public class SimpleUrlHandlerMapping extends AbstractUrlHandlerMapping {
/**
* Set a Map with URL paths as keys and handler beans (or handler bean names)
* as values. Convenient for population with bean references.
- *
Supports direct URL matches and Ant-style pattern matches. For syntax
- * details, see the {@link org.springframework.web.util.ParsingPathMatcher} javadoc.
+ *
Supports direct URL matches and Ant-style pattern matches. For syntax details,
+ * see the {@link org.springframework.web.util.pattern.ParsingPathMatcher} javadoc.
* @param urlMap map with URLs as keys and beans as values
* @see #setMappings
*/
diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/resource/ResourceUrlProvider.java b/spring-webflux/src/main/java/org/springframework/web/reactive/resource/ResourceUrlProvider.java
index 873817d014d..26cf7f6aaec 100644
--- a/spring-webflux/src/main/java/org/springframework/web/reactive/resource/ResourceUrlProvider.java
+++ b/spring-webflux/src/main/java/org/springframework/web/reactive/resource/ResourceUrlProvider.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2016 the original author or authors.
+ * Copyright 2002-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -37,7 +37,7 @@ import org.springframework.util.PathMatcher;
import org.springframework.web.reactive.handler.SimpleUrlHandlerMapping;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.support.HttpRequestPathHelper;
-import org.springframework.web.util.ParsingPathMatcher;
+import org.springframework.web.util.pattern.ParsingPathMatcher;
/**
* A central component to use to obtain the public URL path that clients should
@@ -54,7 +54,7 @@ public class ResourceUrlProvider implements ApplicationListener