Closed
Description
Affects: 4.1.2 and up
I'm trying to use my own matching functions with spring-expression, but I came across a possible bug.
Here's some code for context:
import org.springframework.expression.spel.support.StandardEvaluationContext;
public class RuleEvaluationContext extends StandardEvaluationContext {
public RuleEvaluationContext(Object rootObject) {
super(rootObject);
registerFunctions();
}
public RuleEvaluationContext() {
super();
registerFunctions();
}
private void registerFunctions() {
for (Functions fun : Functions.values()) {
this.registerFunction(fun.funcName(), fun.method());
}
}
}
enum Functions {
/* Functions commented for the sake of simplicity
CONTAINS(Contains.FUNC_NAME,
getDeclaredMethod(Contains.class, Contains.FUNC_NAME, String.class, String[].class)),
STARTS_WITH(StartsWith.FUNC_NAME,
getDeclaredMethod(StartsWith.class, StartsWith.FUNC_NAME, String.class, String[].class)),
*/
MATCHES(Matches.FUNC_NAME,
getDeclaredMethod(Matches.class, Matches.FUNC_NAME, String.class, String[].class)),
/*
EQUALS(Equals.FUNC_NAME,
getDeclaredMethod(Equals.class, Equals.FUNC_NAME, String.class, String.class)),
GREATER(GreaterThan.FUNC_NAME,
getDeclaredMethod(GreaterThan.class, GreaterThan.FUNC_NAME, String.class, String.class)),
LESS_THAN(LessThan.FUNC_NAME,
getDeclaredMethod(LessThan.class, LessThan.FUNC_NAME, String.class, String.class));
*/
private final String funcName;
private final Method method;
Functions(String funcName, Method method) {
this.funcName = funcName;
this.method = method;
}
public String funcName() {
return funcName;
}
public Method method() {
return method;
}
private static Method getDeclaredMethod(Class c, String name, Class<?>... parameterTypes) {
try {
return c.getDeclaredMethod(name, parameterTypes);
} catch (NoSuchMethodException e) {
throw new FunctionsException(e);
}
}
}
This is one of the registered functions:
public class Matches {
public static final String FUNC_NAME = "matches";
private Matches() {
super();
}
public static boolean matches(String text, String... regexps) {
if (text != null) {
for (String regex : regexps) {
Pattern pattern = Pattern.compile(regex, Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE);
Matcher matcher = pattern.matcher(text);
if (matcher.find()) {
return true;
}
}
}
return false;
}
}
@Test
public void test() {
final RuleEvaluationContext context = new RuleEvaluationContext(new FakeObject());
String originalRule = "#matches(prop, 'xyz,xyz')";
Expression expression = parser.parseExpression(originalRule);
Boolean result = expression.getValue(context, Boolean.class);
assertThat(result, is(false));
}
private final ExpressionParser parser = new SpelExpressionParser();
private static class FakeObject {
private String prop = "xyz";
public String getProp() {
return prop;
}
}
This test fails when it should pass. Upon further inspection I've found that the problem is that spring-expression breaks the 'xyz,xyz' into ["xyz", "xyz"] as can be seen below.
However I've also found that using old versions of the library (pre 4.1.2), this doesn't happen.
As does putting more strings inside the argument:
@Test
public void test() {
final RuleEvaluationContext context = new RuleEvaluationContext(new FakeObject());
String originalRule = "#matches(prop, 'abc', 'xyz,xyz')";
Expression expression = parser.parseExpression(originalRule);
Boolean result = expression.getValue(context, Boolean.class);
assertThat(result, is(false));
}