|
|
|
@ -16,11 +16,15 @@ |
|
|
|
|
|
|
|
|
|
|
|
package org.springframework.util; |
|
|
|
package org.springframework.util; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
import java.util.LinkedList; |
|
|
|
|
|
|
|
import java.util.List; |
|
|
|
import java.util.Map; |
|
|
|
import java.util.Map; |
|
|
|
|
|
|
|
import java.util.regex.Matcher; |
|
|
|
|
|
|
|
import java.util.regex.Pattern; |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
|
* Package-protected helper class for {@link AntPathMatcher}. |
|
|
|
* Package-protected helper class for {@link AntPathMatcher}. |
|
|
|
* Tests whether or not a string matches against a pattern. |
|
|
|
* Tests whether or not a string matches against a pattern using a regular expression. |
|
|
|
* |
|
|
|
* |
|
|
|
* <p>The pattern may contain special characters: '*' means zero or more characters; |
|
|
|
* <p>The pattern may contain special characters: '*' means zero or more characters; |
|
|
|
* '?' means one and only one character; '{' and '}' indicate a URI template pattern. |
|
|
|
* '?' means one and only one character; '{' and '}' indicate a URI template pattern. |
|
|
|
@ -30,189 +34,66 @@ import java.util.Map; |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
class AntPatchStringMatcher { |
|
|
|
class AntPatchStringMatcher { |
|
|
|
|
|
|
|
|
|
|
|
private final char[] patArr; |
|
|
|
private static final Pattern GLOB_PATTERN = Pattern.compile("\\?|\\*|\\{([^/]+?)\\}"); |
|
|
|
|
|
|
|
|
|
|
|
private final char[] strArr; |
|
|
|
private final Pattern pattern; |
|
|
|
|
|
|
|
|
|
|
|
private int patIdxStart = 0; |
|
|
|
private String str; |
|
|
|
|
|
|
|
|
|
|
|
private int patIdxEnd; |
|
|
|
private final List<String> variableNames = new LinkedList<String>(); |
|
|
|
|
|
|
|
|
|
|
|
private int strIdxStart = 0; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private int strIdxEnd; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private char ch; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private final Map<String, String> uriTemplateVariables; |
|
|
|
private final Map<String, String> uriTemplateVariables; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** Construct a new instance of the <code>AntPatchStringMatcher</code>. */ |
|
|
|
/** |
|
|
|
AntPatchStringMatcher(String pattern, String str, Map<String, String> uriTemplateVariables) { |
|
|
|
* Construct a new instance of the <code>AntPatchStringMatcher</code>. |
|
|
|
this.str = str; |
|
|
|
*/ |
|
|
|
|
|
|
|
public AntPatchStringMatcher(String pattern, String str, Map<String, String> uriTemplateVariables) { |
|
|
|
|
|
|
|
this.patArr = pattern.toCharArray(); |
|
|
|
|
|
|
|
this.strArr = str.toCharArray(); |
|
|
|
|
|
|
|
this.patIdxEnd = this.patArr.length - 1; |
|
|
|
|
|
|
|
this.strIdxEnd = this.strArr.length - 1; |
|
|
|
|
|
|
|
this.uriTemplateVariables = uriTemplateVariables; |
|
|
|
this.uriTemplateVariables = uriTemplateVariables; |
|
|
|
|
|
|
|
this.pattern = createPattern(pattern); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private Pattern createPattern(String pattern) { |
|
|
|
/** |
|
|
|
StringBuilder patternBuilder = new StringBuilder(); |
|
|
|
* Main entry point. |
|
|
|
Matcher m = GLOB_PATTERN.matcher(pattern); |
|
|
|
* @return <code>true</code> if the string matches against the pattern, or <code>false</code> otherwise. |
|
|
|
int end = 0; |
|
|
|
*/ |
|
|
|
while (m.find()) { |
|
|
|
public boolean matchStrings() { |
|
|
|
patternBuilder.append(quote(pattern, end, m.start())); |
|
|
|
if (shortcutPossible()) { |
|
|
|
String match = m.group(); |
|
|
|
return doShortcut(); |
|
|
|
if ("?".equals(match)) { |
|
|
|
} |
|
|
|
patternBuilder.append('.'); |
|
|
|
if (patternContainsOnlyStar()) { |
|
|
|
|
|
|
|
return true; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
if (patternContainsOneTemplateVariable()) { |
|
|
|
|
|
|
|
addTemplateVariable(0, patIdxEnd, 0, strIdxEnd); |
|
|
|
|
|
|
|
return true; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
if (!matchBeforeFirstStarOrCurly()) { |
|
|
|
|
|
|
|
return false; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
if (allCharsUsed()) { |
|
|
|
|
|
|
|
return onlyStarsLeft(); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
if (!matchAfterLastStarOrCurly()) { |
|
|
|
|
|
|
|
return false; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
if (allCharsUsed()) { |
|
|
|
|
|
|
|
return onlyStarsLeft(); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
// process pattern between stars. padIdxStart and patIdxEnd point
|
|
|
|
|
|
|
|
// always to a '*'.
|
|
|
|
|
|
|
|
while (patIdxStart != patIdxEnd && strIdxStart <= strIdxEnd) { |
|
|
|
|
|
|
|
int patIdxTmp; |
|
|
|
|
|
|
|
if (patArr[patIdxStart] == '{') { |
|
|
|
|
|
|
|
patIdxTmp = findClosingCurly(); |
|
|
|
|
|
|
|
addTemplateVariable(patIdxStart, patIdxTmp, strIdxStart, strIdxEnd); |
|
|
|
|
|
|
|
patIdxStart = patIdxTmp + 1; |
|
|
|
|
|
|
|
strIdxStart = strIdxEnd + 1; |
|
|
|
|
|
|
|
continue; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
patIdxTmp = findNextStarOrCurly(); |
|
|
|
|
|
|
|
if (consecutiveStars(patIdxTmp)) { |
|
|
|
|
|
|
|
continue; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
// Find the pattern between padIdxStart & padIdxTmp in str between
|
|
|
|
|
|
|
|
// strIdxStart & strIdxEnd
|
|
|
|
|
|
|
|
int patLength = (patIdxTmp - patIdxStart - 1); |
|
|
|
|
|
|
|
int strLength = (strIdxEnd - strIdxStart + 1); |
|
|
|
|
|
|
|
int foundIdx = -1; |
|
|
|
|
|
|
|
strLoop: |
|
|
|
|
|
|
|
for (int i = 0; i <= strLength - patLength; i++) { |
|
|
|
|
|
|
|
for (int j = 0; j < patLength; j++) { |
|
|
|
|
|
|
|
ch = patArr[patIdxStart + j + 1]; |
|
|
|
|
|
|
|
if (ch != '?') { |
|
|
|
|
|
|
|
if (ch != strArr[strIdxStart + i + j]) { |
|
|
|
|
|
|
|
continue strLoop; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
foundIdx = strIdxStart + i; |
|
|
|
|
|
|
|
break; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (foundIdx == -1) { |
|
|
|
|
|
|
|
return false; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
patIdxStart = patIdxTmp; |
|
|
|
|
|
|
|
strIdxStart = foundIdx + patLength; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return onlyStarsLeft(); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private void addTemplateVariable(int curlyIdxStart, int curlyIdxEnd, int valIdxStart, int valIdxEnd) { |
|
|
|
|
|
|
|
if (uriTemplateVariables != null) { |
|
|
|
|
|
|
|
String varName = new String(patArr, curlyIdxStart + 1, curlyIdxEnd - curlyIdxStart - 1); |
|
|
|
|
|
|
|
String varValue = new String(strArr, valIdxStart, valIdxEnd - valIdxStart + 1); |
|
|
|
|
|
|
|
uriTemplateVariables.put(varName, varValue); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private boolean consecutiveStars(int patIdxTmp) { |
|
|
|
|
|
|
|
if (patIdxTmp == patIdxStart + 1 && patArr[patIdxStart] == '*' && patArr[patIdxTmp] == '*') { |
|
|
|
|
|
|
|
// Two stars next to each other, skip the first one.
|
|
|
|
|
|
|
|
patIdxStart++; |
|
|
|
|
|
|
|
return true; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
return false; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private int findNextStarOrCurly() { |
|
|
|
|
|
|
|
for (int i = patIdxStart + 1; i <= patIdxEnd; i++) { |
|
|
|
|
|
|
|
if (patArr[i] == '*' || patArr[i] == '{') { |
|
|
|
|
|
|
|
return i; |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
else if ("*".equals(match)) { |
|
|
|
return -1; |
|
|
|
patternBuilder.append(".*"); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private int findClosingCurly() { |
|
|
|
|
|
|
|
for (int i = patIdxStart + 1; i <= patIdxEnd; i++) { |
|
|
|
|
|
|
|
if (patArr[i] == '}') { |
|
|
|
|
|
|
|
return i; |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
else if (match.startsWith("{") && match.endsWith("}")) { |
|
|
|
return -1; |
|
|
|
patternBuilder.append("(.*)"); |
|
|
|
} |
|
|
|
variableNames.add(m.group(1)); |
|
|
|
|
|
|
|
|
|
|
|
private boolean onlyStarsLeft() { |
|
|
|
|
|
|
|
for (int i = patIdxStart; i <= patIdxEnd; i++) { |
|
|
|
|
|
|
|
if (patArr[i] != '*') { |
|
|
|
|
|
|
|
return false; |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
end = m.end(); |
|
|
|
} |
|
|
|
} |
|
|
|
return true; |
|
|
|
patternBuilder.append(quote(pattern, end, pattern.length())); |
|
|
|
} |
|
|
|
return Pattern.compile(patternBuilder.toString()); |
|
|
|
|
|
|
|
|
|
|
|
private boolean allCharsUsed() { |
|
|
|
|
|
|
|
return strIdxStart > strIdxEnd; |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private boolean shortcutPossible() { |
|
|
|
private String quote(String s, int start, int end) { |
|
|
|
for (char ch : patArr) { |
|
|
|
if (start == end) { |
|
|
|
if (ch == '*' || ch == '{' || ch == '}') { |
|
|
|
return ""; |
|
|
|
return false; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
return true; |
|
|
|
return Pattern.quote(s.substring(start, end)); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private boolean doShortcut() { |
|
|
|
/** |
|
|
|
if (patIdxEnd != strIdxEnd) { |
|
|
|
* Main entry point. |
|
|
|
return false; // Pattern and string do not have the same size
|
|
|
|
* |
|
|
|
} |
|
|
|
* @return <code>true</code> if the string matches against the pattern, or <code>false</code> otherwise. |
|
|
|
for (int i = 0; i <= patIdxEnd; i++) { |
|
|
|
*/ |
|
|
|
ch = patArr[i]; |
|
|
|
public boolean matchStrings() { |
|
|
|
if (ch != '?') { |
|
|
|
Matcher matcher = pattern.matcher(str); |
|
|
|
if (ch != strArr[i]) { |
|
|
|
if (matcher.matches()) { |
|
|
|
return false;// Character mismatch
|
|
|
|
if (uriTemplateVariables != null) { |
|
|
|
} |
|
|
|
for (int i = 1; i <= matcher.groupCount(); i++) { |
|
|
|
} |
|
|
|
String name = this.variableNames.get(i - 1); |
|
|
|
} |
|
|
|
String value = matcher.group(i); |
|
|
|
return true; // String matches against pattern
|
|
|
|
uriTemplateVariables.put(name, value); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private boolean patternContainsOnlyStar() { |
|
|
|
|
|
|
|
return (patIdxEnd == 0 && patArr[0] == '*'); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private boolean patternContainsOneTemplateVariable() { |
|
|
|
|
|
|
|
if ((patIdxEnd >= 2 && patArr[0] == '{' && patArr[patIdxEnd] == '}')) { |
|
|
|
|
|
|
|
for (int i = 1; i < patIdxEnd; i++) { |
|
|
|
|
|
|
|
if (patArr[i] == '}') { |
|
|
|
|
|
|
|
return false; |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
return true; |
|
|
|
return true; |
|
|
|
@ -222,30 +103,4 @@ class AntPatchStringMatcher { |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private boolean matchBeforeFirstStarOrCurly() { |
|
|
|
|
|
|
|
while ((ch = patArr[patIdxStart]) != '*' && ch != '{' && strIdxStart <= strIdxEnd) { |
|
|
|
|
|
|
|
if (ch != '?') { |
|
|
|
|
|
|
|
if (ch != strArr[strIdxStart]) { |
|
|
|
|
|
|
|
return false; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
patIdxStart++; |
|
|
|
|
|
|
|
strIdxStart++; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
return true; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private boolean matchAfterLastStarOrCurly() { |
|
|
|
|
|
|
|
while ((ch = patArr[patIdxEnd]) != '*' && ch != '}' && strIdxStart <= strIdxEnd) { |
|
|
|
|
|
|
|
if (ch != '?') { |
|
|
|
|
|
|
|
if (ch != strArr[strIdxEnd]) { |
|
|
|
|
|
|
|
return false; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
patIdxEnd--; |
|
|
|
|
|
|
|
strIdxEnd--; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
return true; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|