Added combine method to PathMatcher, for combining two patterns.

This commit is contained in:
Arjen Poutsma 2009-04-03 10:44:57 +00:00
parent 6e7e107621
commit 56ddc76712
3 changed files with 126 additions and 14 deletions

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2007 the original author or authors.
* Copyright 2002-2009 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -290,18 +290,96 @@ public class AntPathMatcher implements PathMatcher {
return variables;
}
/**
* Combines two patterns into a new pattern that is returned.
* <p>This implementation simply concatenates the two patterns, unless the
* first pattern contains a file extension match (such as {@code *.html}. In
* that case, the second pattern should be included in the first, or an
* {@code IllegalArgumentException} is thrown.
* <p>For example:
* <table>
* <tr><th>Pattern 1</th><th>Pattern 2</th><th>Result</th></tr>
* <tr><td>/hotels</td><td>{@code null}</td><td>/hotels</td></tr>
* <tr><td>{@code null}</td><td>/hotels</td><td>/hotels</td></tr>
* <tr><td>/hotels</td><td>/bookings</td><td>/hotels/bookings</td></tr>
* <tr><td>/hotels</td><td>bookings</td><td>/hotels/bookings</td></tr>
* <tr><td>/hotels/*</td><td>/bookings</td><td>/hotels/bookings</td></tr>
* <tr><td>/hotels/&#42;&#42;</td><td>/bookings</td><td>/hotels/&#42;&#42;/bookings</td></tr>
* <tr><td>/hotels</td><td>{hotel}</td><td>/hotels/{hotel}</td></tr>
* <tr><td>/hotels/*</td><td>{hotel}</td><td>/hotels/{hotel}</td></tr>
* <tr><td>/hotels/&#42;&#42;</td><td>{hotel}</td><td>/hotels/&#42;&#42;/{hotel}</td></tr>
* <tr><td>/*.html</td><td>/hotels.html</td><td>/hotels.html</td></tr>
* <tr><td>/*.html</td><td>/hotels</td><td>IllegalArgumentException</td></tr>
* </table>
*
* @param pattern1 the first pattern
* @param pattern2 the second pattern
* @return the combination of the two patterns
* @throws IllegalArgumentException when the two patterns cannot be combined
*/
public String combine(String pattern1, String pattern2) {
if (!StringUtils.hasText(pattern1) && !StringUtils.hasText(pattern2)) {
return "";
}
else if (!StringUtils.hasText(pattern1)) {
return pattern2;
}
else if (!StringUtils.hasText(pattern2)) {
return pattern1;
}
else if (pattern1.endsWith("/*")) {
if (pattern2.startsWith("/")) {
// /hotels/* + /booking -> /hotels/booking
return pattern1.substring(0, pattern1.length() - 1) + pattern2.substring(1);
}
else {
// /hotels/* + booking -> /hotels/booking
return pattern1.substring(0, pattern1.length() - 1) + pattern2;
}
}
else if (pattern1.endsWith("/**")) {
if (pattern2.startsWith("/")) {
// /hotels/** + /booking -> /hotels/**/booking
return pattern1 + pattern2;
}
else {
// /hotels/** + booking -> /hotels/**/booking
return pattern1 + "/" + pattern2;
}
}
else {
int idx = pattern1.indexOf("*.");
if (idx == -1) {
if (pattern1.endsWith("/") || pattern2.startsWith("/")) {
return pattern1 + pattern2;
}
else {
return pattern1 + "/" + pattern2;
}
}
else {
// /*.html + /hotels.html -> /hotels.html
String extension = pattern1.substring(idx + 1);
if (pattern2.endsWith(extension)) {
return pattern2;
}
else {
// /*.html + /hotels -> exception
throw new IllegalArgumentException(
"Conflicting paths: \"" + pattern1 + "\" does not include \"" + pattern2 + "\"");
}
}
}
}
/**
* Given a full path, returns a {@link Comparator} suitable for sorting patterns in order of explicitness.
*
* <p>The returned <code>Comparator</code> will {@linkplain java.util.Collections#sort(java.util.List,
* java.util.Comparator) sort} a list so that more specific patterns (without uri templates or wild cards) come before
* generic patterns. So given a list with the following patterns:
* <ol>
* <li><code>/hotels/new</code></li>
* <li><code>/hotels/{hotel}</code></li>
* <li><code>/hotels/*</code></li>
* </ol>
* the returned comparator will sort this list so that the order will be as indicated.
* generic patterns. So given a list with the following patterns: <ol> <li><code>/hotels/new</code></li>
* <li><code>/hotels/{hotel}</code></li> <li><code>/hotels/*</code></li> </ol> the returned comparator will sort this
* list so that the order will be as indicated.
*
* @param path the full path to use for comparison
* @return a comparator capable of sorting patterns in order of explicitness
@ -310,7 +388,7 @@ public class AntPathMatcher implements PathMatcher {
return new AntPatternComparator(path);
}
private static class AntPatternComparator implements Comparator<String>{
private static class AntPatternComparator implements Comparator<String> {
private final String path;

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2007 the original author or authors.
* Copyright 2002-2009 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -94,7 +94,6 @@ public interface PathMatcher {
/**
* Given a pattern and a full path, extract the URI template variables. URI template
* variables are expressed through curly brackets ('{' and '}').
*
* <p>For example: For pattern "/hotels/{hotel}" and path "/hotels/1", this method will
* return a map containing "hotel"->"1".
*
@ -106,7 +105,6 @@ public interface PathMatcher {
/**
* Given a full path, returns a {@link Comparator} suitable for sorting patterns in order of explicitness.
*
* <p>The full algorithm used depends on the underlying implementation, but generally, the returned
* <code>Comparator</code> will {@linkplain java.util.Collections#sort(java.util.List, java.util.Comparator) sort} a
* list so that more specific patterns come before generic patterns.
@ -115,4 +113,15 @@ public interface PathMatcher {
* @return a comparator capable of sorting patterns in order of explicitness
*/
Comparator<String> getPatternComparator(String path);
/**
* Combines two patterns into a new pattern that is returned.
* <p>The full algorithm used for combining the two pattern depends on the underlying implementation.
*
* @param pattern1 the first pattern
* @param pattern2 the second pattern
* @return the combination of the two patterns
* @throws IllegalArgumentException when the two patterns cannot be combined
*/
String combine(String pattern1, String pattern2);
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2007 the original author or authors.
* Copyright 2002-2009 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -334,6 +334,31 @@ public class AntPathMatcherTests {
assertEquals(expected, result);
}
@Test
public void combine() {
assertEquals("", pathMatcher.combine(null, null));
assertEquals("/hotels", pathMatcher.combine("/hotels", null));
assertEquals("/hotels", pathMatcher.combine(null, "/hotels"));
assertEquals("/hotels/booking", pathMatcher.combine("/hotels/*", "booking"));
assertEquals("/hotels/booking", pathMatcher.combine("/hotels/*", "/booking"));
assertEquals("/hotels/**/booking", pathMatcher.combine("/hotels/**", "booking"));
assertEquals("/hotels/**/booking", pathMatcher.combine("/hotels/**", "/booking"));
assertEquals("/hotels/booking", pathMatcher.combine("/hotels", "/booking"));
assertEquals("/hotels/booking", pathMatcher.combine("/hotels", "booking"));
assertEquals("/hotels/{hotel}", pathMatcher.combine("/hotels/*", "{hotel}"));
assertEquals("/hotels/**/{hotel}", pathMatcher.combine("/hotels/**", "{hotel}"));
assertEquals("/hotels/{hotel}", pathMatcher.combine("/hotels", "{hotel}"));
assertEquals("/hotels/*/booking/{booking}", pathMatcher.combine("/hotels/*/booking", "{booking}"));
assertEquals("/hotel.html", pathMatcher.combine("/*.html", "/hotel.html"));
try {
assertEquals("/hotel.html", pathMatcher.combine("/*.html", "/hotel"));
fail("IllegalArgumentException expected");
}
catch (IllegalArgumentException ex) {
// expected
}
}
@Test
public void patternComparator() {
Comparator<String> comparator = pathMatcher.getPatternComparator("/hotels/new");
@ -355,7 +380,7 @@ public class AntPathMatcherTests {
assertEquals(-1, comparator.compare("/hotels/{hotel}", "/hotels/*"));
assertEquals(1, comparator.compare("/hotels/*", "/hotels/{hotel}"));
assertEquals(-1, comparator.compare("/hotels/*","/hotels/*/**"));
assertEquals(-1, comparator.compare("/hotels/*", "/hotels/*/**"));
assertEquals(1, comparator.compare("/hotels/*/**", "/hotels/*"));
}