Further encapsulate AntPatternComparator$PatternInfo

Issue: SPR-6741
This commit is contained in:
Rossen Stoyanchev 2014-05-28 13:28:38 -04:00 committed by Brian Clozel
parent f829cd1b35
commit ce0473f926
2 changed files with 89 additions and 55 deletions

View File

@ -594,15 +594,16 @@ public class AntPathMatcher implements PathMatcher {
@Override @Override
public int compare(String pattern1, String pattern2) { public int compare(String pattern1, String pattern2) {
boolean pattern1NullCaptureAll = isNullOrCaptureAllPattern(pattern1); PatternInfo info1 = new PatternInfo(pattern1);
boolean pattern2NullCaptureAll = isNullOrCaptureAllPattern(pattern2); PatternInfo info2 = new PatternInfo(pattern2);
if (pattern1NullCaptureAll && pattern2NullCaptureAll) {
if (info1.isLeastSpecific() && info2.isLeastSpecific()) {
return 0; return 0;
} }
else if (pattern1NullCaptureAll) { else if (info1.isLeastSpecific()) {
return 1; return 1;
} }
else if (pattern2NullCaptureAll) { else if (info2.isLeastSpecific()) {
return -1; return -1;
} }
@ -618,93 +619,126 @@ public class AntPathMatcher implements PathMatcher {
return 1; return 1;
} }
PatternElements pattern1Elements = new PatternElements(pattern1); if (info1.isPrefixPattern() && info2.getDoubleWildcards() == 0) {
PatternElements pattern2Elements = new PatternElements(pattern2);
if(pattern1Elements.endsWithCatchAll && pattern2Elements.catchAllCount == 0) {
return 1; return 1;
} }
else if(pattern2Elements.endsWithCatchAll && pattern1Elements.catchAllCount == 0) { else if (info2.isPrefixPattern() && info1.getDoubleWildcards() == 0) {
return -1; return -1;
} }
int totalCount1 = pattern1Elements.bracketCount + pattern1Elements.wildcardsCount; if (info1.getTotalCount() != info2.getTotalCount()) {
int totalCount2 = pattern2Elements.bracketCount + pattern2Elements.wildcardsCount; return info1.getTotalCount() - info2.getTotalCount();
if (totalCount1 != totalCount2) {
return totalCount1 - totalCount2;
} }
int pattern1Length = getPatternLength(pattern1); if (info1.getLength() != info2.getLength()) {
int pattern2Length = getPatternLength(pattern2); return info2.getLength() - info1.getLength();
if (pattern1Length != pattern2Length) {
return pattern2Length - pattern1Length;
} }
if (pattern1Elements.wildcardsCount < pattern2Elements.wildcardsCount) { if (info1.getSingleWildcards() < info2.getSingleWildcards()) {
return -1; return -1;
} }
else if (pattern2Elements.wildcardsCount < pattern1Elements.wildcardsCount) { else if (info2.getSingleWildcards() < info1.getSingleWildcards()) {
return 1; return 1;
} }
if (pattern1Elements.bracketCount < pattern2Elements.bracketCount) { if (info1.getUriVars() < info2.getUriVars()) {
return -1; return -1;
} }
else if (pattern2Elements.bracketCount < pattern1Elements.bracketCount) { else if (info2.getUriVars() < info1.getUriVars()) {
return 1; return 1;
} }
return 0; return 0;
} }
private boolean isNullOrCaptureAllPattern(String pattern) {
return pattern == null || "/**".equals(pattern);
}
/** /**
* Returns the length of the given pattern, where template variables are considered to be 1 long. * Value class that holds information about the pattern, e.g. number of
* occurrences of "*", "**", and "{" pattern elements.
*/ */
private int getPatternLength(String pattern) { private static class PatternInfo {
return VARIABLE_PATTERN.matcher(pattern).replaceAll("#").length();
}
/** private final String pattern;
* Value class that holds the number of occurrences for "*", "**", and "{" pattern elements
*/
private class PatternElements {
int bracketCount = 0;
int wildcardsCount = 0;
int catchAllCount = 0;
boolean endsWithCatchAll;
public PatternElements(String pattern) { private int uriVars;
if(pattern == null || pattern.length() == 0) { private int singleWildcards;
return;
private int doubleWildcards;
private boolean catchAllPattern;
private boolean prefixPattern;
private Integer length;
public PatternInfo(String pattern) {
this.pattern = pattern;
if (this.pattern != null) {
initCounters();
this.catchAllPattern = this.pattern.equals("/**");
this.prefixPattern = !this.catchAllPattern && this.pattern.endsWith("/**");
} }
int pos = 0; if (this.uriVars == 0) {
this.length = (this.pattern != null ? this.pattern.length() : 0);
}
}
while(pos < pattern.length()) { protected void initCounters() {
if(pattern.charAt(pos) == '{') { int pos = 0;
bracketCount++; while (pos < this.pattern.length()) {
if(this.pattern.charAt(pos) == '{') {
this.uriVars++;
pos++; pos++;
} else if(pattern.charAt(pos) == '*') { }
if(pos + 1 < pattern.length() && pattern.charAt(pos + 1) == '*') { else if(this.pattern.charAt(pos) == '*') {
catchAllCount++; if(pos + 1 < this.pattern.length() && this.pattern.charAt(pos + 1) == '*') {
this.doubleWildcards++;
pos += 2; pos += 2;
} else { }
wildcardsCount++; else {
this.singleWildcards++;
pos++; pos++;
} }
} else { } else {
pos++; pos++;
} }
} }
endsWithCatchAll = pattern.endsWith("**");
} }
public int getUriVars() {
return this.uriVars;
}
public int getSingleWildcards() {
return this.singleWildcards;
}
public int getDoubleWildcards() {
return this.doubleWildcards;
}
public boolean isLeastSpecific() {
return (this.pattern == null || this.catchAllPattern);
}
public boolean isPrefixPattern() {
return this.prefixPattern;
}
public int getTotalCount() {
return this.uriVars + this.singleWildcards + (2 * this.doubleWildcards);
}
/**
* Returns the length of the given pattern, where template variables are considered to be 1 long.
*/
public int getLength() {
if (this.length == null) {
this.length = VARIABLE_PATTERN.matcher(this.pattern).replaceAll("#").length();
}
return this.length;
}
} }
} }

View File

@ -457,8 +457,8 @@ public class AntPathMatcherTests {
assertEquals(1, comparator.compare("/hotels/**", "/hotels/{hotel}/bookings/{booking}/cutomers/{customer}")); assertEquals(1, comparator.compare("/hotels/**", "/hotels/{hotel}/bookings/{booking}/cutomers/{customer}"));
assertEquals(1, comparator.compare("/hotels/foo/bar/**", "/hotels/{hotel}")); assertEquals(1, comparator.compare("/hotels/foo/bar/**", "/hotels/{hotel}"));
assertEquals(-1, comparator.compare("/hotels/{hotel}", "/hotels/foo/bar/**")); assertEquals(-1, comparator.compare("/hotels/{hotel}", "/hotels/foo/bar/**"));
assertEquals(-12, comparator.compare("/hotels/**/bookings/**", "/hotels/**")); assertEquals(2, comparator.compare("/hotels/**/bookings/**", "/hotels/**"));
assertEquals(12, comparator.compare("/hotels/**", "/hotels/**/bookings/**")); assertEquals(-2, comparator.compare("/hotels/**", "/hotels/**/bookings/**"));
//SPR-8683 //SPR-8683
assertEquals(1, comparator.compare("/**", "/hotels/{hotel}")); assertEquals(1, comparator.compare("/**", "/hotels/{hotel}"));