mirror of
https://github.com/apache/struts.git
synced 2026-08-11 01:27:14 +00:00
WW-3262 improve wildcard to support regular expressions
bump xwork version git-svn-id: https://svn.apache.org/repos/asf/struts/struts2/trunk@818958 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
+2
-2
@@ -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>-->
|
||||
|
||||
@@ -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>
|
||||
* <action name="/{bio:.+}/test/{name}" class="org.apache.struts2.showcase.UITagExample">
|
||||
* <result>/tags/ui/example.jsp</result>
|
||||
* </action>
|
||||
* </pre>
|
||||
*
|
||||
* For this to work it is important to set the following:
|
||||
* <pre>
|
||||
* <constant name="struts.enable.SlashesInActionNames" value="true"/>
|
||||
* <constant name="struts.mapper.alwaysSelectFullNamespace" value="false"/>
|
||||
* <constant name="struts.patternMatcher" value="regex" />
|
||||
* </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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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" />
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user