From 56ddc767129d4240b16a3d409d98343b8288673d Mon Sep 17 00:00:00 2001 From: Arjen Poutsma Date: Fri, 3 Apr 2009 10:44:57 +0000 Subject: [PATCH] Added combine method to PathMatcher, for combining two patterns. --- .../springframework/util/AntPathMatcher.java | 96 +++++++++++++++++-- .../org/springframework/util/PathMatcher.java | 15 ++- .../util/AntPathMatcherTests.java | 29 +++++- 3 files changed, 126 insertions(+), 14 deletions(-) diff --git a/org.springframework.core/src/main/java/org/springframework/util/AntPathMatcher.java b/org.springframework.core/src/main/java/org/springframework/util/AntPathMatcher.java index 9602d7cc1b0..367e5b843d9 100644 --- a/org.springframework.core/src/main/java/org/springframework/util/AntPathMatcher.java +++ b/org.springframework.core/src/main/java/org/springframework/util/AntPathMatcher.java @@ -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. + *

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. + *

For example: + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Pattern 1Pattern 2Result
/hotels{@code null}/hotels
{@code null}/hotels/hotels
/hotels/bookings/hotels/bookings
/hotelsbookings/hotels/bookings
/hotels/*/bookings/hotels/bookings
/hotels/**/bookings/hotels/**/bookings
/hotels{hotel}/hotels/{hotel}
/hotels/*{hotel}/hotels/{hotel}
/hotels/**{hotel}/hotels/**/{hotel}
/*.html/hotels.html/hotels.html
/*.html/hotelsIllegalArgumentException
+ * + * @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. * *

The returned Comparator 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: - *

    - *
  1. /hotels/new
  2. - *
  3. /hotels/{hotel}
  4. - *
  5. /hotels/*
  6. - *
- * 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:
  1. /hotels/new
  2. + *
  3. /hotels/{hotel}
  4. /hotels/*
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{ + private static class AntPatternComparator implements Comparator { private final String path; diff --git a/org.springframework.core/src/main/java/org/springframework/util/PathMatcher.java b/org.springframework.core/src/main/java/org/springframework/util/PathMatcher.java index 6804af56362..244aa884f1a 100644 --- a/org.springframework.core/src/main/java/org/springframework/util/PathMatcher.java +++ b/org.springframework.core/src/main/java/org/springframework/util/PathMatcher.java @@ -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 '}'). - * *

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. - * *

The full algorithm used depends on the underlying implementation, but generally, the returned * Comparator 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 getPatternComparator(String path); + + /** + * Combines two patterns into a new pattern that is returned. + *

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); } diff --git a/org.springframework.core/src/test/java/org/springframework/util/AntPathMatcherTests.java b/org.springframework.core/src/test/java/org/springframework/util/AntPathMatcherTests.java index 1491299ec65..72bfbeed5ad 100644 --- a/org.springframework.core/src/test/java/org/springframework/util/AntPathMatcherTests.java +++ b/org.springframework.core/src/test/java/org/springframework/util/AntPathMatcherTests.java @@ -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 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/*")); }