Author: sergeyb
Date: Wed Dec  3 10:04:20 2008
New Revision: 722988

URL: http://svn.apache.org/viewvc?rev=722988&view=rev
Log:
JAXRS : support for arbitrary regular expressions

Modified:
    
cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/model/OperationResourceInfoComparator.java
    
cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/model/URITemplate.java
    
cxf/trunk/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/impl/UriInfoImplTest.java
    
cxf/trunk/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/model/URITemplateTest.java
    cxf/trunk/systests/src/test/java/org/apache/cxf/systest/jaxrs/BookStore.java
    
cxf/trunk/systests/src/test/java/org/apache/cxf/systest/jaxrs/JAXRSClientServerBookTest.java

Modified: 
cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/model/OperationResourceInfoComparator.java
URL: 
http://svn.apache.org/viewvc/cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/model/OperationResourceInfoComparator.java?rev=722988&r1=722987&r2=722988&view=diff
==============================================================================
--- 
cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/model/OperationResourceInfoComparator.java
 (original)
+++ 
cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/model/OperationResourceInfoComparator.java
 Wed Dec  3 10:04:20 2008
@@ -48,6 +48,12 @@
             return g1 < g2 ? 1 : -1;
         }
         
+        int gCustom1 = 
e1.getURITemplate().getNumberOfGroupsWithCustomExpression();
+        int gCustom2 = 
e2.getURITemplate().getNumberOfGroupsWithCustomExpression();
+        if (gCustom1 != gCustom2) {
+            // descending order 
+            return gCustom1 < gCustom2 ? 1 : -1;
+        }
         
         int result = JAXRSUtils.compareSortedMediaTypes(
                           e1.getConsumeTypes(), 

Modified: 
cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/model/URITemplate.java
URL: 
http://svn.apache.org/viewvc/cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/model/URITemplate.java?rev=722988&r1=722987&r2=722988&view=diff
==============================================================================
--- 
cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/model/URITemplate.java
 (original)
+++ 
cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/model/URITemplate.java
 Wed Dec  3 10:04:20 2008
@@ -21,7 +21,6 @@
 
 
 import java.util.ArrayList;
-import java.util.Collections;
 import java.util.List;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
@@ -38,68 +37,63 @@
     public static final String TEMPLATE_PARAMETERS = 
"jaxrs.template.parameters";
     
     public static final String LIMITED_REGEX_SUFFIX = "(/.*)?";
-    public static final String UNLIMITED_REGEX_SUFFIX = "(/)?";
     public static final String FINAL_MATCH_GROUP = "FINAL_MATCH_GROUP";
     
     /**
      * The regular expression for matching URI templates and names.
      */
     private static final Pattern TEMPLATE_NAMES_PATTERN = 
-        Pattern.compile("\\{(\\w[-\\w\\.]*)\\}");
-
-    /**
-     * A URI template is converted into a regular expression by substituting
-     * (.*?) for each occurrence of {\([w- 14 \. ]+?\)} within the URL
-     * template
-     */
-    private static final String PATH_VARIABLE_REGEX = "([^/]+?)";
-    private static final String PATH_UNLIMITED_VARIABLE_REGEX = "(.*?)";
+        Pattern.compile("\\{(\\w[-\\w\\.]*)(\\:(.+?))?\\}");
 
+    private static final String DEFAULT_PATH_VARIABLE_REGEX = "([^/]+?)";
+    
     private final String template;
-    private final List<String> templateVariables;
+    private final List<String> templateVariables = new ArrayList<String>();
+    private final List<String> customTemplateVariables = new 
ArrayList<String>();
     private final Pattern templateRegexPattern;
     private final String literals;
 
-    public URITemplate(String theTemplate) {
-        this(theTemplate, true);
-    }
     
-    public URITemplate(String theTemplate, boolean limited) {
+    public URITemplate(String theTemplate) {
+        
         this.template = theTemplate;
         
         StringBuilder literalChars = new StringBuilder();
-        StringBuilder stringBuilder = new StringBuilder();
-        List<String> names = new ArrayList<String>();
-
+        StringBuilder patternBuilder = new StringBuilder();
+        
         // compute a regular expression from URI template
         Matcher matcher = TEMPLATE_NAMES_PATTERN.matcher(template);
         int i = 0;
         while (matcher.find()) {
-            literalChars.append(template.substring(i, matcher.start()));
-            copyURITemplateCharacters(template, i, matcher.start(), 
stringBuilder);
+            templateVariables.add(matcher.group(1).trim());
+            
+            String substr = escapeCharacters(template.substring(i, 
matcher.start()));
+            literalChars.append(substr);
+            patternBuilder.append(substr);
             i = matcher.end();
-            if (!limited && i == template.length()) {
-                stringBuilder.append(PATH_UNLIMITED_VARIABLE_REGEX);
+            if (matcher.group(2) != null && matcher.group(3) != null) {
+                patternBuilder.append('(');
+                patternBuilder.append(matcher.group(3).trim());
+                patternBuilder.append(')');
+                customTemplateVariables.add(matcher.group(1).trim());
             } else {
-                stringBuilder.append(PATH_VARIABLE_REGEX);
-            }
-            names.add(matcher.group(1));
+                patternBuilder.append(DEFAULT_PATH_VARIABLE_REGEX);
+            } 
         }
-        literalChars.append(template.substring(i, template.length()));
-        copyURITemplateCharacters(template, i, template.length(), 
stringBuilder);
+        String substr = escapeCharacters(template.substring(i, 
template.length()));
+        literalChars.append(substr);
+        patternBuilder.append(substr);
 
         literals = literalChars.toString();
-        templateVariables = Collections.unmodifiableList(names);
-
-        int endPos = stringBuilder.length() - 1;
-        boolean endsWithSlash = (endPos >= 0) ? stringBuilder.charAt(endPos) 
== '/' : false;
         
+        int endPos = patternBuilder.length() - 1;
+        boolean endsWithSlash = (endPos >= 0) ? patternBuilder.charAt(endPos) 
== '/' : false;
         if (endsWithSlash) {
-            stringBuilder.deleteCharAt(endPos);
+            patternBuilder.deleteCharAt(endPos);
         }
-        stringBuilder.append(limited ? LIMITED_REGEX_SUFFIX : 
UNLIMITED_REGEX_SUFFIX);
+        patternBuilder.append(LIMITED_REGEX_SUFFIX);
         
-        templateRegexPattern = Pattern.compile(stringBuilder.toString());
+        templateRegexPattern = Pattern.compile(patternBuilder.toString());
     }
 
     public String getLiteralChars() {
@@ -114,15 +108,21 @@
         return templateVariables.size();
     }
     
-    private void copyURITemplateCharacters(String templateValue, int start, 
int end, StringBuilder b) {
-        for (int i = start; i < end; i++) {
-            char c = templateValue.charAt(i);
-            if (c == '?') {
-                b.append("\\?");
-            } else {
-                b.append(c);
-            }
+    public int getNumberOfGroupsWithCustomExpression() {
+        return customTemplateVariables.size();
+    }
+    
+    private static String escapeCharacters(String expression) {
+        StringBuilder sb = new StringBuilder();
+        for (int i = 0; i < expression.length(); i++) {
+            char ch = expression.charAt(i);
+            sb.append(isReservedCharater(ch) ? "\\" + ch : ch);
         }
+        return sb.toString();
+    }
+    
+    private static boolean isReservedCharater(char ch) {
+        return '.' == ch;
     }
 
     public boolean match(String uri, MultivaluedMap<String, String> 
templateVariableToValue) {

Modified: 
cxf/trunk/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/impl/UriInfoImplTest.java
URL: 
http://svn.apache.org/viewvc/cxf/trunk/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/impl/UriInfoImplTest.java?rev=722988&r1=722987&r2=722988&view=diff
==============================================================================
--- 
cxf/trunk/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/impl/UriInfoImplTest.java
 (original)
+++ 
cxf/trunk/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/impl/UriInfoImplTest.java
 Wed Dec  3 10:04:20 2008
@@ -122,7 +122,7 @@
         
         // with suffix
         values.clear();
-        new URITemplate("/bar", false).match("/bar", values);
+        new URITemplate("/bar").match("/bar", values);
         
         u = new UriInfoImpl(mockMessage("http://localhost:8080/baz";, "/bar"),
                                         values);

Modified: 
cxf/trunk/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/model/URITemplateTest.java
URL: 
http://svn.apache.org/viewvc/cxf/trunk/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/model/URITemplateTest.java?rev=722988&r1=722987&r2=722988&view=diff
==============================================================================
--- 
cxf/trunk/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/model/URITemplateTest.java
 (original)
+++ 
cxf/trunk/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/model/URITemplateTest.java
 Wed Dec  3 10:04:20 2008
@@ -35,8 +35,7 @@
 
     @Test
     public void testMatchBasic() throws Exception {
-        URITemplate uriTemplate = new URITemplate("/customers/{id}",
-                                                  false);
+        URITemplate uriTemplate = new URITemplate("/customers/{id}");
         MultivaluedMap<String, String> values = new MetadataMap<String, 
String>();
         
         boolean match = uriTemplate.match("/customers/123/", values);
@@ -47,8 +46,7 @@
     
     @Test
     public void testMatchWithMatrixAndTemplate() throws Exception {
-        URITemplate uriTemplate = new URITemplate("/customers/{id}",
-                                                  false);
+        URITemplate uriTemplate = new URITemplate("/customers/{id}");
         MultivaluedMap<String, String> values = new MetadataMap<String, 
String>();
         
         boolean match = uriTemplate.match("/customers/123;123456/", values);
@@ -59,8 +57,7 @@
     
     @Test
     public void testMatchWithMatrixOnClearPath1() throws Exception {
-        URITemplate uriTemplate = new URITemplate("/customers/{id}",
-                                                  false);
+        URITemplate uriTemplate = new URITemplate("/customers/{id}");
         MultivaluedMap<String, String> values = new MetadataMap<String, 
String>();
         
         boolean match = uriTemplate.match("/customers;123456/123/", values);
@@ -71,8 +68,7 @@
     
     @Test
     public void testMatchWithMatrixOnClearPath2() throws Exception {
-        URITemplate uriTemplate = new 
URITemplate("/customers/{id}/orders/{order}",
-                                                  false);
+        URITemplate uriTemplate = new 
URITemplate("/customers/{id}/orders/{order}");
         MultivaluedMap<String, String> values = new MetadataMap<String, 
String>();
         
         assertTrue(uriTemplate.match("/customers;123456/123/orders;456/3", 
values));
@@ -82,8 +78,7 @@
     
     @Test
     public void testMatchWithMatrixOnClearPath3() throws Exception {
-        URITemplate uriTemplate = new URITemplate("/{id}/customers/",
-                                                  false);
+        URITemplate uriTemplate = new URITemplate("/{id}/customers/");
         MultivaluedMap<String, String> values = new MetadataMap<String, 
String>();
         
         boolean match = uriTemplate.match("/123/customers;123456/", values);
@@ -94,8 +89,7 @@
     
     @Test
     public void testMatchBasicTwoParametersVariation1() throws Exception {
-        URITemplate uriTemplate = new 
URITemplate("/customers/{name}/{department}",
-                                                  false);
+        URITemplate uriTemplate = new 
URITemplate("/customers/{name}/{department}");
         MultivaluedMap<String, String> values = new MetadataMap<String, 
String>();
         
         boolean match = uriTemplate.match("/customers/john/CS", values);
@@ -108,8 +102,7 @@
     
     @Test
     public void testMatchBasicTwoParametersVariation2() throws Exception {
-        URITemplate uriTemplate = new 
URITemplate("/customers/name/{name}/dep/{department}",
-                                                  false);
+        URITemplate uriTemplate = new 
URITemplate("/customers/name/{name}/dep/{department}");
         MultivaluedMap<String, String> values = new MetadataMap<String, 
String>();
         
         boolean match = uriTemplate.match("/customers/name/john/dep/CS", 
values);
@@ -123,7 +116,7 @@
     @Test
     public void testURITemplateWithSubResource() throws Exception {
         //So "/customers" is the URITemplate for the root resource class
-        URITemplate uriTemplate = new URITemplate("/customers", true);
+        URITemplate uriTemplate = new URITemplate("/customers");
         MultivaluedMap<String, String> values = new MetadataMap<String, 
String>();
         
         boolean match = uriTemplate.match("/customers/123", values);
@@ -135,7 +128,7 @@
     @Test
     public void testURITemplateWithSubResourceVariation2() throws Exception {
         //So "/customers" is the URITemplate for the root resource class
-        URITemplate uriTemplate = new URITemplate("/customers", true);
+        URITemplate uriTemplate = new URITemplate("/customers");
         MultivaluedMap<String, String> values = new MetadataMap<String, 
String>();
         
         boolean match = uriTemplate.match("/customers/name/john/dep/CS", 
values);
@@ -150,7 +143,7 @@
      * public Book getBook(@UriParam("bookId") String id)
      */
     public void testURITemplateWithSubResourceVariation3() throws Exception {
-        URITemplate uriTemplate = new URITemplate("/books/{bookId}/", true);
+        URITemplate uriTemplate = new URITemplate("/books/{bookId}/");
         MultivaluedMap<String, String> values = new MetadataMap<String, 
String>();
         
         boolean match = uriTemplate.match("/books/123/chapter/1", values);
@@ -158,4 +151,119 @@
         String subResourcePath = 
values.getFirst(URITemplate.FINAL_MATCH_GROUP);
         assertEquals("/chapter/1", subResourcePath);
     }
+    
+    @Test
+    public void testBasicCustomExpression() throws Exception {
+        URITemplate uriTemplate = new URITemplate("/books/{bookId:[^/]+?}");
+        MultivaluedMap<String, String> values = new MetadataMap<String, 
String>();
+        
+        boolean match = uriTemplate.match("/books/123/chapter/1", values);
+        assertTrue(match);
+        assertEquals("123", values.getFirst("bookId"));
+        String subResourcePath = 
values.getFirst(URITemplate.FINAL_MATCH_GROUP);
+        assertEquals("/chapter/1", subResourcePath);
+    }
+    
+    @Test
+    public void testBasicCustomExpression2() throws Exception {
+        URITemplate uriTemplate = new URITemplate("/books/{bookId:123}");
+        MultivaluedMap<String, String> values = new MetadataMap<String, 
String>();
+        
+        boolean match = uriTemplate.match("/books/123/chapter/1", values);
+        assertTrue(match);
+        assertEquals("123", values.getFirst("bookId"));
+        String subResourcePath = 
values.getFirst(URITemplate.FINAL_MATCH_GROUP);
+        assertEquals("/chapter/1", subResourcePath);
+    }
+    
+    @Test
+    public void testBasicCustomExpression3() throws Exception {
+        URITemplate uriTemplate = new URITemplate("/books/{bookId:\\d\\d\\d}");
+        MultivaluedMap<String, String> values = new MetadataMap<String, 
String>();
+        
+        boolean match = uriTemplate.match("/books/123/chapter/1", values);
+        assertTrue(match);
+        assertEquals("123", values.getFirst("bookId"));
+        String subResourcePath = 
values.getFirst(URITemplate.FINAL_MATCH_GROUP);
+        assertEquals("/chapter/1", subResourcePath);
+    }
+    
+    @Test
+    public void testEscaping() throws Exception {
+        URITemplate uriTemplate = new URITemplate("/books/a.db");
+        MultivaluedMap<String, String> values = new MetadataMap<String, 
String>();
+        
+        assertTrue(uriTemplate.match("/books/a.db", values));
+        assertFalse(uriTemplate.match("/books/adbc", values));
+        assertFalse(uriTemplate.match("/books/acdb", values));
+
+    }
+    
+    @Test
+    public void testBasicCustomExpression4() throws Exception {
+        URITemplate uriTemplate = new URITemplate("/books/{bookId:...\\.}");
+        MultivaluedMap<String, String> values = new MetadataMap<String, 
String>();
+        
+        assertTrue(uriTemplate.match("/books/123.", values));
+        assertEquals("123.", values.getFirst("bookId"));
+        values.clear();
+        assertTrue(uriTemplate.match("/books/abc.", values));
+        assertEquals("abc.", values.getFirst("bookId"));
+        assertFalse(uriTemplate.match("/books/abcd", values));
+        assertFalse(uriTemplate.match("/books/abc", values));
+    }
+    
+    @Test
+    public void testMultipleExpression2() throws Exception {
+        URITemplate uriTemplate = new 
URITemplate("/books/{bookId:123}/chapter/{id}");
+        MultivaluedMap<String, String> values = new MetadataMap<String, 
String>();
+        
+        boolean match = uriTemplate.match("/books/123/chapter/1", values);
+        assertTrue(match);
+        assertEquals("123", values.getFirst("bookId"));
+        assertEquals("1", values.getFirst("id"));
+        String subResourcePath = 
values.getFirst(URITemplate.FINAL_MATCH_GROUP);
+        assertEquals("/", subResourcePath);
+    }
+    
+    @Test
+    public void testFailCustomExpression() throws Exception {
+        URITemplate uriTemplate = new URITemplate("/books/{bookId:124}");
+        MultivaluedMap<String, String> values = new MetadataMap<String, 
String>();
+        
+        boolean match = uriTemplate.match("/books/123/chapter/1", values);
+        assertFalse(match);
+    }
+    
+    @Test
+    public void testBaseTail1() {
+        URITemplate uriTemplate = new URITemplate("/{base:base.+}/{tail}");
+        MultivaluedMap<String, String> values = new MetadataMap<String, 
String>();
+        assertFalse(uriTemplate.match("/base/tails", values));
+        assertTrue(uriTemplate.match("/base1/tails", values));
+        assertEquals("base1", values.getFirst("base"));
+        assertEquals("tails", values.getFirst("tail"));
+    }
+    
+    @Test
+    public void testBaseTail2() {
+        URITemplate uriTemplate = new URITemplate("/{base:.+base}/{tail}");
+        MultivaluedMap<String, String> values = new MetadataMap<String, 
String>();
+        assertFalse(uriTemplate.match("/base/tails", values));
+        assertFalse(uriTemplate.match("/base1/tails", values));
+        assertTrue(uriTemplate.match("/1base/tails", values));
+        assertEquals("1base", values.getFirst("base"));
+        assertEquals("tails", values.getFirst("tail"));
+    }
+    
+    @Test
+    public void testBaseTail3() {
+        URITemplate uriTemplate = new 
URITemplate("/{base:base.+suffix}/{tail}");
+        MultivaluedMap<String, String> values = new MetadataMap<String, 
String>();
+        assertFalse(uriTemplate.match("/base/tails", values));
+        assertFalse(uriTemplate.match("/base1/tails", values));
+        assertTrue(uriTemplate.match("/base1suffix/tails", values));
+        assertEquals("base1suffix", values.getFirst("base"));
+        assertEquals("tails", values.getFirst("tail"));
+    }
 }

Modified: 
cxf/trunk/systests/src/test/java/org/apache/cxf/systest/jaxrs/BookStore.java
URL: 
http://svn.apache.org/viewvc/cxf/trunk/systests/src/test/java/org/apache/cxf/systest/jaxrs/BookStore.java?rev=722988&r1=722987&r2=722988&view=diff
==============================================================================
--- 
cxf/trunk/systests/src/test/java/org/apache/cxf/systest/jaxrs/BookStore.java 
(original)
+++ 
cxf/trunk/systests/src/test/java/org/apache/cxf/systest/jaxrs/BookStore.java 
Wed Dec  3 10:04:20 2008
@@ -51,7 +51,7 @@
 
 import org.apache.cxf.helpers.XMLUtils;
 
[EMAIL PROTECTED]("/bookstore/")
[EMAIL PROTECTED]("/bookstore")
 public class BookStore {
 
     private Map<Long, Book> books = new HashMap<Long, Book>();
@@ -139,6 +139,12 @@
     }
     
     @GET
+    @Path("books/custom/{bookId:\\d\\d\\d}")
+    public Book getBookCustom(@PathParam("bookId") String id) throws 
BookNotFoundFault {
+        return doGetBook(id);
+    }
+    
+    @GET
     @Path("/books/query")
     public Book getBookQuery(@QueryParam("bookId") long id) throws 
BookNotFoundFault {
         return doGetBook(Long.toString(id));

Modified: 
cxf/trunk/systests/src/test/java/org/apache/cxf/systest/jaxrs/JAXRSClientServerBookTest.java
URL: 
http://svn.apache.org/viewvc/cxf/trunk/systests/src/test/java/org/apache/cxf/systest/jaxrs/JAXRSClientServerBookTest.java?rev=722988&r1=722987&r2=722988&view=diff
==============================================================================
--- 
cxf/trunk/systests/src/test/java/org/apache/cxf/systest/jaxrs/JAXRSClientServerBookTest.java
 (original)
+++ 
cxf/trunk/systests/src/test/java/org/apache/cxf/systest/jaxrs/JAXRSClientServerBookTest.java
 Wed Dec  3 10:04:20 2008
@@ -184,6 +184,13 @@
     }
     
     @Test
+    public void testGetBookCustomExpression() throws Exception {
+        
getAndCompareAsStrings("http://localhost:9080/bookstore/books/custom/123";,
+                               "resources/expected_get_book123.txt",
+                               "application/xml", 200);
+    }
+    
+    @Test
     public void testGetBook123() throws Exception {
         getAndCompareAsStrings("http://localhost:9080/bookstore/books/123";,
                                "resources/expected_get_book123.txt",


Reply via email to