You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@struts.apache.org by mu...@apache.org on 2009/09/25 21:08:39 UTC

svn commit: r818958 - in /struts/struts2/trunk/core: ./ src/main/java/org/apache/struts2/util/ src/main/resources/ src/test/java/org/apache/struts2/util/

Author: musachy
Date: Fri Sep 25 19:08:38 2009
New Revision: 818958

URL: http://svn.apache.org/viewvc?rev=818958&view=rev
Log:
WW-3262 improve wildcard to support regular expressions

bump xwork version

Added:
    struts/struts2/trunk/core/src/main/java/org/apache/struts2/util/RegexPatternMatcher.java
    struts/struts2/trunk/core/src/main/java/org/apache/struts2/util/RegexPatternMatcherExpression.java
    struts/struts2/trunk/core/src/test/java/org/apache/struts2/util/RegexPatternMatcherTest.java
Modified:
    struts/struts2/trunk/core/pom.xml
    struts/struts2/trunk/core/src/main/resources/struts-default.xml

Modified: struts/struts2/trunk/core/pom.xml
URL: http://svn.apache.org/viewvc/struts/struts2/trunk/core/pom.xml?rev=818958&r1=818957&r2=818958&view=diff
==============================================================================
--- struts/struts2/trunk/core/pom.xml (original)
+++ struts/struts2/trunk/core/pom.xml Fri Sep 25 19:08:38 2009
@@ -56,7 +56,7 @@
                                 <artifactItem>
                                     <groupId>com.opensymphony</groupId>
                                     <artifactId>xwork-core</artifactId>
-                                    <version>2.1.6</version>
+                                    <version>2.1.7-SNAPSHOT</version>
                                     <classifier>sources</classifier>
                                 </artifactItem>
                             </artifactItems>
@@ -229,7 +229,7 @@
         <dependency>
             <groupId>com.opensymphony</groupId>
             <artifactId>xwork-core</artifactId>
-            <version>2.1.6</version>
+            <version>2.1.7-SNAPSHOT</version>
         </dependency>
 
         <!--<dependency>-->

