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); * Value class that holds information about the pattern, e.g. number of
* occurrences of "*", "**", and "{" pattern elements.
*/
private static class PatternInfo {
private final String pattern;
private int uriVars;
private int singleWildcards;
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("/**");
}
if (this.uriVars == 0) {
this.length = (this.pattern != null ? this.pattern.length() : 0);
}
}
protected void initCounters() {
int pos = 0;
while (pos < this.pattern.length()) {
if(this.pattern.charAt(pos) == '{') {
this.uriVars++;
pos++;
}
else if(this.pattern.charAt(pos) == '*') {
if(pos + 1 < this.pattern.length() && this.pattern.charAt(pos + 1) == '*') {
this.doubleWildcards++;
pos += 2;
}
else {
this.singleWildcards++;
pos++;
}
} else {
pos++;
}
}
}
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. * Returns the length of the given pattern, where template variables are considered to be 1 long.
*/ */
private int getPatternLength(String pattern) { public int getLength() {
return VARIABLE_PATTERN.matcher(pattern).replaceAll("#").length(); if (this.length == null) {
this.length = VARIABLE_PATTERN.matcher(this.pattern).replaceAll("#").length();
} }
return this.length;
/**
* 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) {
if(pattern == null || pattern.length() == 0) {
return;
} }
int pos = 0;
while(pos < pattern.length()) {
if(pattern.charAt(pos) == '{') {
bracketCount++;
pos++;
} else if(pattern.charAt(pos) == '*') {
if(pos + 1 < pattern.length() && pattern.charAt(pos + 1) == '*') {
catchAllCount++;
pos += 2;
} else {
wildcardsCount++;
pos++;
}
} else {
pos++;
}
}
endsWithCatchAll = pattern.endsWith("**");
}
} }
} }

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}"));