WW-3998 Extracts interface and adds default implementation base on existing code

git-svn-id: https://svn.apache.org/repos/asf/struts/struts2/trunk@1456961 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Lukasz Lenart
2013-03-15 14:02:12 +00:00
parent da6fa05ed9
commit 34af1cd8a2
12 changed files with 141 additions and 89 deletions
@@ -247,4 +247,6 @@ public final class StrutsConstants {
public static final String STRUTS_CONVERTER_CREATOR = "struts.converter.creator";
public static final String STRUTS_CONVERTER_HOLDER = "struts..converter.holder";
public static final String STRUTS_EXPRESSION_PARSER = "struts.expression.parser";
}
@@ -49,10 +49,7 @@ import com.opensymphony.xwork2.inject.ContainerBuilder;
import com.opensymphony.xwork2.inject.Context;
import com.opensymphony.xwork2.inject.Factory;
import com.opensymphony.xwork2.inject.Scope;
import com.opensymphony.xwork2.util.ClassLoaderUtil;
import com.opensymphony.xwork2.util.LocalizedTextUtil;
import com.opensymphony.xwork2.util.PatternMatcher;
import com.opensymphony.xwork2.util.ValueStackFactory;
import com.opensymphony.xwork2.util.*;
import com.opensymphony.xwork2.util.location.LocatableProperties;
import com.opensymphony.xwork2.util.logging.Logger;
import com.opensymphony.xwork2.util.logging.LoggerFactory;
@@ -276,6 +273,13 @@ import java.util.StringTokenizer;
* <td>singleton</td>
* <td>Holds user converters' instances</td>
* </tr>
* <tr>
* <td>com.opensymphony.xwork2.util.TextParser</td>
* <td>struts.expression.parser</td>
* <td>singleton</td>
* <td>Used to parse expressions like ${foo.bar} or %{bar.foo} but it is up tp the TextParser's
* implementation what kind of opening char to use (#, $, %, etc)</td>
* </tr>
* </table>
*
* <!-- END SNIPPET: extensionPoints -->
@@ -355,6 +359,8 @@ public class BeanSelectionProvider implements ConfigurationProvider {
alias(UnknownHandlerManager.class, StrutsConstants.STRUTS_UNKNOWN_HANDLER_MANAGER, builder, props);
alias(UrlHelper.class, StrutsConstants.STRUTS_URL_HELPER, builder, props);
alias(TextParser.class, StrutsConstants.STRUTS_EXPRESSION_PARSER, builder, props);
if ("true".equalsIgnoreCase(props.getProperty(StrutsConstants.STRUTS_DEVMODE))) {
props.setProperty(StrutsConstants.STRUTS_I18N_RELOAD, "true");
props.setProperty(StrutsConstants.STRUTS_CONFIGURATION_XML_RELOAD, "true");
@@ -197,13 +197,13 @@ public abstract class StrutsResultSupport implements Result, StrutsStatics {
if (parse && param != null && invocation != null) {
return TextParseUtil.translateVariables(param, invocation.getStack(),
new TextParseUtil.ParsedValueEvaluator() {
public Object evaluate(Object parsedValue) {
public Object evaluate(String parsedValue) {
if (encode) {
if (parsedValue != null) {
try {
// use UTF-8 as this is the recommended encoding by W3C to
// avoid incompatibilities.
return URLEncoder.encode(parsedValue.toString(), "UTF-8");
return URLEncoder.encode(parsedValue, "UTF-8");
}
catch(UnsupportedEncodingException e) {
if (LOG.isWarnEnabled()) {
@@ -100,6 +100,8 @@
<bean class="com.opensymphony.xwork2.ognl.OgnlUtil" />
<bean type="com.opensymphony.xwork2.util.TextParser" name="struts" class="com.opensymphony.xwork2.util.OgnlTextParser" scope="singleton"/>
<bean type="ognl.PropertyAccessor" name="com.opensymphony.xwork2.util.CompoundRoot" class="com.opensymphony.xwork2.ognl.accessor.CompoundRootAccessor" />
<bean type="ognl.PropertyAccessor" name="java.lang.Object" class="com.opensymphony.xwork2.ognl.accessor.ObjectAccessor" />
<bean type="ognl.PropertyAccessor" name="java.util.Iterator" class="com.opensymphony.xwork2.ognl.accessor.XWorkIteratorPropertyAccessor" />
@@ -25,6 +25,8 @@ import com.opensymphony.xwork2.FileManager;
import com.opensymphony.xwork2.FileManagerFactory;
import com.opensymphony.xwork2.conversion.impl.XWorkConverter;
import com.opensymphony.xwork2.inject.Container;
import com.opensymphony.xwork2.util.TextParser;
import com.opensymphony.xwork2.util.OgnlTextParser;
import com.opensymphony.xwork2.util.ValueStack;
import com.opensymphony.xwork2.util.finder.ClassLoaderInterface;
import com.opensymphony.xwork2.util.finder.ClassLoaderInterfaceDelegate;
@@ -275,6 +277,8 @@ public class EmbeddedJSPResultTest extends TestCase {
//mock container
Container container = EasyMock.createNiceMock(Container.class);
EasyMock.expect(container.getInstance(XWorkConverter.class)).andReturn(converter).anyTimes();
TextParser parser = new OgnlTextParser();
EasyMock.expect(container.getInstance(TextParser.class)).andReturn(parser).anyTimes();
EasyMock.expect(container.getInstanceNames(FileManager.class)).andReturn(new HashSet<String>()).anyTimes();
EasyMock.expect(container.getInstance(FileManager.class)).andReturn(fileManager).anyTimes();
@@ -24,10 +24,12 @@ package org.apache.struts2.views.java.simple;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.conversion.impl.XWorkConverter;
import com.opensymphony.xwork2.inject.Container;
import com.opensymphony.xwork2.util.TextParser;
import com.opensymphony.xwork2.util.OgnlTextParser;
import com.opensymphony.xwork2.util.ValueStack;
import junit.framework.TestCase;
import org.apache.struts2.StrutsConstants;
import org.apache.struts2.ServletActionContext;
import org.apache.struts2.StrutsConstants;
import org.apache.struts2.components.Component;
import org.apache.struts2.components.UIBean;
import org.apache.struts2.components.template.Template;
@@ -107,6 +109,8 @@ public abstract class AbstractTest extends TestCase {
XWorkConverter converter = new ConverterEx();
EasyMock.expect(container.getInstance(String.class, StrutsConstants.STRUTS_TAG_ALTSYNTAX)).andReturn("true").anyTimes();
EasyMock.expect(container.getInstance(XWorkConverter.class)).andReturn(converter).anyTimes();
TextParser parser = new OgnlTextParser();
EasyMock.expect(container.getInstance(TextParser.class)).andReturn(parser).anyTimes();
stackContext.put(ActionContext.CONTAINER, container);
@@ -52,10 +52,7 @@ import com.opensymphony.xwork2.ognl.accessor.XWorkIteratorPropertyAccessor;
import com.opensymphony.xwork2.ognl.accessor.XWorkListPropertyAccessor;
import com.opensymphony.xwork2.ognl.accessor.XWorkMapPropertyAccessor;
import com.opensymphony.xwork2.ognl.accessor.XWorkMethodAccessor;
import com.opensymphony.xwork2.util.CompoundRoot;
import com.opensymphony.xwork2.util.PatternMatcher;
import com.opensymphony.xwork2.util.ValueStackFactory;
import com.opensymphony.xwork2.util.WildcardHelper;
import com.opensymphony.xwork2.util.*;
import com.opensymphony.xwork2.util.fs.DefaultFileManager;
import com.opensymphony.xwork2.util.fs.DefaultFileManagerFactory;
import com.opensymphony.xwork2.util.location.LocatableProperties;
@@ -137,6 +134,8 @@ public class XWorkConfigurationProvider implements ConfigurationProvider {
.factory(MethodAccessor.class, Object.class.getName(), XWorkMethodAccessor.class, Scope.SINGLETON)
.factory(MethodAccessor.class, CompoundRoot.class.getName(), CompoundRootAccessor.class, Scope.SINGLETON)
.factory(TextParser.class, OgnlTextParser.class, Scope.SINGLETON)
.factory(NullHandler.class, Object.class.getName(), InstantiatingNullHandler.class, Scope.SINGLETON)
.factory(ActionValidatorManager.class, AnnotationActionValidatorManager.class, Scope.SINGLETON)
.factory(ActionValidatorManager.class, "no-annotations", DefaultActionValidatorManager.class, Scope.SINGLETON)
@@ -332,7 +332,7 @@ public class ParametersInterceptor extends MethodFilterInterceptor {
String developerNotification = LocalizedTextUtil.findText(ParametersInterceptor.class, "devmode.notification", ActionContext.getContext().getLocale(), "Developer Notification:\n{0}", new Object[]{
"Unexpected Exception caught setting '" + name + "' on '" + action.getClass() + ": " + e.getMessage()
});
LOG.error(developerNotification);
LOG.error(developerNotification, e);
if (action instanceof ValidationAware) {
((ValidationAware) action).addActionMessage(developerNotification);
}
@@ -0,0 +1,84 @@
package com.opensymphony.xwork2.util;
import org.apache.commons.lang3.StringUtils;
/**
* OGNL implementation of {@link TextParser}
*/
public class OgnlTextParser implements TextParser {
public Object evaluate(char[] openChars, String expression, TextParseUtil.ParsedValueEvaluator evaluator, int maxLoopCount) {
// deal with the "pure" expressions first!
//expression = expression.trim();
Object result = expression;
for (char open : openChars) {
int loopCount = 1;
int pos = 0;
//this creates an implicit StringBuffer and shouldn't be used in the inner loop
final String lookupChars = open + "{";
while (true) {
int start = expression.indexOf(lookupChars, pos);
if (start == -1) {
pos = 0;
loopCount++;
start = expression.indexOf(lookupChars);
}
if (loopCount > maxLoopCount) {
// translateVariables prevent infinite loop / expression recursive evaluation
break;
}
int length = expression.length();
int x = start + 2;
int end;
char c;
int count = 1;
while (start != -1 && x < length && count != 0) {
c = expression.charAt(x++);
if (c == '{') {
count++;
} else if (c == '}') {
count--;
}
}
end = x - 1;
if ((start != -1) && (end != -1) && (count == 0)) {
String var = expression.substring(start + 2, end);
Object o = evaluator.evaluate(var);
String left = expression.substring(0, start);
String right = expression.substring(end + 1);
String middle = null;
if (o != null) {
middle = o.toString();
if (StringUtils.isEmpty(left)) {
result = o;
} else {
result = left.concat(middle);
}
if (StringUtils.isNotEmpty(right)) {
result = result.toString().concat(right);
}
expression = left.concat(middle).concat(right);
} else {
// the variable doesn't exist, so don't display anything
expression = left.concat(right);
result = expression;
}
pos = (left != null && left.length() > 0 ? left.length() - 1: 0) +
(middle != null && middle.length() > 0 ? middle.length() - 1: 0) +
1;
pos = Math.max(pos, 1);
} else {
break;
}
}
}
return result;
}
}
@@ -22,8 +22,6 @@ import com.opensymphony.xwork2.inject.Container;
import java.util.HashSet;
import java.util.Set;
import org.apache.commons.lang3.StringUtils;
/**
* Utility class for text parsing.
@@ -153,84 +151,26 @@ public class TextParseUtil {
* @param evaluator
* @return Converted object from variable translation.
*/
public static Object translateVariables(char[] openChars, String expression, ValueStack stack, Class asType, ParsedValueEvaluator evaluator, int maxLoopCount) {
// deal with the "pure" expressions first!
//expression = expression.trim();
Object result = expression;
for (char open : openChars) {
int loopCount = 1;
int pos = 0;
public static Object translateVariables(char[] openChars, String expression, final ValueStack stack, final Class asType, final ParsedValueEvaluator evaluator, int maxLoopCount) {
//this creates an implicit StringBuffer and shouldn't be used in the inner loop
final String lookupChars = open + "{";
while (true) {
int start = expression.indexOf(lookupChars, pos);
if (start == -1) {
pos = 0;
loopCount++;
start = expression.indexOf(lookupChars);
}
if (loopCount > maxLoopCount) {
// translateVariables prevent infinite loop / expression recursive evaluation
break;
}
int length = expression.length();
int x = start + 2;
int end;
char c;
int count = 1;
while (start != -1 && x < length && count != 0) {
c = expression.charAt(x++);
if (c == '{') {
count++;
} else if (c == '}') {
count--;
}
}
end = x - 1;
if ((start != -1) && (end != -1) && (count == 0)) {
String var = expression.substring(start + 2, end);
Object o = stack.findValue(var, asType);
if (evaluator != null) {
o = evaluator.evaluate(o);
}
String left = expression.substring(0, start);
String right = expression.substring(end + 1);
String middle = null;
if (o != null) {
middle = o.toString();
if (StringUtils.isEmpty(left)) {
result = o;
} else {
result = left.concat(middle);
}
if (StringUtils.isNotEmpty(right)) {
result = result.toString().concat(right);
}
expression = left.concat(middle).concat(right);
} else {
// the variable doesn't exist, so don't display anything
expression = left.concat(right);
result = expression;
}
pos = (left != null && left.length() > 0 ? left.length() - 1: 0) +
(middle != null && middle.length() > 0 ? middle.length() - 1: 0) +
1;
pos = Math.max(pos, 1);
} else {
break;
ParsedValueEvaluator ognlEval = new ParsedValueEvaluator() {
public Object evaluate(String parsedValue) {
Object o = stack.findValue(parsedValue, asType);
if (evaluator != null) {
o = evaluator.evaluate(o.toString());
}
return o;
}
}
};
TextParser parser = ((Container)stack.getContext().get(ActionContext.CONTAINER)).getInstance(TextParser.class);
XWorkConverter conv = ((Container)stack.getContext().get(ActionContext.CONTAINER)).getInstance(XWorkConverter.class);
Object result = parser.evaluate(openChars, expression, ognlEval, maxLoopCount);
return conv.convertValue(stack.getContext(), result, asType);
}
@@ -279,6 +219,6 @@ public class TextParseUtil {
* @param parsedValue - value parsed by ognl value stack
* @return return the evaluted value.
*/
Object evaluate(Object parsedValue);
Object evaluate(String parsedValue);
}
}
@@ -0,0 +1,11 @@
package com.opensymphony.xwork2.util;
/**
* Used to parse expressions like ${foo.bar} or %{bar.foo} but it is up tp the TextParser's
* implementation what kind of opening char to use (#, $, %, etc)
*/
public interface TextParser {
Object evaluate(char[] openChars, String expression, TextParseUtil.ParsedValueEvaluator evaluator, int maxLoopCount);
}
@@ -40,7 +40,7 @@ public class TextParseUtilTest extends XWorkTestCase {
});
TextParseUtil.ParsedValueEvaluator evaluator = new TextParseUtil.ParsedValueEvaluator() {
public Object evaluate(Object parsedValue) {
public Object evaluate(String parsedValue) {
return parsedValue.toString()+"Something";
}
};
@@ -53,7 +53,7 @@ public class TextParseUtilTest extends XWorkTestCase {
public void testTranslateVariables() {
ValueStack stack = ActionContext.getContext().getValueStack();
Object s = TextParseUtil.translateVariables("foo: ${{1, 2, 3}}, bar: ${1}", stack);
Object s = TextParseUtil.translateVariables("foo: ${{1, 2, 3}}, bar: %{1}", stack);
assertEquals("foo: [1, 2, 3], bar: 1", s);
s = TextParseUtil.translateVariables("foo: %{{1, 2, 3}}, bar: %{1}", stack);