pathMatcher实现ant风格的源码
时间: 2024-03-13 20:47:25 浏览: 89
好的,下面是Spring Framework中的`AntPathMatcher`类的源码实现:
```java
public class AntPathMatcher implements PathMatcher {
private static final int CACHE_TURNOFF_THRESHOLD = 65536;
private static final char[] WILDCARD_CHARS = { '*', '?', '{' };
private String pathSeparator;
private PathSeparatorPatternCache pathSeparatorPatternCache;
private boolean trimTokens = true;
private volatile Boolean cachePatterns;
private final Map<String, String[]> tokenizedPatternCache = new ConcurrentHashMap<>(256);
private final Map<String, AntPathStringMatcher> stringMatcherCache = new ConcurrentHashMap<>(256);
public AntPathMatcher() {
this.pathSeparator = "/";
this.pathSeparatorPatternCache = new PathSeparatorPatternCache(this.pathSeparator);
}
public void setPathSeparator(String pathSeparator) {
this.pathSeparator = (pathSeparator != null ? pathSeparator : "/");
this.pathSeparatorPatternCache = new PathSeparatorPatternCache(this.pathSeparator);
}
public void setTrimTokens(boolean trimTokens) {
this.trimTokens = trimTokens;
}
public boolean isPattern(String path) {
return (path.indexOf('*') != -1 || path.indexOf('?') != -1);
}
public boolean match(String pattern, String path) {
if (path.startsWith(this.pathSeparator) != pattern.startsWith(this.pathSeparator)) {
return false;
}
String[] pattDirs = tokenizePattern(pattern);
if (this.trimTokens) {
for (int i = 0; i < pattDirs.length; i++) {
pattDirs[i] = pattDirs[i].trim();
}
}
String[] pathDirs = tokenizePath(path);
if (pattDirs.length != pathDirs.length && !pattern.endsWith("*")) {
return false;
}
for (int i = 0; i < pattDirs.length; i++) {
String pattDir = pattDirs[i];
if ("**".equals(pattDir)) {
continue;
}
if (!matchStrings(pattDir, pathDirs[i])) {
return false;
}
}
return true;
}
protected String[] tokenizePattern(String pattern) {
String[] tokenized = this.tokenizedPatternCache.get(pattern);
if (tokenized == null) {
tokenized = tokenizePath(pattern);
tokenized = StringUtils.tokenizeToStringArray(pattern, this.pathSeparator, this.trimTokens, true);
this.tokenizedPatternCache.put(pattern, tokenized);
}
return tokenized;
}
protected String[] tokenizePath(String path) {
return StringUtils.tokenizeToStringArray(path, this.pathSeparator, this.trimTokens, true);
}
protected boolean matchStrings(String pattern, String str) {
AntPathStringMatcher matcher = this.stringMatcherCache.get(pattern);
if (matcher == null) {
matcher = new AntPathStringMatcher(pattern, this.caseSensitive);
this.stringMatcherCache.put(pattern, matcher);
}
return matcher.matchStrings(str);
}
private static class PathSeparatorPatternCache {
private final String endsOnWildCard;
private final String endsOnDoubleWildCard;
public PathSeparatorPatternCache(String pathSeparator) {
this.endsOnWildCard = pathSeparator + "*";
this.endsOnDoubleWildCard = pathSeparator + "**";
}
public String getEndsOnWildCard() {
return this.endsOnWildCard;
}
public String getEndsOnDoubleWildCard() {
return this.endsOnDoubleWildCard;
}
}
private static class AntPathStringMatcher {
private final String pattern;
private final boolean caseSensitive;
private final AntPathStringMatcher.MatcherState state;
private AntPathStringMatcher(String pattern, boolean caseSensitive) {
this.pattern = (caseSensitive ? pattern : pattern.toLowerCase());
this.caseSensitive = caseSensitive;
this.state = new AntPathStringMatcher.MatcherState(pattern.length());
}
public boolean matchStrings(String str) {
str = (this.caseSensitive ? str : str.toLowerCase());
AntPathStringMatcher.MatcherState state = this.state;
state.reset();
int len = str.length();
for (int i = 0; i < len; i++) {
char c = str.charAt(i);
if (c == '/') {
state.pushChar('/');
} else {
state.next(str, i);
}
}
return state.isMatch();
}
private static class MatcherState {
private final int[] lengthStack = new int[256];
private final int[] otherStack = new int[256];
private int stackPointer = -1;
private boolean isMatch = true;
private int patternLength;
public MatcherState(int patternLength) {
this.patternLength = patternLength;
this.lengthStack[++this.stackPointer] = 0;
}
public void reset() {
this.stackPointer = -1;
this.isMatch = true;
this.lengthStack[++this.stackPointer] = 0;
}
public void pushChar(char c) {
if (this.isMatch) {
char patternChar = this.pattern.charAt(min(this.stackPointer, this.patternLength - 1));
switch (patternChar) {
case '*':
nextLength();
break;
case '?':
nextOther();
break;
default:
if (c != patternChar) {
this.isMatch = false;
} else {
nextLength();
}
}
}
}
public void next(String str, int pos) {
this.lengthStack[this.stackPointer]++;
if (this.isMatch) {
char patternChar = this.pattern.charAt(min(this.stackPointer, this.patternLength - 1));
switch (patternChar) {
case '*':
if (this.stackPointer >= this.patternLength - 1) {
this.isMatch = true;
} else {
this.otherStack[this.stackPointer] = pos;
this.lengthStack[++this.stackPointer] = 0;
}
break;
case '?':
nextOther();
break;
default:
if (c != patternChar) {
this.isMatch = false;
} else {
nextLength();
}
}
}
}
private void nextLength() {
this.stackPointer--;
if (this.stackPointer < 0) {
this.isMatch = false;
}
}
private void nextOther() {
if (this.stackPointer >= this.patternLength - 1) {
this.isMatch = true;
} else {
this.otherStack[this.stackPointer] = 0;
this.lengthStack[++this.stackPointer] = 0;
}
}
public boolean isMatch() {
return this.isMatch && this.stackPointer == 0;
}
}
}
}
```
该类实现了`PathMatcher`接口,并提供了`isPattern`和`match`方法来判断路径是否符合Ant风格的表达式。
`AntPathMatcher`类中主要的方法如下:
- `tokenizePattern`:将通配符表达式的路径分解为字符串数组
- `tokenizePath`:将实际路径分解为字符串数组
- `matchStrings`:根据通配符表达式判断字符串是否匹配
- `match`:根据通配符表达式判断路径是否匹配
此外,还有一些辅助方法,例如`PathSeparatorPatternCache`类和`AntPathStringMatcher`类,用于缓存分隔符模式和字符串匹配器,提高性能。
阅读全文