From 077fc1613def92ad3dd6919af2e80f3163a116f3 Mon Sep 17 00:00:00 2001 From: Andy Clement Date: Sun, 17 Aug 2008 01:42:06 +0000 Subject: [PATCH] starting to sort out like/matches. Matches is the java regex related one. Like is the SQL related one. But the spring .net 'like' doesn't seem to be the same as the SQL doc I found for like... --- org.springframework.expression/readme.txt | 4 +++- .../expression/spel/SpelMessages.java | 4 +++- .../expression/spel/ast/OperatorLike.java | 19 ++++++++++++------- .../expression/spel/ast/OperatorMatches.java | 4 ++-- .../expression/spel/EvaluationTests.java | 16 ++++++++++------ 5 files changed, 30 insertions(+), 17 deletions(-) diff --git a/org.springframework.expression/readme.txt b/org.springframework.expression/readme.txt index 681076af7d0..814045c3f09 100644 --- a/org.springframework.expression/readme.txt +++ b/org.springframework.expression/readme.txt @@ -23,4 +23,6 @@ Syntax So 'l'.charAt(0) was required - wonder if we can build in a converter for a single length string to char? Can't do that as equals take Object and so we don't know to do a cast in order to pass a char into equals We certainly cannot do a cast (unless casts are added to the syntax). See MethodInvocationTest.testStringClass() - +- MATCHES is now the thing that takes a java regex. What does 'like' do? right now it is the SQL LIKE that supports + wildcards % and _. It has a poor implementation but I need to know whether to keep it in the language before + fixing that. diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/spel/SpelMessages.java b/org.springframework.expression/src/main/java/org/springframework/expression/spel/SpelMessages.java index 101a814b4f8..748b968297f 100644 --- a/org.springframework.expression/src/main/java/org/springframework/expression/spel/SpelMessages.java +++ b/org.springframework.expression/src/main/java/org/springframework/expression/spel/SpelMessages.java @@ -106,7 +106,9 @@ public enum SpelMessages { Kind.ERROR, 1063, "A problem occurred whilst attempting to set the property ''{0}'': ''{1}''"), NOT_AN_INTEGER( Kind.ERROR, 1064, "The value ''{0}'' cannot be parsed as an int"), NOT_A_LONG(Kind.ERROR, 1065, "The value ''{0}'' cannot be parsed as a long"), PARSE_PROBLEM(Kind.ERROR, 1066, - "Error occurred during expression parse: {0}"), ; + "Error occurred during expression parse: {0}"), INVALID_FIRST_OPERAND_FOR_MATCHES_OPERATOR(Kind.ERROR, + 1067, "First operand to matches operator must be a string. ''{0}'' is not"), INVALID_SECOND_OPERAND_FOR_MATCHES_OPERATOR( + Kind.ERROR, 1068, "Second operand to matches operator must be a string. ''{0}'' is not"); private Kind kind; private int code; diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/OperatorLike.java b/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/OperatorLike.java index e30f0010cac..489eeb15d7f 100644 --- a/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/OperatorLike.java +++ b/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/OperatorLike.java @@ -21,13 +21,14 @@ import java.util.regex.PatternSyntaxException; import org.antlr.runtime.Token; import org.springframework.expression.EvaluationException; +import org.springframework.expression.spel.ExpressionState; import org.springframework.expression.spel.SpelException; import org.springframework.expression.spel.SpelMessages; -import org.springframework.expression.spel.ExpressionState; /** - * Implements the like operator. The expected operands for like are a string and a pattern (JDK regex). The operator - * will return true if the string matches the regex. + * Implements the like operator. The like operator behaves the same as the SQL LIKE operator. The first operand is + * compared against the expression supplied as the second operand. This expression supports two wildcards: % meaning any + * string of any length, and _ meaning any single character. * * @author Andy Clement */ @@ -43,10 +44,10 @@ public class OperatorLike extends Operator { } @Override - public Object getValue(ExpressionState state) throws EvaluationException { + public Boolean getValue(ExpressionState state) throws EvaluationException { SpelNode leftOp = getLeftOperand(); SpelNode rightOp = getRightOperand(); - Object left = leftOp.getValue(state); + Object left = leftOp.getValue(state, String.class); Object right = getRightOperand().getValue(state); try { if (!(left instanceof String)) { @@ -57,12 +58,16 @@ public class OperatorLike extends Operator { throw new SpelException(rightOp.getCharPositionInLine(), SpelMessages.INVALID_SECOND_OPERAND_FOR_LIKE_OPERATOR, right); } - Pattern pattern = Pattern.compile((String) right); + // Translate that pattern to a java regex + // not really the best option, what if the right operand already had regex related chars in it? + String likePattern = (String) right; + likePattern = likePattern.replace('_', '.'); + likePattern = likePattern.replaceAll("%", ".*"); + Pattern pattern = Pattern.compile(likePattern); Matcher matcher = pattern.matcher((String) left); return matcher.matches(); } catch (PatternSyntaxException pse) { throw new SpelException(rightOp.getCharPositionInLine(), pse, SpelMessages.INVALID_PATTERN, right); } } - } diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/OperatorMatches.java b/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/OperatorMatches.java index 297a436fcc9..6445569b16b 100644 --- a/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/OperatorMatches.java +++ b/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/OperatorMatches.java @@ -58,11 +58,11 @@ public class OperatorMatches extends Operator { try { if (!(left instanceof String)) { throw new SpelException(leftOp.getCharPositionInLine(), - SpelMessages.INVALID_FIRST_OPERAND_FOR_LIKE_OPERATOR, left); + SpelMessages.INVALID_FIRST_OPERAND_FOR_MATCHES_OPERATOR, left); } if (!(right instanceof String)) { throw new SpelException(rightOp.getCharPositionInLine(), - SpelMessages.INVALID_SECOND_OPERAND_FOR_LIKE_OPERATOR, right); + SpelMessages.INVALID_SECOND_OPERAND_FOR_MATCHES_OPERATOR, right); } Pattern pattern = Pattern.compile((String) right); Matcher matcher = pattern.matcher((String) left); diff --git a/org.springframework.expression/src/test/java/org/springframework/expression/spel/EvaluationTests.java b/org.springframework.expression/src/test/java/org/springframework/expression/spel/EvaluationTests.java index fa3ad1c6658..d6c042e8127 100644 --- a/org.springframework.expression/src/test/java/org/springframework/expression/spel/EvaluationTests.java +++ b/org.springframework.expression/src/test/java/org/springframework/expression/spel/EvaluationTests.java @@ -58,21 +58,25 @@ public class EvaluationTests extends ExpressionTestCase { } public void testRelOperatorsLike01() { - evaluate("'Abc' like '[A-Z]b.*'", "true", Boolean.class); - } // not the same as CSharp thing which matched '[A-Z]b*' + evaluate("'Abc' like 'A%_'", "true", Boolean.class); + } public void testRelOperatorsLike02() { evaluate("'Abc' like '..'", "false", Boolean.class); - } // was '?' + } public void testRelOperatorsLike03() { - evaluateAndCheckError("7 like '.'", SpelMessages.INVALID_FIRST_OPERAND_FOR_LIKE_OPERATOR); + evaluateAndCheckError("null like '.'", SpelMessages.INVALID_FIRST_OPERAND_FOR_LIKE_OPERATOR); } public void testRelOperatorsLike04() { evaluateAndCheckError("'abc' like 2.0", SpelMessages.INVALID_SECOND_OPERAND_FOR_LIKE_OPERATOR); } + public void testRelOperatorsLike05() { + evaluate("27 like '__'", "true", Boolean.class); // conversion int>string + } + public void testRelOperatorsBetween01() { evaluate("1 between {1, 5}", "true", Boolean.class); } @@ -122,11 +126,11 @@ public class EvaluationTests extends ExpressionTestCase { } public void testRelOperatorsMatches03() { - evaluateAndCheckError("null matches '^.*$'", SpelMessages.INVALID_FIRST_OPERAND_FOR_LIKE_OPERATOR, 0, null); + evaluateAndCheckError("null matches '^.*$'", SpelMessages.INVALID_FIRST_OPERAND_FOR_MATCHES_OPERATOR, 0, null); } public void testRelOperatorsMatches04() { - evaluateAndCheckError("'abc' matches null", SpelMessages.INVALID_SECOND_OPERAND_FOR_LIKE_OPERATOR, 14, null); + evaluateAndCheckError("'abc' matches null", SpelMessages.INVALID_SECOND_OPERAND_FOR_MATCHES_OPERATOR, 14, null); } public void testRelOperatorsMatches05() {