Return-Path: Delivered-To: apmail-cxf-commits-archive@www.apache.org Received: (qmail 63455 invoked from network); 3 Dec 2008 18:05:13 -0000 Received: from hermes.apache.org (HELO mail.apache.org) (140.211.11.2) by minotaur.apache.org with SMTP; 3 Dec 2008 18:05:13 -0000 Received: (qmail 46370 invoked by uid 500); 3 Dec 2008 18:05:25 -0000 Delivered-To: apmail-cxf-commits-archive@cxf.apache.org Received: (qmail 46316 invoked by uid 500); 3 Dec 2008 18:05:25 -0000 Mailing-List: contact commits-help@cxf.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@cxf.apache.org Delivered-To: mailing list commits@cxf.apache.org Received: (qmail 46306 invoked by uid 99); 3 Dec 2008 18:05:25 -0000 Received: from athena.apache.org (HELO athena.apache.org) (140.211.11.136) by apache.org (qpsmtpd/0.29) with ESMTP; Wed, 03 Dec 2008 10:05:25 -0800 X-ASF-Spam-Status: No, hits=-1998.5 required=10.0 tests=ALL_TRUSTED,WEIRD_PORT X-Spam-Check-By: apache.org Received: from [140.211.11.4] (HELO eris.apache.org) (140.211.11.4) by apache.org (qpsmtpd/0.29) with ESMTP; Wed, 03 Dec 2008 18:04:04 +0000 Received: by eris.apache.org (Postfix, from userid 65534) id 25F3023888A6; Wed, 3 Dec 2008 10:04:22 -0800 (PST) Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: svn commit: r722988 - in /cxf/trunk: rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/model/ rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/impl/ rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/model/ systests/src/test/java/org/apache/cx... Date: Wed, 03 Dec 2008 18:04:21 -0000 To: commits@cxf.apache.org From: sergeyb@apache.org X-Mailer: svnmailer-1.0.8 Message-Id: <20081203180422.25F3023888A6@eris.apache.org> X-Virus-Checked: Checked by ClamAV on apache.org 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 templateVariables; + private final List templateVariables = new ArrayList(); + private final List customTemplateVariables = new ArrayList(); 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 names = new ArrayList(); - + 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 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 values = new MetadataMap(); 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 values = new MetadataMap(); 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 values = new MetadataMap(); 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 values = new MetadataMap(); 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 values = new MetadataMap(); 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 values = new MetadataMap(); 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 values = new MetadataMap(); 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 values = new MetadataMap(); 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 values = new MetadataMap(); 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 values = new MetadataMap(); 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 values = new MetadataMap(); + + 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 values = new MetadataMap(); + + 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 values = new MetadataMap(); + + 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 values = new MetadataMap(); + + 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 values = new MetadataMap(); + + 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 values = new MetadataMap(); + + 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 values = new MetadataMap(); + + boolean match = uriTemplate.match("/books/123/chapter/1", values); + assertFalse(match); + } + + @Test + public void testBaseTail1() { + URITemplate uriTemplate = new URITemplate("/{base:base.+}/{tail}"); + MultivaluedMap values = new MetadataMap(); + 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 values = new MetadataMap(); + 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 values = new MetadataMap(); + 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; -@Path("/bookstore/") +@Path("/bookstore") public class BookStore { private Map books = new HashMap(); @@ -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",