Proper null handling in PathPattern comparator

This commit is contained in:
Rossen Stoyanchev 2017-08-14 11:45:27 +03:00
parent adeb521ce4
commit 631b546d1c
2 changed files with 29 additions and 23 deletions

View File

@ -693,12 +693,17 @@ public class PathPattern implements Comparable<PathPattern> {
public static final Comparator<PathPattern> SPECIFICITY_COMPARATOR = (p1, p2) -> { public static final Comparator<PathPattern> SPECIFICITY_COMPARATOR = (p1, p2) -> {
// 1) null is sorted last // Same object or null == null?
if (p1 == p2) {
return 0;
}
// null is sorted last
if (p2 == null) { if (p2 == null) {
return -1; return -1;
} }
// 2) catchall patterns are sorted last. If both catchall then the // catchall patterns are sorted last. If both catchall then the
// length is considered // length is considered
if (p1.isCatchAll()) { if (p1.isCatchAll()) {
if (p2.isCatchAll()) { if (p2.isCatchAll()) {
@ -715,14 +720,14 @@ public class PathPattern implements Comparable<PathPattern> {
return -1; return -1;
} }
// 3) This will sort such that if they differ in terms of wildcards or // 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 // captured variable counts, the one with the most will be sorted last
int score = p1.getScore() - p2.getScore(); int score = p1.getScore() - p2.getScore();
if (score != 0) { if (score != 0) {
return (score < 0) ? -1 : +1; return (score < 0) ? -1 : +1;
} }
// 4) longer is better // longer is better
int lenDifference = p1.getNormalizedLength() - p2.getNormalizedLength(); int lenDifference = p1.getNormalizedLength() - p2.getNormalizedLength();
return Integer.compare(0, lenDifference); return Integer.compare(0, lenDifference);
}; };

View File

@ -886,10 +886,7 @@ public class PathPatternTests {
@Test @Test
public void patternComparator() { public void patternComparator() {
Comparator<PathPattern> comparator = (p1, p2) -> { Comparator<PathPattern> comparator = PathPattern.SPECIFICITY_COMPARATOR;
int index = p1.compareTo(p2);
return (index != 0 ? index : p1.getPatternString().compareTo(p2.getPatternString()));
};
assertEquals(0, comparator.compare(parse("/hotels/new"), parse("/hotels/new"))); assertEquals(0, comparator.compare(parse("/hotels/new"), parse("/hotels/new")));
@ -970,7 +967,7 @@ public class PathPatternTests {
} }
@Test @Test
public void patternCompareTo() { public void patternCompareToNull() {
PathPatternParser p = new PathPatternParser(); PathPatternParser p = new PathPatternParser();
PathPattern pp = p.parse("/abc"); PathPattern pp = p.parse("/abc");
assertEquals(-1, pp.compareTo(null)); assertEquals(-1, pp.compareTo(null));
@ -978,58 +975,62 @@ public class PathPatternTests {
@Test @Test
public void patternComparatorSort() { public void patternComparatorSort() {
Comparator<PathPattern> comparator = (p1, p2) -> { Comparator<PathPattern> comparator = PathPattern.SPECIFICITY_COMPARATOR;
int index = p1.compareTo(p2);
return (index != 0 ? index : p1.getPatternString().compareTo(p2.getPatternString()));
};
List<PathPattern> paths = new ArrayList<>(3); List<PathPattern> paths = new ArrayList<>(3);
PathPatternParser pp = new PathPatternParser(); PathPatternParser pp = new PathPatternParser();
paths.add(null);
paths.add(null);
paths.sort(comparator);
assertNull(paths.get(0));
assertNull(paths.get(1));
paths.clear();
paths.add(null); paths.add(null);
paths.add(pp.parse("/hotels/new")); paths.add(pp.parse("/hotels/new"));
Collections.sort(paths, comparator); paths.sort(comparator);
assertEquals("/hotels/new", paths.get(0).getPatternString()); assertEquals("/hotels/new", paths.get(0).getPatternString());
assertNull(paths.get(1)); assertNull(paths.get(1));
paths.clear(); paths.clear();
paths.add(pp.parse("/hotels/*")); paths.add(pp.parse("/hotels/*"));
paths.add(pp.parse("/hotels/new")); paths.add(pp.parse("/hotels/new"));
Collections.sort(paths, comparator); paths.sort(comparator);
assertEquals("/hotels/new", paths.get(0).getPatternString()); assertEquals("/hotels/new", paths.get(0).getPatternString());
assertEquals("/hotels/*", paths.get(1).getPatternString()); assertEquals("/hotels/*", paths.get(1).getPatternString());
paths.clear(); paths.clear();
paths.add(pp.parse("/hotels/new")); paths.add(pp.parse("/hotels/new"));
paths.add(pp.parse("/hotels/*")); paths.add(pp.parse("/hotels/*"));
Collections.sort(paths, comparator); paths.sort(comparator);
assertEquals("/hotels/new", paths.get(0).getPatternString()); assertEquals("/hotels/new", paths.get(0).getPatternString());
assertEquals("/hotels/*", paths.get(1).getPatternString()); assertEquals("/hotels/*", paths.get(1).getPatternString());
paths.clear(); paths.clear();
paths.add(pp.parse("/hotels/**")); paths.add(pp.parse("/hotels/**"));
paths.add(pp.parse("/hotels/*")); paths.add(pp.parse("/hotels/*"));
Collections.sort(paths, comparator); paths.sort(comparator);
assertEquals("/hotels/*", paths.get(0).getPatternString()); assertEquals("/hotels/*", paths.get(0).getPatternString());
assertEquals("/hotels/**", paths.get(1).getPatternString()); assertEquals("/hotels/**", paths.get(1).getPatternString());
paths.clear(); paths.clear();
paths.add(pp.parse("/hotels/*")); paths.add(pp.parse("/hotels/*"));
paths.add(pp.parse("/hotels/**")); paths.add(pp.parse("/hotels/**"));
Collections.sort(paths, comparator); paths.sort(comparator);
assertEquals("/hotels/*", paths.get(0).getPatternString()); assertEquals("/hotels/*", paths.get(0).getPatternString());
assertEquals("/hotels/**", paths.get(1).getPatternString()); assertEquals("/hotels/**", paths.get(1).getPatternString());
paths.clear(); paths.clear();
paths.add(pp.parse("/hotels/{hotel}")); paths.add(pp.parse("/hotels/{hotel}"));
paths.add(pp.parse("/hotels/new")); paths.add(pp.parse("/hotels/new"));
Collections.sort(paths, comparator); paths.sort(comparator);
assertEquals("/hotels/new", paths.get(0).getPatternString()); assertEquals("/hotels/new", paths.get(0).getPatternString());
assertEquals("/hotels/{hotel}", paths.get(1).getPatternString()); assertEquals("/hotels/{hotel}", paths.get(1).getPatternString());
paths.clear(); paths.clear();
paths.add(pp.parse("/hotels/new")); paths.add(pp.parse("/hotels/new"));
paths.add(pp.parse("/hotels/{hotel}")); paths.add(pp.parse("/hotels/{hotel}"));
Collections.sort(paths, comparator); paths.sort(comparator);
assertEquals("/hotels/new", paths.get(0).getPatternString()); assertEquals("/hotels/new", paths.get(0).getPatternString());
assertEquals("/hotels/{hotel}", paths.get(1).getPatternString()); assertEquals("/hotels/{hotel}", paths.get(1).getPatternString());
paths.clear(); paths.clear();
@ -1037,7 +1038,7 @@ public class PathPatternTests {
paths.add(pp.parse("/hotels/*")); paths.add(pp.parse("/hotels/*"));
paths.add(pp.parse("/hotels/{hotel}")); paths.add(pp.parse("/hotels/{hotel}"));
paths.add(pp.parse("/hotels/new")); paths.add(pp.parse("/hotels/new"));
Collections.sort(paths, comparator); paths.sort(comparator);
assertEquals("/hotels/new", paths.get(0).getPatternString()); assertEquals("/hotels/new", paths.get(0).getPatternString());
assertEquals("/hotels/{hotel}", paths.get(1).getPatternString()); assertEquals("/hotels/{hotel}", paths.get(1).getPatternString());
assertEquals("/hotels/*", paths.get(2).getPatternString()); assertEquals("/hotels/*", paths.get(2).getPatternString());
@ -1046,7 +1047,7 @@ public class PathPatternTests {
paths.add(pp.parse("/hotels/ne*")); paths.add(pp.parse("/hotels/ne*"));
paths.add(pp.parse("/hotels/n*")); paths.add(pp.parse("/hotels/n*"));
Collections.shuffle(paths); Collections.shuffle(paths);
Collections.sort(paths, comparator); paths.sort(comparator);
assertEquals("/hotels/ne*", paths.get(0).getPatternString()); assertEquals("/hotels/ne*", paths.get(0).getPatternString());
assertEquals("/hotels/n*", paths.get(1).getPatternString()); assertEquals("/hotels/n*", paths.get(1).getPatternString());
paths.clear(); paths.clear();
@ -1066,7 +1067,7 @@ public class PathPatternTests {
}; };
paths.add(pp.parse("/*/login.*")); paths.add(pp.parse("/*/login.*"));
paths.add(pp.parse("/*/endUser/action/login.*")); paths.add(pp.parse("/*/endUser/action/login.*"));
Collections.sort(paths, comparator); paths.sort(comparator);
assertEquals("/*/endUser/action/login.*", paths.get(0).getPatternString()); assertEquals("/*/endUser/action/login.*", paths.get(0).getPatternString());
assertEquals("/*/login.*", paths.get(1).getPatternString()); assertEquals("/*/login.*", paths.get(1).getPatternString());
paths.clear(); paths.clear();