Added: struts/struts2/trunk/core/src/main/java/org/apache/struts2/util/RegexPatternMatcher.java
URL: http://svn.apache.org/viewvc/struts/struts2/trunk/core/src/main/java/org/apache/struts2/util/RegexPatternMatcher.java?rev=818958&view=auto
==============================================================================
--- struts/struts2/trunk/core/src/main/java/org/apache/struts2/util/RegexPatternMatcher.java (added)
+++ struts/struts2/trunk/core/src/main/java/org/apache/struts2/util/RegexPatternMatcher.java Fri Sep 25 19:08:38 2009
@@ -0,0 +1,113 @@
+/*
+ * $Id: ServletContextAware.java 651946 2008-04-27 13:41:38Z apetrelli $
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.struts2.util;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.apache.commons.lang.xwork.StringUtils;
+
+import com.opensymphony.xwork2.util.PatternMatcher;
+
+/**
+ * Allows regular expressions to be used in action names. The regular expressions
+ * can be in the form {FIELD_NAME} or {FIELD_NAME:REGULAR_EXPRESSION}. For example:
+ * <br/>
+ * <pre>
+ *  &lt;action name="/{bio:.+}/test/{name}" class="org.apache.struts2.showcase.UITagExample"&gt;
+ *       &lt;result>/tags/ui/example.jsp&lt;/result&gt;
+ *  &lt;/action&gt;
+ * </pre>
+ *
+ * For this to work it is important to set the following:
+ * <pre>
+ * &lt;constant name="struts.enable.SlashesInActionNames" value="true"/&gt;
+ * &lt;constant name="struts.mapper.alwaysSelectFullNamespace" value="false"/&gt;
+ * &lt;constant name="struts.patternMatcher" value="regex" /&gt;
+ * </pre>
+ */
+public class RegexPatternMatcher implements PatternMatcher<RegexPatternMatcherExpression> {
+    private static final Pattern PATTERN = Pattern.compile("\\{(.*?)\\}");
+
+    public RegexPatternMatcherExpression compilePattern(String data) {
+        Map<Integer, String> params = new HashMap<Integer, String>();
+
+        Matcher matcher = PATTERN.matcher(data);
+        int count = 0;
+        while (matcher.find()) {
+            String expression = matcher.group(1);
+            //check if it is a regex
+            int index = expression.indexOf(':');
+            if (index > 0) {
+                String paramName = expression.substring(0, index);
+                String regex = StringUtils.substring(expression, index + 1);
+                if (StringUtils.isBlank(regex)) {
+                    throw new IllegalArgumentException("invalid expression [" + expression + "], named parameter regular exression "
+                            + "must be in the format {PARAM_NAME:REGEX}");
+                }
+
+                params.put(++count, paramName);
+
+            } else {
+                params.put(++count, expression);
+            }
+        }
+
+        //generate a new pattern used to match URIs
+        //replace {X:B} by (B)
+        String newPattern = data.replaceAll("(\\{.*?:(.*?)\\})", "($2)");
+
+        //replace {X} by (.*?)
+        newPattern = newPattern.replaceAll("(\\{.*?\\})", "(.*?)");
+        return new RegexPatternMatcherExpression(Pattern.compile(newPattern), params);
+    }
+
+    public boolean isLiteral(String pattern) {
+        return (pattern == null || pattern.indexOf('{') == -1);
+    }
+
+    public boolean match(Map<String, String> map, String data, RegexPatternMatcherExpression expr) {
+        Matcher matcher = expr.getPattern().matcher(data);
+        Map<Integer, String> params = expr.getParams();
+
+        if (matcher.matches()) {
+            map.put("0", data);
+            
+            //extract values and get param names from the expression
+            for (int i = 1; i <= matcher.groupCount(); i++) {
+                String paramName = params.get(i);
+                String value = matcher.group(i);
+                
+                //by name
+                map.put(paramName, value);
+                //by index so the old {1} still works
+                map.put(String.valueOf(i), value);
+            }
+
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+}

Added: struts/struts2/trunk/core/src/main/java/org/apache/struts2/util/RegexPatternMatcherExpression.java
URL: http://svn.apache.org/viewvc/struts/struts2/trunk/core/src/main/java/org/apache/struts2/util/RegexPatternMatcherExpression.java?rev=818958&view=auto
==============================================================================
--- struts/struts2/trunk/core/src/main/java/org/apache/struts2/util/RegexPatternMatcherExpression.java (added)
+++ struts/struts2/trunk/core/src/main/java/org/apache/struts2/util/RegexPatternMatcherExpression.java Fri Sep 25 19:08:38 2009
@@ -0,0 +1,49 @@
+/*
+ * $Id: ServletContextAware.java 651946 2008-04-27 13:41:38Z apetrelli $
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.struts2.util;
+
+import java.util.Map;
+import java.util.regex.Pattern;
+
+/**
+ * Holds a compiled expression to match URLs
+ * @see RegexPatternMatcher
+ */
+public class RegexPatternMatcherExpression {
+    //pattern that matches the whole string
+    private final Pattern pattern;
+    
+    //maps group index (from pattern) to parameter names
+    private final Map<Integer, String> params;
+
+    public RegexPatternMatcherExpression(Pattern pattern, Map<Integer, String> params) {
+        this.pattern = pattern;
+        this.params = params;
+    }
+
+    public Pattern getPattern() {
+        return pattern;
+    }
+
+    public Map<Integer, String> getParams() {
+        return params;
+    }
+}

Modified: struts/struts2/trunk/core/src/main/resources/struts-default.xml
URL: http://svn.apache.org/viewvc/struts/struts2/trunk/core/src/main/resources/struts-default.xml?rev=818958&r1=818957&r2=818958&view=diff
==============================================================================
--- struts/struts2/trunk/core/src/main/resources/struts-default.xml (original)
+++ struts/struts2/trunk/core/src/main/resources/struts-default.xml Fri Sep 25 19:08:38 2009
@@ -38,6 +38,7 @@
 
     <bean type="com.opensymphony.xwork2.util.PatternMatcher" name="struts" class="com.opensymphony.xwork2.util.WildcardHelper" />
     <bean type="com.opensymphony.xwork2.util.PatternMatcher" name="namedVariable" class="com.opensymphony.xwork2.util.NamedVariablePatternMatcher"/>
+    <bean type="com.opensymphony.xwork2.util.PatternMatcher" name="regex" class="org.apache.struts2.util.RegexPatternMatcher"/>
 
     <bean type="org.apache.struts2.dispatcher.mapper.ActionMapper" name="struts" class="org.apache.struts2.dispatcher.mapper.DefaultActionMapper" />
     <bean type="org.apache.struts2.dispatcher.mapper.ActionMapper" name="composite" class="org.apache.struts2.dispatcher.mapper.CompositeActionMapper" />

Added: struts/struts2/trunk/core/src/test/java/org/apache/struts2/util/RegexPatternMatcherTest.java
URL: http://svn.apache.org/viewvc/struts/struts2/trunk/core/src/test/java/org/apache/struts2/util/RegexPatternMatcherTest.java?rev=818958&view=auto
==============================================================================
--- struts/struts2/trunk/core/src/test/java/org/apache/struts2/util/RegexPatternMatcherTest.java (added)
+++ struts/struts2/trunk/core/src/test/java/org/apache/struts2/util/RegexPatternMatcherTest.java Fri Sep 25 19:08:38 2009
@@ -0,0 +1,169 @@
+/*
+ * $Id: ServletContextAware.java 651946 2008-04-27 13:41:38Z apetrelli $
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.struts2.util;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.regex.Pattern;
+
+import junit.framework.TestCase;
+
+
+public class RegexPatternMatcherTest extends TestCase {
+    private RegexPatternMatcher matcher = new RegexPatternMatcher();
+
+    public void testIsLiteral() {
+        assertTrue(matcher.isLiteral(null));
+        assertTrue(matcher.isLiteral(""));
+        assertTrue(matcher.isLiteral("    \t"));
+        assertTrue(matcher.isLiteral("something"));
+
+        assertFalse(matcher.isLiteral("{"));
+    }
+
+    public void testCompile0() {
+        RegexPatternMatcherExpression expr = matcher.compilePattern("/some/{test}");
+        assertNotNull(expr);
+
+        //params
+        Map<Integer, String> params = expr.getParams();
+        assertNotNull(params);
+        assertEquals(1, params.size());
+        assertEquals("test", params.get(1));
+
+        //pattern
+        Pattern pattern = expr.getPattern();
+        assertNotNull(pattern);
+        assertEquals("/some/(.*?)", pattern.pattern());
+    }
+
+    public void testCompile1() {
+        RegexPatternMatcherExpression expr = matcher.compilePattern("/{test}/some/{test1}/");
+        assertNotNull(expr);
+
+        //params
+        Map<Integer, String> params = expr.getParams();
+        assertNotNull(params);
+        assertEquals(2, params.size());
+        assertEquals("test", params.get(1));
+        assertEquals("test1", params.get(2));
+
+        //pattern
+        Pattern pattern = expr.getPattern();
+        assertNotNull(pattern);
+        assertEquals("/(.*?)/some/(.*?)/", pattern.pattern());
+    }
+    
+    public void testCompileNamedParams0() {
+        RegexPatternMatcherExpression expr = matcher.compilePattern("/some/{test:.+}");
+        assertNotNull(expr);
+
+        //params
+        Map<Integer, String> params = expr.getParams();
+        assertNotNull(params);
+        assertEquals(1, params.size());
+        assertEquals("test", params.get(1));
+
+        //pattern
+        Pattern pattern = expr.getPattern();
+        assertNotNull(pattern);
+        assertEquals("/some/(.+)", pattern.pattern());
+    }
+    
+    public void testCompileNamedParams1() {
+        RegexPatternMatcherExpression expr = matcher.compilePattern("/some/{test1:.+}/{test2:.*}");
+        assertNotNull(expr);
+
+        //params
+        Map<Integer, String> params = expr.getParams();
+        assertNotNull(params);
+        assertEquals(2, params.size());
+        assertEquals("test1", params.get(1));
+        assertEquals("test2", params.get(2));
+
+        //pattern
+        Pattern pattern = expr.getPattern();
+        assertNotNull(pattern);
+        assertEquals("/some/(.+)/(.*)", pattern.pattern());
+    }
+    
+    public void testMatch0() {
+        RegexPatternMatcherExpression expr = matcher.compilePattern("/some/{test}");
+        
+        Map<String, String> values = new HashMap<String, String>();
+        
+        assertTrue(matcher.match(values, "/some/val", expr));
+        assertEquals(3, values.size());
+        assertEquals("val", values.get("test"));
+        assertEquals("val", values.get("1"));
+        
+        assertEquals("/some/val", values.get("0"));
+    }
+    
+    public void testMatch1() {
+        RegexPatternMatcherExpression expr = matcher.compilePattern("/some/{test0}/some/{test1}");
+        
+        Map<String, String> values = new HashMap<String, String>();
+        
+        assertTrue(matcher.match(values, "/some/val0/some/val1", expr));
+        assertEquals(5, values.size());
+        assertEquals("val0", values.get("test0"));
+        assertEquals("val1", values.get("test1"));
+        assertEquals("val0", values.get("1"));
+        assertEquals("val1", values.get("2"));
+        
+        assertEquals("/some/val0/some/val1", values.get("0"));
+    }
+    
+    public void testMatch2() {
+        RegexPatternMatcherExpression expr = matcher.compilePattern("/some/{test0}/some/{test1}/.*");
+        
+        Map<String, String> values = new HashMap<String, String>();
+        
+        assertTrue(matcher.match(values, "/some/val0/some/val1/buaaa", expr));
+        assertEquals(5, values.size());
+        assertEquals("val0", values.get("test0"));
+        assertEquals("val1", values.get("test1"));
+        assertEquals("val0", values.get("1"));
+        assertEquals("val1", values.get("2"));
+        
+        assertEquals("/some/val0/some/val1/buaaa", values.get("0"));
+    }
+
+    public void testCompileBad0() {
+        try {
+            matcher.compilePattern("/{test/some");
+            fail("Should have failed");
+        } catch (Exception e) {
+            //ok
+        }
+    }
+    
+    public void testCompileBad1() {
+        try {
+            matcher.compilePattern("/test/{p:}");
+            fail("Should have failed");
+        } catch (Exception e) {
+            //ok
+        }
+    }
+
+}