From 00cecd0dd0a3e2a659a8e3b0ecd138582cc10905 Mon Sep 17 00:00:00 2001 From: Andy Clement Date: Thu, 9 Apr 2009 22:03:38 +0000 Subject: [PATCH] more tests, minor fixes. some findbugs issues addressed. --- .../expression/TypedValue.java | 2 + .../common/TemplateAwareExpressionParser.java | 2 +- .../expression/spel/ExpressionState.java | 31 +- .../expression/spel/ast/NullLiteral.java | 7 +- .../expression/spel/ast/Projection.java | 7 +- .../expression/spel/ast/Selection.java | 10 +- .../ReflectiveConstructorExecutor.java | 3 +- .../support/ReflectivePropertyResolver.java | 3 + .../spel/support/StandardTypeComparator.java | 5 +- .../spel/ConstructorInvocationTests.java | 49 --- .../spel/DefaultComparatorUnitTests.java | 50 +++ .../expression/spel/EvaluationTests.java | 324 +------------- .../expression/spel/ExpressionStateTests.java | 241 +++++++++++ .../expression/spel/HelperTests.java | 37 +- .../expression/spel/InProgressTests.java | 395 ++++++++++++++++++ .../expression/spel/SetValueTests.java | 1 + .../spel/StandardTypeLocatorTests.java | 55 +++ .../spel/TemplateExpressionParsingTests.java | 46 ++ 18 files changed, 862 insertions(+), 406 deletions(-) create mode 100644 org.springframework.expression/src/test/java/org/springframework/expression/spel/ExpressionStateTests.java create mode 100644 org.springframework.expression/src/test/java/org/springframework/expression/spel/InProgressTests.java create mode 100644 org.springframework.expression/src/test/java/org/springframework/expression/spel/StandardTypeLocatorTests.java diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/TypedValue.java b/org.springframework.expression/src/main/java/org/springframework/expression/TypedValue.java index 00c56fce363..383c8064587 100644 --- a/org.springframework.expression/src/main/java/org/springframework/expression/TypedValue.java +++ b/org.springframework.expression/src/main/java/org/springframework/expression/TypedValue.java @@ -29,6 +29,8 @@ public class TypedValue { private Object value; private TypeDescriptor typeDescriptor; + + public static final TypedValue NULL_TYPED_VALUE = new TypedValue(null, TypeDescriptor.NULL_TYPE_DESCRIPTOR); /** * Create a TypedValue for a simple object. The type descriptor is inferred diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/common/TemplateAwareExpressionParser.java b/org.springframework.expression/src/main/java/org/springframework/expression/common/TemplateAwareExpressionParser.java index d0423925a41..bd09dc56e12 100644 --- a/org.springframework.expression/src/main/java/org/springframework/expression/common/TemplateAwareExpressionParser.java +++ b/org.springframework.expression/src/main/java/org/springframework/expression/common/TemplateAwareExpressionParser.java @@ -1 +1 @@ -/* * Copyright 2002-2009 the original author or authors. * * Licensed 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.springframework.expression.common; import java.util.LinkedList; import java.util.List; import org.springframework.expression.Expression; import org.springframework.expression.ExpressionParser; import org.springframework.expression.ParseException; import org.springframework.expression.ParserContext; /** * An expression parser that understands templates. It can be subclassed * by expression parsers that do not offer first class support for templating. * * @author Keith Donald * @author Juergen Hoeller * @author Andy Clement * @since 3.0 */ public abstract class TemplateAwareExpressionParser implements ExpressionParser { /** * Default ParserContext instance for non-template expressions. */ private static final ParserContext NON_TEMPLATE_PARSER_CONTEXT = new ParserContext() { public String getExpressionPrefix() { return null; } public String getExpressionSuffix() { return null; } public boolean isTemplate() { return false; } }; public Expression parseExpression(String expressionString) throws ParseException { return parseExpression(expressionString, NON_TEMPLATE_PARSER_CONTEXT); } public Expression parseExpression(String expressionString, ParserContext context) throws ParseException { if (context == null) { context = NON_TEMPLATE_PARSER_CONTEXT; } if (context.isTemplate()) { return parseTemplate(expressionString, context); } else { return doParseExpression(expressionString, context); } } private Expression parseTemplate(String expressionString, ParserContext context) throws ParseException { if (expressionString.length() == 0) { return new LiteralExpression(""); } Expression[] expressions = parseExpressions(expressionString, context); if (expressions.length == 1) { return expressions[0]; } else { return new CompositeStringExpression(expressionString, expressions); } } /** * Helper that parses given expression string using the configured parser. The expression string can contain any * number of expressions all contained in "${...}" markers. For instance: "foo${expr0}bar${expr1}". The static * pieces of text will also be returned as Expressions that just return that static piece of text. As a result, * evaluating all returned expressions and concatenating the results produces the complete evaluated string. * Unwrapping is only done of the outermost delimiters found, so the string 'hello ${foo${abc}}' would break into * the pieces 'hello ' and 'foo${abc}'. This means that expression languages that used ${..} as part of their * functionality are supported without any problem * @param expressionString the expression string * @return the parsed expressions * @throws ParseException when the expressions cannot be parsed */ private Expression[] parseExpressions(String expressionString, ParserContext context) throws ParseException { // TODO this needs to handle nested delimiters for cases where the expression uses the delim chars List expressions = new LinkedList(); int startIdx = 0; String prefix = context.getExpressionPrefix(); String suffix = context.getExpressionSuffix(); while (startIdx < expressionString.length()) { int prefixIndex = expressionString.indexOf(prefix, startIdx); if (prefixIndex >= startIdx) { // a inner expression was found - this is a composite if (prefixIndex > startIdx) { expressions.add(new LiteralExpression(expressionString.substring(startIdx, prefixIndex))); } int afterPrefixIndex = prefixIndex + prefix.length(); int suffixIndex = expressionString.indexOf(suffix, afterPrefixIndex); int nextPrefixIndex = expressionString.indexOf(prefix, afterPrefixIndex); if (nextPrefixIndex != -1 && suffixIndex > nextPrefixIndex) { throw new ParseException(expressionString, "No ending suffix '" + suffix + "' for expression starting at character " + prefixIndex + ": " + expressionString.substring(prefixIndex)); } else if (suffixIndex == afterPrefixIndex) { throw new ParseException(expressionString, "No expression defined within delimiter '" + prefix + suffix + "' at character " + prefixIndex); } else { String expr = expressionString.substring(prefixIndex + prefix.length(), suffixIndex); expressions.add(doParseExpression(expr, context)); startIdx = suffixIndex + suffix.length(); } } else { // no more ${expressions} found in string, add rest as static text expressions.add(new LiteralExpression(expressionString.substring(startIdx))); startIdx = expressionString.length(); } } return expressions.toArray(new Expression[expressions.size()]); } /** * Actually parse the expression string and return an Expression object. * @param expressionString the raw expression string to parse * @param context a context for influencing this expression parsing routine (optional) * @return an evaluator for the parsed expression * @throws ParseException an exception occurred during parsing */ protected abstract Expression doParseExpression(String expressionString, ParserContext context) throws ParseException; } \ No newline at end of file +/* * Copyright 2002-2009 the original author or authors. * * Licensed 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.springframework.expression.common; import java.util.LinkedList; import java.util.List; import org.springframework.expression.Expression; import org.springframework.expression.ExpressionParser; import org.springframework.expression.ParseException; import org.springframework.expression.ParserContext; /** * An expression parser that understands templates. It can be subclassed * by expression parsers that do not offer first class support for templating. * * @author Keith Donald * @author Juergen Hoeller * @author Andy Clement * @since 3.0 */ public abstract class TemplateAwareExpressionParser implements ExpressionParser { /** * Default ParserContext instance for non-template expressions. */ private static final ParserContext NON_TEMPLATE_PARSER_CONTEXT = new ParserContext() { public String getExpressionPrefix() { return null; } public String getExpressionSuffix() { return null; } public boolean isTemplate() { return false; } }; public Expression parseExpression(String expressionString) throws ParseException { return parseExpression(expressionString, NON_TEMPLATE_PARSER_CONTEXT); } public Expression parseExpression(String expressionString, ParserContext context) throws ParseException { if (context == null) { context = NON_TEMPLATE_PARSER_CONTEXT; } if (context.isTemplate()) { return parseTemplate(expressionString, context); } else { return doParseExpression(expressionString, context); } } private Expression parseTemplate(String expressionString, ParserContext context) throws ParseException { if (expressionString.length() == 0) { return new LiteralExpression(""); } Expression[] expressions = parseExpressions(expressionString, context); if (expressions.length == 1) { return expressions[0]; } else { return new CompositeStringExpression(expressionString, expressions); } } /** * Helper that parses given expression string using the configured parser. The expression string can contain any * number of expressions all contained in "${...}" markers. For instance: "foo${expr0}bar${expr1}". The static * pieces of text will also be returned as Expressions that just return that static piece of text. As a result, * evaluating all returned expressions and concatenating the results produces the complete evaluated string. * Unwrapping is only done of the outermost delimiters found, so the string 'hello ${foo${abc}}' would break into * the pieces 'hello ' and 'foo${abc}'. This means that expression languages that used ${..} as part of their * functionality are supported without any problem * @param expressionString the expression string * @return the parsed expressions * @throws ParseException when the expressions cannot be parsed */ private Expression[] parseExpressions(String expressionString, ParserContext context) throws ParseException { List expressions = new LinkedList(); int startIdx = 0; String prefix = context.getExpressionPrefix(); String suffix = context.getExpressionSuffix(); while (startIdx < expressionString.length()) { int prefixIndex = expressionString.indexOf(prefix, startIdx); if (prefixIndex >= startIdx) { // a inner expression was found - this is a composite if (prefixIndex > startIdx) { expressions.add(new LiteralExpression(expressionString.substring(startIdx, prefixIndex))); } int afterPrefixIndex = prefixIndex + prefix.length(); int suffixIndex = expressionString.indexOf(suffix, afterPrefixIndex); int nextPrefixIndex = expressionString.indexOf(prefix, afterPrefixIndex); if (suffixIndex == -1) { throw new ParseException(expressionString, "No ending suffix '" + suffix + "' for expression starting at character " + prefixIndex + ": " + expressionString.substring(prefixIndex)); } if (nextPrefixIndex != -1 && suffixIndex > nextPrefixIndex) { throw new ParseException(expressionString, "No ending suffix '" + suffix + "' for expression starting at character " + prefixIndex + ": " + expressionString.substring(prefixIndex)); } else if (suffixIndex == afterPrefixIndex) { throw new ParseException(expressionString, "No expression defined within delimiter '" + prefix + suffix + "' at character " + prefixIndex); } else { String expr = expressionString.substring(prefixIndex + prefix.length(), suffixIndex); expressions.add(doParseExpression(expr, context)); startIdx = suffixIndex + suffix.length(); } } else { // no more ${expressions} found in string, add rest as static text expressions.add(new LiteralExpression(expressionString.substring(startIdx))); startIdx = expressionString.length(); } } return expressions.toArray(new Expression[expressions.size()]); } /** * Actually parse the expression string and return an Expression object. * @param expressionString the raw expression string to parse * @param context a context for influencing this expression parsing routine (optional) * @return an evaluator for the parsed expression * @throws ParseException an exception occurred during parsing */ protected abstract Expression doParseExpression(String expressionString, ParserContext context) throws ParseException; } \ No newline at end of file diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/spel/ExpressionState.java b/org.springframework.expression/src/main/java/org/springframework/expression/spel/ExpressionState.java index 75a26674bb9..515b0c00561 100644 --- a/org.springframework.expression/src/main/java/org/springframework/expression/spel/ExpressionState.java +++ b/org.springframework.expression/src/main/java/org/springframework/expression/spel/ExpressionState.java @@ -52,9 +52,10 @@ public class ExpressionState { this.relatedContext = context; createVariableScope(); } - + + // create an empty top level VariableScope private void createVariableScope() { - this.variableScopes.add(new VariableScope()); // create an empty top level VariableScope + this.variableScopes.add(new VariableScope()); } /** @@ -64,9 +65,10 @@ public class ExpressionState { if (this.contextObjects.isEmpty()) { TypedValue rootObject = this.relatedContext.getRootObject(); if (rootObject == null) { - return new TypedValue(rootObject,TypeDescriptor.NULL_TYPE_DESCRIPTOR); + return TypedValue.NULL_TYPED_VALUE; + } else { + return rootObject; } - return rootObject; } return this.contextObjects.peek(); } @@ -80,7 +82,12 @@ public class ExpressionState { } public TypedValue getRootContextObject() { - return this.relatedContext.getRootObject(); + TypedValue root = this.relatedContext.getRootObject(); + if (root == null) { + return TypedValue.NULL_TYPED_VALUE; + } else { + return root; + } } public void setVariable(String name, Object value) { @@ -89,7 +96,11 @@ public class ExpressionState { public TypedValue lookupVariable(String name) { Object value = this.relatedContext.lookupVariable(name); - return new TypedValue(value,TypeDescriptor.forObject(value)); + if (value==null) { + return TypedValue.NULL_TYPED_VALUE; + } else { + return new TypedValue(value,TypeDescriptor.forObject(value)); + } } public TypeComparator getTypeComparator() { @@ -108,13 +119,10 @@ public class ExpressionState { return this.relatedContext.getTypeConverter().convertValue(value.getValue(), targetTypeDescriptor); } -// public T convertValue(TypedValue value, Class targetType) throws EvaluationException { -// return this.relatedContext.getTypeConverter().convertValue(value, targetType); -// } - - /** + /* * A new scope is entered when a function is invoked */ + public void enterScope(Map argMap) { this.variableScopes.push(new VariableScope(argMap)); } @@ -167,7 +175,6 @@ public class ExpressionState { * A new scope is entered when a function is called and it is used to hold the parameters to the function call. If the names * of the parameters clash with those in a higher level scope, those in the higher level scope will not be accessible whilst * the function is executing. When the function returns the scope is exited. - * */ private static class VariableScope { diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/NullLiteral.java b/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/NullLiteral.java index b2f647aa8ab..8e342e9ab5f 100644 --- a/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/NullLiteral.java +++ b/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/NullLiteral.java @@ -17,7 +17,6 @@ package org.springframework.expression.spel.ast; import org.antlr.runtime.Token; -import org.springframework.core.convert.TypeDescriptor; import org.springframework.expression.TypedValue; /** @@ -26,20 +25,18 @@ import org.springframework.expression.TypedValue; */ public class NullLiteral extends Literal { - private static final TypedValue NULL_TYPED_VALUE = new TypedValue(null,TypeDescriptor.NULL_TYPE_DESCRIPTOR); - public NullLiteral(Token payload) { super(payload); } @Override public TypedValue getLiteralValue() { - return NULL_TYPED_VALUE; + return TypedValue.NULL_TYPED_VALUE; } @Override public String toString() { - return null; + return "null"; } } diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/Projection.java b/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/Projection.java index 18b7190003f..1dd1465e186 100644 --- a/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/Projection.java +++ b/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/Projection.java @@ -48,7 +48,7 @@ public class Projection extends SpelNodeImpl { TypedValue op = state.getActiveContextObject(); Object operand = op.getValue(); - TypeDescriptor operandTypeDescriptor = op.getTypeDescriptor(); + // TypeDescriptor operandTypeDescriptor = op.getTypeDescriptor(); // When the input is a map, we push a special context object on the stack // before calling the specified operation. This special context object @@ -58,9 +58,10 @@ public class Projection extends SpelNodeImpl { if (operand instanceof Map) { Map mapdata = (Map) operand; List result = new ArrayList(); - for (Object k : mapdata.keySet()) { + mapdata.entrySet(); + for (Map.Entry entry : mapdata.entrySet()) { try { - state.pushActiveContextObject(new TypedValue(new KeyValuePair(k, mapdata.get(k)),TypeDescriptor.valueOf(KeyValuePair.class))); + state.pushActiveContextObject(new TypedValue(new KeyValuePair(entry.getKey(), entry.getValue()),TypeDescriptor.valueOf(KeyValuePair.class))); result.add(getChild(0).getValueInternal(state).getValue()); } finally { state.popActiveContextObject(); diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/Selection.java b/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/Selection.java index 35af3d2cf30..59931f49f34 100644 --- a/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/Selection.java +++ b/org.springframework.expression/src/main/java/org/springframework/expression/spel/ast/Selection.java @@ -63,10 +63,10 @@ public class Selection extends SpelNodeImpl { // TODO don't lose generic info for the new map Map result = new HashMap(); Object lastKey = null; - for (Object k : mapdata.keySet()) { + for (Map.Entry entry : mapdata.entrySet()) { try { - lastKey = k; - KeyValuePair kvp = new KeyValuePair(k,mapdata.get(k)); + lastKey = entry.getKey(); + KeyValuePair kvp = new KeyValuePair(entry.getKey(),entry.getValue()); TypedValue kvpair = new TypedValue(kvp,TypeDescriptor.valueOf(KeyValuePair.class)); state.pushActiveContextObject(kvpair); Object o = selectionCriteria.getValueInternal(state).getValue(); @@ -90,9 +90,9 @@ public class Selection extends SpelNodeImpl { return new TypedValue(null,TypeDescriptor.NULL_TYPE_DESCRIPTOR); } if (variant == LAST) { - Object lastValue = result.get(lastKey); Map resultMap = new HashMap(); - resultMap.put(lastKey,result.get(lastKey)); + Object lastValue = result.get(lastKey); + resultMap.put(lastKey,lastValue); return new TypedValue(resultMap,TypeDescriptor.valueOf(Map.class)); } return new TypedValue(result,op.getTypeDescriptor()); diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/spel/support/ReflectiveConstructorExecutor.java b/org.springframework.expression/src/main/java/org/springframework/expression/spel/support/ReflectiveConstructorExecutor.java index 9a4ce4008f0..fa2a4478d23 100644 --- a/org.springframework.expression/src/main/java/org/springframework/expression/spel/support/ReflectiveConstructorExecutor.java +++ b/org.springframework.expression/src/main/java/org/springframework/expression/spel/support/ReflectiveConstructorExecutor.java @@ -57,8 +57,7 @@ class ReflectiveConstructorExecutor implements ConstructorExecutor { c.setAccessible(true); } return new TypedValue(c.newInstance(arguments),TypeDescriptor.valueOf(c.getClass())); - } - catch (Exception ex) { + } catch (Exception ex) { throw new AccessException("Problem invoking constructor: " + c, ex); } } diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/spel/support/ReflectivePropertyResolver.java b/org.springframework.expression/src/main/java/org/springframework/expression/spel/support/ReflectivePropertyResolver.java index 34414a9e017..7035d08aa97 100644 --- a/org.springframework.expression/src/main/java/org/springframework/expression/spel/support/ReflectivePropertyResolver.java +++ b/org.springframework.expression/src/main/java/org/springframework/expression/spel/support/ReflectivePropertyResolver.java @@ -333,6 +333,9 @@ public class ReflectivePropertyResolver implements PropertyAccessor { if (this == other) { return true; } + if (!(other instanceof CacheKey)) { + return false; + } CacheKey otherKey = (CacheKey) other; return (this.clazz.equals(otherKey.clazz) && this.name.equals(otherKey.name)); } diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/spel/support/StandardTypeComparator.java b/org.springframework.expression/src/main/java/org/springframework/expression/spel/support/StandardTypeComparator.java index 614f6b5ad26..71496a8afd3 100644 --- a/org.springframework.expression/src/main/java/org/springframework/expression/spel/support/StandardTypeComparator.java +++ b/org.springframework.expression/src/main/java/org/springframework/expression/spel/support/StandardTypeComparator.java @@ -34,9 +34,8 @@ public class StandardTypeComparator implements TypeComparator { // If one is null, check if the other is if (left == null) { return right == null ? 0 : 1; - } - else if (right == null) { - return left == null ? 0 : -1; + } else if (right == null) { + return -1; // left cannot be null } // Basic number comparisons diff --git a/org.springframework.expression/src/test/java/org/springframework/expression/spel/ConstructorInvocationTests.java b/org.springframework.expression/src/test/java/org/springframework/expression/spel/ConstructorInvocationTests.java index 802cb9b0351..ab2ecd72f44 100644 --- a/org.springframework.expression/src/test/java/org/springframework/expression/spel/ConstructorInvocationTests.java +++ b/org.springframework.expression/src/test/java/org/springframework/expression/spel/ConstructorInvocationTests.java @@ -23,57 +23,8 @@ package org.springframework.expression.spel; */ public class ConstructorInvocationTests extends ExpressionTestCase { - // Some tests commented out as language support has been removed for now - - // public void testPrimitiveTypeArrayConstructors() { - // evaluate("new int[]{1,2,3,4}.count()", 4, Integer.class); - // evaluate("new boolean[]{true,false,true}.count()", 3, Integer.class); - // evaluate("new char[]{'a','b','c'}.count()", 3, Integer.class); - // evaluate("new long[]{1,2,3,4,5}.count()", 5, Integer.class); - // evaluate("new short[]{2,3,4,5,6}.count()", 5, Integer.class); - // evaluate("new double[]{1d,2d,3d,4d}.count()", 4, Integer.class); - // evaluate("new float[]{1f,2f,3f,4f}.count()", 4, Integer.class); - // evaluate("new byte[]{1,2,3,4}.count()", 4, Integer.class); - // } - - // public void testPrimitiveTypeArrayConstructorsElements() { - // evaluate("new int[]{1,2,3,4}[0]", 1, Integer.class); - // evaluate("new boolean[]{true,false,true}[0]", true, Boolean.class); - // evaluate("new char[]{'a','b','c'}[0]", 'a', Character.class); - // evaluate("new long[]{1,2,3,4,5}[0]", 1L, Long.class); - // evaluate("new short[]{2,3,4,5,6}[0]", (short) 2, Short.class); - // evaluate("new double[]{1d,2d,3d,4d}[0]", (double) 1, Double.class); - // evaluate("new float[]{1f,2f,3f,4f}[0]", (float) 1, Float.class); - // evaluate("new byte[]{1,2,3,4}[0]", (byte) 1, Byte.class); - // } - - // public void testErrorCases() { - // evaluateAndCheckError("new char[7]{'a','c','d','e'}", SpelMessages.INITIALIZER_LENGTH_INCORRECT); - // evaluateAndCheckError("new char[3]{'a','c','d','e'}", SpelMessages.INITIALIZER_LENGTH_INCORRECT); - // evaluateAndCheckError("new char[2]{'hello','world'}", SpelMessages.TYPE_CONVERSION_ERROR); - // evaluateAndCheckError("new String('a','c','d')", SpelMessages.CONSTRUCTOR_NOT_FOUND); - // } - - // public void testTypeArrayConstructors() { - // evaluate("new String[]{'a','b','c','d'}[1]", "b", String.class); - // evaluateAndCheckError("new String[]{'a','b','c','d'}.size()", SpelMessages.METHOD_NOT_FOUND, 30, "size()", - // "java.lang.String[]"); - // evaluateAndCheckError("new String[]{'a','b','c','d'}.juggernaut", SpelMessages.PROPERTY_OR_FIELD_NOT_FOUND, 30, - // "juggernaut", "java.lang.String[]"); - // evaluate("new String[]{'a','b','c','d'}.length", 4, Integer.class); - // } - - // public void testMultiDimensionalArrays() { - // evaluate( - // "new String[3,4]", - // "[Ljava.lang.String;[3]{java.lang.String[4]{null,null,null,null},java.lang.String[4]{null,null,null,null},java.lang.String[4]{null,null,null,null}}" - // , - // new String[3][4].getClass()); - // } - public void testTypeConstructors() { evaluate("new String('hello world')", "hello world", String.class); - // evaluate("new String(new char[]{'h','e','l','l','o'})", "hello", String.class); } public void testNonExistentType() { diff --git a/org.springframework.expression/src/test/java/org/springframework/expression/spel/DefaultComparatorUnitTests.java b/org.springframework.expression/src/test/java/org/springframework/expression/spel/DefaultComparatorUnitTests.java index b146c52b582..98a951cc090 100644 --- a/org.springframework.expression/src/test/java/org/springframework/expression/spel/DefaultComparatorUnitTests.java +++ b/org.springframework.expression/src/test/java/org/springframework/expression/spel/DefaultComparatorUnitTests.java @@ -43,5 +43,55 @@ public class DefaultComparatorUnitTests extends TestCase { assertTrue(comparator.compare(1.0f, 1) == 0); assertTrue(comparator.compare(2.0f, 1) > 0); + assertTrue(comparator.compare(1L, 2) < 0); + assertTrue(comparator.compare(1L, 1) == 0); + assertTrue(comparator.compare(2L, 1) > 0); + + assertTrue(comparator.compare(1, 2L) < 0); + assertTrue(comparator.compare(1, 1L) == 0); + assertTrue(comparator.compare(2, 1L) > 0); + + assertTrue(comparator.compare(1L, 2L) < 0); + assertTrue(comparator.compare(1L, 1L) == 0); + assertTrue(comparator.compare(2L, 1L) > 0); + } + + public void testNulls() throws EvaluationException { + TypeComparator comparator = new StandardTypeComparator(); + assertTrue(comparator.compare(null,"abc")>0); + assertTrue(comparator.compare(null,null)==0); + assertTrue(comparator.compare("abc",null)<0); + } + + public void testObjects() throws EvaluationException { + TypeComparator comparator = new StandardTypeComparator(); + assertTrue(comparator.compare("a","a")==0); + assertTrue(comparator.compare("a","b")<0); + assertTrue(comparator.compare("b","a")>0); + + try { + comparator.compare("a",3); + fail("Should have failed"); + } catch (EvaluationException ee) { + SpelException sEx = (SpelException)ee; + assertEquals(SpelMessages.NOT_COMPARABLE,sEx.getMessageUnformatted()); + } + try { + comparator.compare(2,"b"); + fail("Should have failed"); + } catch (EvaluationException ee) { + SpelException sEx = (SpelException)ee; + assertEquals(SpelMessages.NOT_COMPARABLE,sEx.getMessageUnformatted()); + } + } + + public void testCanCompare() throws EvaluationException { + TypeComparator comparator = new StandardTypeComparator(); + assertTrue(comparator.canCompare(null,1)); + assertTrue(comparator.canCompare(1,null)); + + assertTrue(comparator.canCompare(2,1)); + assertTrue(comparator.canCompare("abc","def")); + assertFalse(comparator.canCompare("abc",3)); } } 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 2cd468d501a..bd081f22536 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 @@ -16,21 +16,16 @@ package org.springframework.expression.spel; -import java.util.ArrayList; - import org.springframework.expression.Expression; import org.springframework.expression.spel.support.StandardEvaluationContext; /** * Tests the evaluation of real expressions in a real context. * - * Node: Some tests commented out as language support has been removed - * * @author Andy Clement */ public class EvaluationTests extends ExpressionTestCase { - // relational operators: lt, le, gt, ge, eq, ne public void testRelOperatorGT01() { evaluate("3 > 6", "false", Boolean.class); } @@ -51,48 +46,10 @@ public class EvaluationTests extends ExpressionTestCase { evaluate("3 >= 3", "true", Boolean.class); } - - // public void testRelOperatorsIn01() { - // evaluate("3 in {1,2,3,4,5}", "true", Boolean.class); - // } - - // public void testRelOperatorsIn02() { - // evaluate("name in {null, \"Nikola Tesla\"}", "true", Boolean.class); - // evaluate("name in {null, \"Anonymous\"}", "false", Boolean.class); - // } - // - public void testRelOperatorsBetween01() { - evaluate("1 between listOneFive", "true", Boolean.class); - // evaluate("1 between {1, 5}", "true", Boolean.class); // no inline list building at the moment - } - // - // public void testRelOperatorsBetween02() { - // evaluate("'efg' between {'abc', 'xyz'}", "true", Boolean.class); - // } - // - public void testRelOperatorsBetweenErrors01() { - evaluateAndCheckError("1 between T(String)", SpelMessages.BETWEEN_RIGHT_OPERAND_MUST_BE_TWO_ELEMENT_LIST, 12); - } - // - // public void testRelOperatorsBetweenErrors02() { - // evaluateAndCheckError("'abc' between {5,7}", SpelMessages.NOT_COMPARABLE, 6); - // } - public void testRelOperatorsBetweenErrors03() { - evaluateAndCheckError("1 between listOfNumbersUpToTen", SpelMessages.BETWEEN_RIGHT_OPERAND_MUST_BE_TWO_ELEMENT_LIST, 10); - } - public void testRelOperatorsIs01() { evaluate("'xyz' instanceof T(int)", "false", Boolean.class); } - // public void testRelOperatorsIs02() { - // evaluate("{1, 2, 3, 4, 5} instanceof T(List)", "true", Boolean.class); - // } - // - // public void testRelOperatorsIs03() { - // evaluate("{1, 2, 3, 4, 5} instanceof T(List)", "true", Boolean.class); - // } - public void testRelOperatorsIs04() { evaluate("null instanceof T(String)", "false", Boolean.class); } @@ -132,9 +89,8 @@ public class EvaluationTests extends ExpressionTestCase { // property access public void testPropertyField01() { - evaluate("name", "Nikola Tesla", String.class, false); // not writable because (1) name is private (2) there is - // no - // setter, only a getter + evaluate("name", "Nikola Tesla", String.class, false); + // not writable because (1) name is private (2) there is no setter, only a getter evaluateAndCheckError("madeup", SpelMessages.PROPERTY_OR_FIELD_NOT_FOUND, 0, "madeup", "org.springframework.expression.spel.testresources.Inventor"); } @@ -157,59 +113,11 @@ public class EvaluationTests extends ExpressionTestCase { evaluate("echo(name)", "Nikola Tesla", String.class); } - // inline list creation - // public void testInlineListCreation01() { - // evaluate("{1, 2, 3, 4, 5}", "[1, 2, 3, 4, 5]", ArrayList.class); - // } - // - // public void testInlineListCreation02() { - // evaluate("{'abc', 'xyz'}", "[abc, xyz]", ArrayList.class); - // } - // - // // inline map creation - // public void testInlineMapCreation01() { - // evaluate("#{'key1':'Value 1', 'today':'Monday'}", "{key1=Value 1, today=Monday}", HashMap.class); - // } - // - // public void testInlineMapCreation02() { - // evaluate("#{1:'January', 2:'February', 3:'March'}.size()", 3, Integer.class);// "{2=February, 1=January, - // // 3=March}", HashMap.class); - // } - // - // public void testInlineMapCreation03() { - // evaluate("#{'key1':'Value 1', 'today':'Monday'}['key1']", "Value 1", String.class); - // } - // - // public void testInlineMapCreation04() { - // evaluate("#{1:'January', 2:'February', 3:'March'}[3]", "March", String.class); - // } - // - // public void testInlineMapCreation05() { - // evaluate("#{1:'January', 2:'February', 3:'March'}.get(2)", "February", String.class); - // } - // - // // set construction - // public void testSetConstruction01() { - // evaluate("new HashSet().addAll({'a','b','c'})", "true", Boolean.class); - // } - // constructors public void testConstructorInvocation01() { evaluate("new String('hello')", "hello", String.class); } - // public void testConstructorInvocation02() { - // evaluate("new String[3]", "java.lang.String[3]{null,null,null}", String[].class); - // } - - // public void testConstructorInvocation03() { - // evaluateAndCheckError("new String[]", SpelMessages.NO_SIZE_OR_INITIALIZER_FOR_ARRAY_CONSTRUCTION, 4); - // } - - // public void testConstructorInvocation04() { - // evaluateAndCheckError("new String[3]{'abc',3,'def'}", SpelMessages.INCORRECT_ELEMENT_TYPE_FOR_ARRAY, 4); - // } - public void testConstructorInvocation05() { evaluate("new java.lang.String('foobar')", "foobar", String.class); } @@ -229,16 +137,6 @@ public class EvaluationTests extends ExpressionTestCase { assertEquals("new String('wibble')",expr.toStringAST()); } - - // array construction - // public void testArrayConstruction01() { - // evaluate("new int[] {1, 2, 3, 4, 5}", "int[5]{1,2,3,4,5}", int[].class); - // } - - // public void testArrayConstruction02() { - // evaluate("new String[] {'abc', 'xyz'}", "java.lang.String[2]{abc,xyz}", String[].class); - // } - // unary expressions public void testUnaryMinus01() { evaluate("-5", "-5", Integer.class); @@ -251,153 +149,11 @@ public class EvaluationTests extends ExpressionTestCase { public void testUnaryNot01() { evaluate("!true", "false", Boolean.class); } - - // collection processors - // from spring.net: count,sum,max,min,average,sort,orderBy,distinct,nonNull - // public void testProcessorsCount01() { - // evaluate("new String[] {'abc','def','xyz'}.count()", "3", Integer.class); - // } - // - // public void testProcessorsCount02() { - // evaluate("new int[] {1,2,3}.count()", "3", Integer.class); - // } - // - // public void testProcessorsMax01() { - // evaluate("new int[] {1,2,3}.max()", "3", Integer.class); - // } - // - // public void testProcessorsMin01() { - // evaluate("new int[] {1,2,3}.min()", "1", Integer.class); - // } - // - // public void testProcessorsKeys01() { - // evaluate("#{1:'January', 2:'February', 3:'March'}.keySet().sort()", "[1, 2, 3]", ArrayList.class); - // } - // - // public void testProcessorsValues01() { - // evaluate("#{1:'January', 2:'February', 3:'March'}.values().sort()", "[February, January, March]", - // ArrayList.class); - // } - // - // public void testProcessorsAverage01() { - // evaluate("new int[] {1,2,3}.average()", "2", Integer.class); - // } - // - // public void testProcessorsSort01() { - // evaluate("new int[] {3,2,1}.sort()", "int[3]{1,2,3}", int[].class); - // } - // - // public void testCollectionProcessorsNonNull01() { - // evaluate("{'a','b',null,'d',null}.nonnull()", "[a, b, d]", ArrayList.class); - // } - // - // public void testCollectionProcessorsDistinct01() { - // evaluate("{'a','b','a','d','e'}.distinct()", "[a, b, d, e]", ArrayList.class); - // } - - // projection and selection - public void testProjection01() { - evaluate("listOfNumbersUpToTen.!{#this<5?'y':'n'}","[y, y, y, y, n, n, n, n, n, n]",ArrayList.class); - // inline list creation not supported at the moment - // evaluate("{1,2,3,4,5,6,7,8,9,10}.!{#isEven(#this)}", "[n, y, n, y, n, y, n, y, n, y]", ArrayList.class); - } - - public void testProjection02() { - // inline map creation not supported at the moment - // evaluate("#{'a':'y','b':'n','c':'y'}.!{value=='y'?key:null}.nonnull().sort()", "[a, c]", ArrayList.class); - evaluate("mapOfNumbersUpToTen.!{key>5?value:null}", "[null, null, null, null, null, six, seven, eight, nine, ten]", ArrayList.class); - } - - // public void testProjection03() { - // evaluate("{1,2,3,4,5,6,7,8,9,10}.!{#this>5}", - // "[false, false, false, false, false, true, true, true, true, true]", ArrayList.class); - // } - - // public void testProjection04() { - // evaluate("{1,2,3,4,5,6,7,8,9,10}.!{$index>5?'y':'n'}", "[n, n, n, n, n, n, y, y, y, y]", ArrayList.class); - // } - - public void testProjection05() { - evaluateAndCheckError("'abc'.!{true}", SpelMessages.PROJECTION_NOT_SUPPORTED_ON_TYPE); - } - - public void testProjection06() throws Exception { - SpelExpression expr = (SpelExpression)parser.parseExpression("'abc'.!{true}"); - assertEquals("'abc'.!{true}",expr.toStringAST()); - assertFalse(expr.isWritable(new StandardEvaluationContext())); - } - - //public void testSelection01() { - // inline list creation not supported: - // evaluate("{1,2,3,4,5,6,7,8,9,10}.?{#isEven(#this) == 'y'}", "[2, 4, 6, 8, 10]", ArrayList.class); - //} - - public void testSelection02() { - evaluate("testMap.keySet().?{#this matches '.*o.*'}", "[monday]", ArrayList.class); - evaluate("testMap.keySet().?{#this matches '.*r.*'}.contains('saturday')", "true", Boolean.class); - evaluate("testMap.keySet().?{#this matches '.*r.*'}.size()", "3", Integer.class); - } - - public void testSelectionError_NonBooleanSelectionCriteria() { - evaluateAndCheckError("listOfNumbersUpToTen.?{'nonboolean'}", - SpelMessages.RESULT_OF_SELECTION_CRITERIA_IS_NOT_BOOLEAN); - } - - // public void testSelectionUsingIndex() { - // evaluate("{1,2,3,4,5,6,7,8,9,10}.?{$index > 5 }", "[7, 8, 9, 10]", ArrayList.class); - // } - - public void testSelection03() { - evaluate("mapOfNumbersUpToTen.?{key>5}.size()", "5", Integer.class); - } - - public void testSelection04() { - evaluateAndCheckError("mapOfNumbersUpToTen.?{'hello'}.size()",SpelMessages.RESULT_OF_SELECTION_CRITERIA_IS_NOT_BOOLEAN); - } - - public void testSelectionFirst01() { - evaluate("listOfNumbersUpToTen.^{#isEven(#this) == 'y'}", "2", Integer.class); - } - - public void testSelectionFirst02() { - evaluate("mapOfNumbersUpToTen.^{key>5}.size()", "1", Integer.class); - } - - public void testSelectionLast01() { - evaluate("listOfNumbersUpToTen.${#isEven(#this) == 'y'}", "10", Integer.class); - } - - public void testSelectionLast02() { - evaluate("mapOfNumbersUpToTen.${key>5}.size()", "1", Integer.class); - } - - public void testSelectionAST() throws Exception { - SpelExpression expr = (SpelExpression)parser.parseExpression("'abc'.^{true}"); - assertEquals("'abc'.^{true}",expr.toStringAST()); - assertFalse(expr.isWritable(new StandardEvaluationContext())); - expr = (SpelExpression)parser.parseExpression("'abc'.?{true}"); - assertEquals("'abc'.?{true}",expr.toStringAST()); - assertFalse(expr.isWritable(new StandardEvaluationContext())); - expr = (SpelExpression)parser.parseExpression("'abc'.${true}"); - assertEquals("'abc'.${true}",expr.toStringAST()); - assertFalse(expr.isWritable(new StandardEvaluationContext())); - } - - // assignment public void testAssignmentToVariables01() { evaluate("#var1='value1'", "value1", String.class); } - // Ternary operator - // public void testTernaryOperator01() { - // evaluate("{1}.#isEven(#this[0]) == 'y'?'it is even':'it is odd'", "it is odd", String.class); - // } - // - // public void testTernaryOperator02() { - // evaluate("{2}.#isEven(#this[0]) == 'y'?'it is even':'it is odd'", "it is even", String.class); - // } - public void testTernaryOperator01() { evaluate("2>4?1:2",2,Integer.class); } @@ -414,15 +170,6 @@ public class EvaluationTests extends ExpressionTestCase { Expression expr = parser.parseExpression("1>2?3:4"); assertFalse(expr.isWritable(eContext)); } - - // Indexer - // public void testCutProcessor01() { - // evaluate("{1,2,3,4,5}.cut(1,3)", "[2, 3, 4]", ArrayList.class); - // } - // - // public void testCutProcessor02() { - // evaluate("{1,2,3,4,5}.cut(3,1)", "[4, 3, 2]", ArrayList.class); - // } public void testIndexer03() { evaluate("'christian'[8]", "n", String.class); @@ -432,36 +179,6 @@ public class EvaluationTests extends ExpressionTestCase { evaluateAndCheckError("new org.springframework.expression.spel.testresources.Inventor().inventions[1]",SpelMessages.CANNOT_INDEX_INTO_NULL_VALUE); } - // Bean references - // public void testReferences01() { - // evaluate("@(apple).name", "Apple", String.class, true); - // } - // - // public void testReferences02() { - // evaluate("@(fruits:banana).name", "Banana", String.class, true); - // } - // - // public void testReferences03() { - // evaluate("@(a.b.c)", null, null); - // } // null - no context, a.b.c treated as name - // - // public void testReferences05() { - // evaluate("@(a/b/c:orange).name", "Orange", String.class, true); - // } - // - // public void testReferences06() { - // evaluate("@(apple).color.getRGB() == T(java.awt.Color).green.getRGB()", "true", Boolean.class); - // } - // - // public void testReferences07() { - // evaluate("@(apple).color.getRGB().equals(T(java.awt.Color).green.getRGB())", "true", Boolean.class); - // } - - // value is not public, it is accessed through getRGB() - // public void testStaticRef01() { - // evaluate("T(Color).green.value!=0", "true", Boolean.class); - // } - public void testStaticRef02() { evaluate("T(java.awt.Color).green.getRGB()!=0", "true", Boolean.class); } @@ -479,42 +196,6 @@ public class EvaluationTests extends ExpressionTestCase { evaluate("#reverseString('hello')", "olleh", String.class); } - // - // public void testLambda02() { - // evaluate("(#max={|x,y| $x > $y ? $x : $y };true)", "true", Boolean.class); - // } - // - // public void testLambdaMax() { - // evaluate("(#max = {|x,y| $x > $y ? $x : $y }; #max(5,25))", "25", Integer.class); - // } - // - // public void testLambdaFactorial01() { - // evaluate("(#fact = {|n| $n <= 1 ? 1 : $n * #fact($n-1) }; #fact(5))", "120", Integer.class); - // } - // - // public void testLambdaFactorial02() { - // evaluate("(#fact = {|n| $n <= 1 ? 1 : #fact($n-1) * $n }; #fact(5))", "120", Integer.class); - // } - // - // public void testLambdaAlphabet01() { - // evaluate("(#alpha = {|l,s| $l>'z'?$s:#alpha($l+1,$s+$l)};#alphabet={||#alpha('a','')}; #alphabet())", - // "abcdefghijklmnopqrstuvwxyz", String.class); - // } - // - // public void testLambdaAlphabet02() { - // evaluate("(#alphabet = {|l,s| $l>'z'?$s:#alphabet($l+1,$s+$l)};#alphabet('a',''))", - // "abcdefghijklmnopqrstuvwxyz", String.class); - // } - // - // public void testLambdaDelegation01() { - // evaluate("(#sqrt={|n| T(Math).sqrt($n)};#delegate={|f,n| $f($n)};#delegate(#sqrt,4))", "2.0", Double.class); - // } - // - // public void testVariableReferences() { - // evaluate("(#answer=42;#answer)", "42", Integer.class, true); - // evaluate("($answer=42;$answer)", "42", Integer.class, true); - // } - // type references public void testTypeReferences01() { evaluate("T(java.lang.String)", "class java.lang.String", Class.class); @@ -556,6 +237,5 @@ public class EvaluationTests extends ExpressionTestCase { evaluateAndAskForReturnType("3*4+5", (short) 17, Short.class); evaluateAndAskForReturnType("3*4+5", "17", String.class); } - } diff --git a/org.springframework.expression/src/test/java/org/springframework/expression/spel/ExpressionStateTests.java b/org.springframework.expression/src/test/java/org/springframework/expression/spel/ExpressionStateTests.java new file mode 100644 index 00000000000..0f4f61b5849 --- /dev/null +++ b/org.springframework.expression/src/test/java/org/springframework/expression/spel/ExpressionStateTests.java @@ -0,0 +1,241 @@ +/* + * Copyright 2002-2009 the original author or authors. + * + * Licensed 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.springframework.expression.spel; + +import java.util.HashMap; +import java.util.Map; + +import org.springframework.core.convert.TypeDescriptor; +import org.springframework.expression.EvaluationContext; +import org.springframework.expression.EvaluationException; +import org.springframework.expression.Operation; +import org.springframework.expression.TypedValue; +import org.springframework.expression.spel.support.StandardEvaluationContext; +import org.springframework.expression.spel.testresources.Inventor; + + +/** + * Tests for the expression state object - some features are not yet exploited in the language (eg nested scopes) + * + * @author Andy Clement + */ +public class ExpressionStateTests extends ExpressionTestCase { + + public void testConstruction() { + EvaluationContext context = TestScenarioCreator.getTestEvaluationContext(); + ExpressionState state = new ExpressionState(context); + assertEquals(context,state.getEvaluationContext()); + } + + // Local variables are in variable scopes which come and go during evaluation. Normal variables are + // accessible through the evaluation context + + + public void testLocalVariables() { + ExpressionState state = getState(); + + Object value = state.lookupLocalVariable("foo"); + assertNull(value); + + state.setLocalVariable("foo",34); + value = state.lookupLocalVariable("foo"); + assertEquals(34,value); + + state.setLocalVariable("foo",null); + value = state.lookupLocalVariable("foo"); + assertEquals(null,value); + + } + + public void testVariables() { + ExpressionState state = getState(); + TypedValue typedValue = state.lookupVariable("foo"); + assertEquals(TypedValue.NULL_TYPED_VALUE,typedValue); + + state.setVariable("foo",34); + typedValue = state.lookupVariable("foo"); + assertEquals(34,typedValue.getValue()); + assertEquals(Integer.class,typedValue.getTypeDescriptor().getType()); + + state.setVariable("foo","abc"); + typedValue = state.lookupVariable("foo"); + assertEquals("abc",typedValue.getValue()); + assertEquals(String.class,typedValue.getTypeDescriptor().getType()); + } + + public void testNoVariableInteference() { + ExpressionState state = getState(); + TypedValue typedValue = state.lookupVariable("foo"); + assertEquals(TypedValue.NULL_TYPED_VALUE,typedValue); + + state.setLocalVariable("foo",34); + typedValue = state.lookupVariable("foo"); + assertEquals(TypedValue.NULL_TYPED_VALUE,typedValue); + + state.setVariable("goo","hello"); + assertNull(state.lookupLocalVariable("goo")); + } + + public void testLocalVariableNestedScopes() { + ExpressionState state = getState(); + assertEquals(null,state.lookupLocalVariable("foo")); + + state.setLocalVariable("foo",12); + assertEquals(12,state.lookupLocalVariable("foo")); + + state.enterScope(null); + assertEquals(12,state.lookupLocalVariable("foo")); // found in upper scope + + state.setLocalVariable("foo","abc"); + assertEquals("abc",state.lookupLocalVariable("foo")); // found in nested scope + + state.exitScope(); + assertEquals(12,state.lookupLocalVariable("foo")); // found in nested scope + } + + public void testRootContextObject() { + ExpressionState state = getState(); + assertEquals(Inventor.class,state.getRootContextObject().getValue().getClass()); + + state.getEvaluationContext().setRootObject(null); + assertEquals(null,state.getRootContextObject().getValue()); + + state = new ExpressionState(new StandardEvaluationContext()); + assertEquals(TypedValue.NULL_TYPED_VALUE,state.getRootContextObject()); + } + + public void testActiveContextObject() { + ExpressionState state = getState(); + assertEquals(state.getRootContextObject().getValue(),state.getActiveContextObject().getValue()); + + state.pushActiveContextObject(new TypedValue(34)); + assertEquals(34,state.getActiveContextObject().getValue()); + + state.pushActiveContextObject(new TypedValue("hello")); + assertEquals("hello",state.getActiveContextObject().getValue()); + + state.popActiveContextObject(); + assertEquals(34,state.getActiveContextObject().getValue()); + + state.popActiveContextObject(); + assertEquals(state.getRootContextObject().getValue(),state.getActiveContextObject().getValue()); + + state = new ExpressionState(new StandardEvaluationContext()); + assertEquals(TypedValue.NULL_TYPED_VALUE,state.getActiveContextObject()); + } + + public void testPopulatedNestedScopes() { + ExpressionState state = getState(); + assertNull(state.lookupLocalVariable("foo")); + + state.enterScope("foo",34); + assertEquals(34,state.lookupLocalVariable("foo")); + + state.enterScope(null); + state.setLocalVariable("foo",12); + assertEquals(12,state.lookupLocalVariable("foo")); + + state.exitScope(); + assertEquals(34,state.lookupLocalVariable("foo")); + + state.exitScope(); + assertNull(state.lookupLocalVariable("goo")); + } + + public void testPopulatedNestedScopesMap() { + ExpressionState state = getState(); + assertNull(state.lookupLocalVariable("foo")); + assertNull(state.lookupLocalVariable("goo")); + + Map m = new HashMap(); + m.put("foo",34); + m.put("goo","abc"); + + state.enterScope(m); + assertEquals(34,state.lookupLocalVariable("foo")); + assertEquals("abc",state.lookupLocalVariable("goo")); + + state.enterScope(null); + state.setLocalVariable("foo",12); + assertEquals(12,state.lookupLocalVariable("foo")); + assertEquals("abc",state.lookupLocalVariable("goo")); + + state.exitScope(); + state.exitScope(); + assertNull(state.lookupLocalVariable("foo")); + assertNull(state.lookupLocalVariable("goo")); + } + + public void testOperators() throws Exception { + ExpressionState state = getState(); + try { + state.operate(Operation.ADD,1,2); + fail("should have failed"); + } catch (EvaluationException ee) { + SpelException sEx = (SpelException)ee; + assertEquals(SpelMessages.OPERATOR_NOT_SUPPORTED_BETWEEN_TYPES,sEx.getMessageUnformatted()); + } + + try { + state.operate(Operation.ADD,null,null); + fail("should have failed"); + } catch (EvaluationException ee) { + SpelException sEx = (SpelException)ee; + assertEquals(SpelMessages.OPERATOR_NOT_SUPPORTED_BETWEEN_TYPES,sEx.getMessageUnformatted()); + } + } + + public void testComparator() { + ExpressionState state = getState(); + assertEquals(state.getEvaluationContext().getTypeComparator(),state.getTypeComparator()); + } + + public void testTypeLocator() throws EvaluationException { + ExpressionState state = getState(); + assertNotNull(state.getEvaluationContext().getTypeLocator()); + assertEquals(Integer.class,state.findType("java.lang.Integer")); + try { + state.findType("someMadeUpName"); + fail("Should have failed to find it"); + } catch (EvaluationException ee) { + SpelException sEx = (SpelException)ee; + assertEquals(SpelMessages.TYPE_NOT_FOUND,sEx.getMessageUnformatted()); + } + } + + public void testTypeConversion() throws EvaluationException { + ExpressionState state = getState(); + String s = (String)state.convertValue(34,TypeDescriptor.valueOf(String.class)); + assertEquals("34",s); + + s = (String)state.convertValue(new TypedValue(34),TypeDescriptor.valueOf(String.class)); + assertEquals("34",s); + } + + public void testPropertyAccessors() { + ExpressionState state = getState(); + assertEquals(state.getEvaluationContext().getPropertyAccessors(),state.getPropertyAccessors()); + } + + /** + * @return a new ExpressionState + */ + private ExpressionState getState() { + EvaluationContext context = TestScenarioCreator.getTestEvaluationContext(); + ExpressionState state = new ExpressionState(context); + return state; + } +} diff --git a/org.springframework.expression/src/test/java/org/springframework/expression/spel/HelperTests.java b/org.springframework.expression/src/test/java/org/springframework/expression/spel/HelperTests.java index ffe54481037..596f304ea04 100644 --- a/org.springframework.expression/src/test/java/org/springframework/expression/spel/HelperTests.java +++ b/org.springframework.expression/src/test/java/org/springframework/expression/spel/HelperTests.java @@ -15,6 +15,10 @@ */ package org.springframework.expression.spel; +import java.io.ByteArrayOutputStream; +import java.io.PrintStream; + +import org.springframework.expression.ParseException; import org.springframework.expression.spel.ast.FormatHelper; /** @@ -38,8 +42,33 @@ public class HelperTests extends ExpressionTestCase { assertEquals("boo()",FormatHelper.formatMethodForMessage("boo")); } -// public void testReflectionHelper01() { -// ReflectionHelper.convertArguments(parameterTypes, isVarargs, converter, arguments) -//// ReflectionHelper.setupArgumentsForVarargsInvocation(;, args) -// } + public void testUtilities() throws ParseException { + SpelExpression expr = (SpelExpression)parser.parseExpression("3+4+5+6+7-2"); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + PrintStream ps = new PrintStream(baos); + SpelUtilities.printAbstractSyntaxTree(ps, expr); + ps.flush(); + String s = baos.toString(); +// ===> Expression '3+4+5+6+7-2' - AST start +// OperatorMinus value:(((((3 + 4) + 5) + 6) + 7) - 2) #children:2 +// OperatorPlus value:((((3 + 4) + 5) + 6) + 7) #children:2 +// OperatorPlus value:(((3 + 4) + 5) + 6) #children:2 +// OperatorPlus value:((3 + 4) + 5) #children:2 +// OperatorPlus value:(3 + 4) #children:2 +// CompoundExpression value:3 +// IntLiteral value:3 +// CompoundExpression value:4 +// IntLiteral value:4 +// CompoundExpression value:5 +// IntLiteral value:5 +// CompoundExpression value:6 +// IntLiteral value:6 +// CompoundExpression value:7 +// IntLiteral value:7 +// CompoundExpression value:2 +// IntLiteral value:2 +// ===> Expression '3+4+5+6+7-2' - AST end + assertTrue(s.indexOf("===> Expression '3+4+5+6+7-2' - AST start")!=-1); + assertTrue(s.indexOf(" OperatorPlus value:((((3 + 4) + 5) + 6) + 7) #children:2")!=-1); + } } diff --git a/org.springframework.expression/src/test/java/org/springframework/expression/spel/InProgressTests.java b/org.springframework.expression/src/test/java/org/springframework/expression/spel/InProgressTests.java new file mode 100644 index 00000000000..2ff063f89f6 --- /dev/null +++ b/org.springframework.expression/src/test/java/org/springframework/expression/spel/InProgressTests.java @@ -0,0 +1,395 @@ +/* + * Copyright 2002-2009 the original author or authors. + * + * Licensed 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.springframework.expression.spel; + +import java.util.ArrayList; + +import org.springframework.expression.spel.support.StandardEvaluationContext; + +/** + * These are tests for language features that are not yet considered 'live'. Either missing implementation, or documentation. + * + * Where implementation is missing the tests are commented out. + * + * @author Andy Clement + */ +public class InProgressTests extends ExpressionTestCase { + + // Constructor invocation + + // public void testPrimitiveTypeArrayConstructors() { + // evaluate("new int[]{1,2,3,4}.count()", 4, Integer.class); + // evaluate("new boolean[]{true,false,true}.count()", 3, Integer.class); + // evaluate("new char[]{'a','b','c'}.count()", 3, Integer.class); + // evaluate("new long[]{1,2,3,4,5}.count()", 5, Integer.class); + // evaluate("new short[]{2,3,4,5,6}.count()", 5, Integer.class); + // evaluate("new double[]{1d,2d,3d,4d}.count()", 4, Integer.class); + // evaluate("new float[]{1f,2f,3f,4f}.count()", 4, Integer.class); + // evaluate("new byte[]{1,2,3,4}.count()", 4, Integer.class); + // } + + // public void testPrimitiveTypeArrayConstructorsElements() { + // evaluate("new int[]{1,2,3,4}[0]", 1, Integer.class); + // evaluate("new boolean[]{true,false,true}[0]", true, Boolean.class); + // evaluate("new char[]{'a','b','c'}[0]", 'a', Character.class); + // evaluate("new long[]{1,2,3,4,5}[0]", 1L, Long.class); + // evaluate("new short[]{2,3,4,5,6}[0]", (short) 2, Short.class); + // evaluate("new double[]{1d,2d,3d,4d}[0]", (double) 1, Double.class); + // evaluate("new float[]{1f,2f,3f,4f}[0]", (float) 1, Float.class); + // evaluate("new byte[]{1,2,3,4}[0]", (byte) 1, Byte.class); + // } + + // public void testErrorCases() { + // evaluateAndCheckError("new char[7]{'a','c','d','e'}", SpelMessages.INITIALIZER_LENGTH_INCORRECT); + // evaluateAndCheckError("new char[3]{'a','c','d','e'}", SpelMessages.INITIALIZER_LENGTH_INCORRECT); + // evaluateAndCheckError("new char[2]{'hello','world'}", SpelMessages.TYPE_CONVERSION_ERROR); + // evaluateAndCheckError("new String('a','c','d')", SpelMessages.CONSTRUCTOR_NOT_FOUND); + // } + + // public void testTypeArrayConstructors() { + // evaluate("new String[]{'a','b','c','d'}[1]", "b", String.class); + // evaluateAndCheckError("new String[]{'a','b','c','d'}.size()", SpelMessages.METHOD_NOT_FOUND, 30, "size()", + // "java.lang.String[]"); + // evaluateAndCheckError("new String[]{'a','b','c','d'}.juggernaut", SpelMessages.PROPERTY_OR_FIELD_NOT_FOUND, 30, + // "juggernaut", "java.lang.String[]"); + // evaluate("new String[]{'a','b','c','d'}.length", 4, Integer.class); + // } + + // public void testMultiDimensionalArrays() { + // evaluate( + // "new String[3,4]", + // "[Ljava.lang.String;[3]{java.lang.String[4]{null,null,null,null},java.lang.String[4]{null,null,null,null},java.lang.String[4]{null,null,null,null}}" + // , + // new String[3][4].getClass()); + // } + + + // evaluate("new String(new char[]{'h','e','l','l','o'})", "hello", String.class); + + + + // public void testRelOperatorsIn01() { + // evaluate("3 in {1,2,3,4,5}", "true", Boolean.class); + // } + + // public void testRelOperatorsIn02() { + // evaluate("name in {null, \"Nikola Tesla\"}", "true", Boolean.class); + // evaluate("name in {null, \"Anonymous\"}", "false", Boolean.class); + // } + // + public void testRelOperatorsBetween01() { + evaluate("1 between listOneFive", "true", Boolean.class); + // evaluate("1 between {1, 5}", "true", Boolean.class); // no inline list building at the moment + } + // + // public void testRelOperatorsBetween02() { + // evaluate("'efg' between {'abc', 'xyz'}", "true", Boolean.class); + // } + // + public void testRelOperatorsBetweenErrors01() { + evaluateAndCheckError("1 between T(String)", SpelMessages.BETWEEN_RIGHT_OPERAND_MUST_BE_TWO_ELEMENT_LIST, 12); + } + // + // public void testRelOperatorsBetweenErrors02() { + // evaluateAndCheckError("'abc' between {5,7}", SpelMessages.NOT_COMPARABLE, 6); + // } + public void testRelOperatorsBetweenErrors03() { + evaluateAndCheckError("1 between listOfNumbersUpToTen", SpelMessages.BETWEEN_RIGHT_OPERAND_MUST_BE_TWO_ELEMENT_LIST, 10); + } + + // Lambda calculations + + // + // public void testLambda02() { + // evaluate("(#max={|x,y| $x > $y ? $x : $y };true)", "true", Boolean.class); + // } + // + // public void testLambdaMax() { + // evaluate("(#max = {|x,y| $x > $y ? $x : $y }; #max(5,25))", "25", Integer.class); + // } + // + // public void testLambdaFactorial01() { + // evaluate("(#fact = {|n| $n <= 1 ? 1 : $n * #fact($n-1) }; #fact(5))", "120", Integer.class); + // } + // + // public void testLambdaFactorial02() { + // evaluate("(#fact = {|n| $n <= 1 ? 1 : #fact($n-1) * $n }; #fact(5))", "120", Integer.class); + // } + // + // public void testLambdaAlphabet01() { + // evaluate("(#alpha = {|l,s| $l>'z'?$s:#alpha($l+1,$s+$l)};#alphabet={||#alpha('a','')}; #alphabet())", + // "abcdefghijklmnopqrstuvwxyz", String.class); + // } + // + // public void testLambdaAlphabet02() { + // evaluate("(#alphabet = {|l,s| $l>'z'?$s:#alphabet($l+1,$s+$l)};#alphabet('a',''))", + // "abcdefghijklmnopqrstuvwxyz", String.class); + // } + // + // public void testLambdaDelegation01() { + // evaluate("(#sqrt={|n| T(Math).sqrt($n)};#delegate={|f,n| $f($n)};#delegate(#sqrt,4))", "2.0", Double.class); + // } + // + // public void testVariableReferences() { + // evaluate("(#answer=42;#answer)", "42", Integer.class, true); + // evaluate("($answer=42;$answer)", "42", Integer.class, true); + // } +// +// +// public void testRelOperatorsIs02() { + // evaluate("{1, 2, 3, 4, 5} instanceof T(List)", "true", Boolean.class); + // } + // + // public void testRelOperatorsIs03() { + // evaluate("{1, 2, 3, 4, 5} instanceof T(List)", "true", Boolean.class); + // } +// + // inline list creation + // public void testInlineListCreation01() { + // evaluate("{1, 2, 3, 4, 5}", "[1, 2, 3, 4, 5]", ArrayList.class); + // } + // + // public void testInlineListCreation02() { + // evaluate("{'abc', 'xyz'}", "[abc, xyz]", ArrayList.class); + // } + // + // // inline map creation + // public void testInlineMapCreation01() { + // evaluate("#{'key1':'Value 1', 'today':'Monday'}", "{key1=Value 1, today=Monday}", HashMap.class); + // } + // + // public void testInlineMapCreation02() { + // evaluate("#{1:'January', 2:'February', 3:'March'}.size()", 3, Integer.class);// "{2=February, 1=January, + // // 3=March}", HashMap.class); + // } + // + // public void testInlineMapCreation03() { + // evaluate("#{'key1':'Value 1', 'today':'Monday'}['key1']", "Value 1", String.class); + // } + // + // public void testInlineMapCreation04() { + // evaluate("#{1:'January', 2:'February', 3:'March'}[3]", "March", String.class); + // } + // + // public void testInlineMapCreation05() { + // evaluate("#{1:'January', 2:'February', 3:'March'}.get(2)", "February", String.class); + // } + // + // // set construction + // public void testSetConstruction01() { + // evaluate("new HashSet().addAll({'a','b','c'})", "true", Boolean.class); + // } + // + // public void testConstructorInvocation02() { + // evaluate("new String[3]", "java.lang.String[3]{null,null,null}", String[].class); + // } + // + // public void testConstructorInvocation03() { + // evaluateAndCheckError("new String[]", SpelMessages.NO_SIZE_OR_INITIALIZER_FOR_ARRAY_CONSTRUCTION, 4); + // } + // + // public void testConstructorInvocation04() { + // evaluateAndCheckError("new String[3]{'abc',3,'def'}", SpelMessages.INCORRECT_ELEMENT_TYPE_FOR_ARRAY, 4); + // } + // // array construction + // public void testArrayConstruction01() { + // evaluate("new int[] {1, 2, 3, 4, 5}", "int[5]{1,2,3,4,5}", int[].class); + // } + // public void testArrayConstruction02() { + // evaluate("new String[] {'abc', 'xyz'}", "java.lang.String[2]{abc,xyz}", String[].class); + // } + + // collection processors + // from spring.net: count,sum,max,min,average,sort,orderBy,distinct,nonNull + // public void testProcessorsCount01() { + // evaluate("new String[] {'abc','def','xyz'}.count()", "3", Integer.class); + // } + // + // public void testProcessorsCount02() { + // evaluate("new int[] {1,2,3}.count()", "3", Integer.class); + // } + // + // public void testProcessorsMax01() { + // evaluate("new int[] {1,2,3}.max()", "3", Integer.class); + // } + // + // public void testProcessorsMin01() { + // evaluate("new int[] {1,2,3}.min()", "1", Integer.class); + // } + // + // public void testProcessorsKeys01() { + // evaluate("#{1:'January', 2:'February', 3:'March'}.keySet().sort()", "[1, 2, 3]", ArrayList.class); + // } + // + // public void testProcessorsValues01() { + // evaluate("#{1:'January', 2:'February', 3:'March'}.values().sort()", "[February, January, March]", + // ArrayList.class); + // } + // + // public void testProcessorsAverage01() { + // evaluate("new int[] {1,2,3}.average()", "2", Integer.class); + // } + // + // public void testProcessorsSort01() { + // evaluate("new int[] {3,2,1}.sort()", "int[3]{1,2,3}", int[].class); + // } + // + // public void testCollectionProcessorsNonNull01() { + // evaluate("{'a','b',null,'d',null}.nonnull()", "[a, b, d]", ArrayList.class); + // } + // + // public void testCollectionProcessorsDistinct01() { + // evaluate("{'a','b','a','d','e'}.distinct()", "[a, b, d, e]", ArrayList.class); + // } + // + // public void testProjection03() { + // evaluate("{1,2,3,4,5,6,7,8,9,10}.!{#this>5}", + // "[false, false, false, false, false, true, true, true, true, true]", ArrayList.class); + // } + // + // public void testProjection04() { + // evaluate("{1,2,3,4,5,6,7,8,9,10}.!{$index>5?'y':'n'}", "[n, n, n, n, n, n, y, y, y, y]", ArrayList.class); + // } + // Bean references + // public void testReferences01() { + // evaluate("@(apple).name", "Apple", String.class, true); + // } + // + // public void testReferences02() { + // evaluate("@(fruits:banana).name", "Banana", String.class, true); + // } + // + // public void testReferences03() { + // evaluate("@(a.b.c)", null, null); + // } // null - no context, a.b.c treated as name + // + // public void testReferences05() { + // evaluate("@(a/b/c:orange).name", "Orange", String.class, true); + // } + // + // public void testReferences06() { + // evaluate("@(apple).color.getRGB() == T(java.awt.Color).green.getRGB()", "true", Boolean.class); + // } + // + // public void testReferences07() { + // evaluate("@(apple).color.getRGB().equals(T(java.awt.Color).green.getRGB())", "true", Boolean.class); + // } + // + // value is not public, it is accessed through getRGB() + // public void testStaticRef01() { + // evaluate("T(Color).green.value!=0", "true", Boolean.class); + // } + // Indexer + // public void testCutProcessor01() { + // evaluate("{1,2,3,4,5}.cut(1,3)", "[2, 3, 4]", ArrayList.class); + // } + // + // public void testCutProcessor02() { + // evaluate("{1,2,3,4,5}.cut(3,1)", "[4, 3, 2]", ArrayList.class); + // } + // Ternary operator + // public void testTernaryOperator01() { + // evaluate("{1}.#isEven(#this[0]) == 'y'?'it is even':'it is odd'", "it is odd", String.class); + // } + // + // public void testTernaryOperator02() { + // evaluate("{2}.#isEven(#this[0]) == 'y'?'it is even':'it is odd'", "it is even", String.class); + // } + // public void testSelectionUsingIndex() { + // evaluate("{1,2,3,4,5,6,7,8,9,10}.?{$index > 5 }", "[7, 8, 9, 10]", ArrayList.class); + // } + //public void testSelection01() { + // inline list creation not supported: + // evaluate("{1,2,3,4,5,6,7,8,9,10}.?{#isEven(#this) == 'y'}", "[2, 4, 6, 8, 10]", ArrayList.class); + //} + + // projection and selection + public void testProjection01() { + evaluate("listOfNumbersUpToTen.!{#this<5?'y':'n'}","[y, y, y, y, n, n, n, n, n, n]",ArrayList.class); + // inline list creation not supported at the moment + // evaluate("{1,2,3,4,5,6,7,8,9,10}.!{#isEven(#this)}", "[n, y, n, y, n, y, n, y, n, y]", ArrayList.class); + } + + public void testProjection02() { + // inline map creation not supported at the moment + // evaluate("#{'a':'y','b':'n','c':'y'}.!{value=='y'?key:null}.nonnull().sort()", "[a, c]", ArrayList.class); + evaluate("mapOfNumbersUpToTen.!{key>5?value:null}", "[null, null, null, null, null, six, seven, eight, nine, ten]", ArrayList.class); + } + + public void testProjection05() { + evaluateAndCheckError("'abc'.!{true}", SpelMessages.PROJECTION_NOT_SUPPORTED_ON_TYPE); + } + + public void testProjection06() throws Exception { + SpelExpression expr = (SpelExpression)parser.parseExpression("'abc'.!{true}"); + assertEquals("'abc'.!{true}",expr.toStringAST()); + assertFalse(expr.isWritable(new StandardEvaluationContext())); + } + + public void testSelection02() { + evaluate("testMap.keySet().?{#this matches '.*o.*'}", "[monday]", ArrayList.class); + evaluate("testMap.keySet().?{#this matches '.*r.*'}.contains('saturday')", "true", Boolean.class); + evaluate("testMap.keySet().?{#this matches '.*r.*'}.size()", "3", Integer.class); + } + + public void testSelectionError_NonBooleanSelectionCriteria() { + evaluateAndCheckError("listOfNumbersUpToTen.?{'nonboolean'}", + SpelMessages.RESULT_OF_SELECTION_CRITERIA_IS_NOT_BOOLEAN); + } + + public void testSelection03() { + evaluate("mapOfNumbersUpToTen.?{key>5}.size()", "5", Integer.class); + } + + public void testSelection04() { + evaluateAndCheckError("mapOfNumbersUpToTen.?{'hello'}.size()",SpelMessages.RESULT_OF_SELECTION_CRITERIA_IS_NOT_BOOLEAN); + } + + public void testSelectionFirst01() { + evaluate("listOfNumbersUpToTen.^{#isEven(#this) == 'y'}", "2", Integer.class); + } + + public void testSelectionFirst02() { + evaluate("mapOfNumbersUpToTen.^{key>5}.size()", "1", Integer.class); + } + + public void testSelectionLast01() { + evaluate("listOfNumbersUpToTen.${#isEven(#this) == 'y'}", "10", Integer.class); + } + + public void testSelectionLast02() { + evaluate("mapOfNumbersUpToTen.${key>5}.size()", "1", Integer.class); + } + + public void testSelectionAST() throws Exception { + SpelExpression expr = (SpelExpression)parser.parseExpression("'abc'.^{true}"); + assertEquals("'abc'.^{true}",expr.toStringAST()); + assertFalse(expr.isWritable(new StandardEvaluationContext())); + expr = (SpelExpression)parser.parseExpression("'abc'.?{true}"); + assertEquals("'abc'.?{true}",expr.toStringAST()); + assertFalse(expr.isWritable(new StandardEvaluationContext())); + expr = (SpelExpression)parser.parseExpression("'abc'.${true}"); + assertEquals("'abc'.${true}",expr.toStringAST()); + assertFalse(expr.isWritable(new StandardEvaluationContext())); + } + + + + + + + +} diff --git a/org.springframework.expression/src/test/java/org/springframework/expression/spel/SetValueTests.java b/org.springframework.expression/src/test/java/org/springframework/expression/spel/SetValueTests.java index b915a0b6226..1eda33ddd7b 100644 --- a/org.springframework.expression/src/test/java/org/springframework/expression/spel/SetValueTests.java +++ b/org.springframework.expression/src/test/java/org/springframework/expression/spel/SetValueTests.java @@ -26,6 +26,7 @@ import org.springframework.expression.ParseException; import org.springframework.expression.spel.support.StandardEvaluationContext; import org.springframework.expression.spel.testresources.PlaceOfBirth; + /** * Tests set value expressions. * diff --git a/org.springframework.expression/src/test/java/org/springframework/expression/spel/StandardTypeLocatorTests.java b/org.springframework.expression/src/test/java/org/springframework/expression/spel/StandardTypeLocatorTests.java new file mode 100644 index 00000000000..aaab9a9619f --- /dev/null +++ b/org.springframework.expression/src/test/java/org/springframework/expression/spel/StandardTypeLocatorTests.java @@ -0,0 +1,55 @@ +/* + * Copyright 2004-2008 the original author or authors. + * + * Licensed 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.springframework.expression.spel; + +import java.util.List; + +import junit.framework.TestCase; + +import org.springframework.expression.EvaluationException; +import org.springframework.expression.spel.support.StandardTypeLocator; + +/** + * Unit tests for type comparison + * + * @author Andy Clement + */ +public class StandardTypeLocatorTests extends TestCase { + + public void testPrimitives() throws EvaluationException { + StandardTypeLocator locator = new StandardTypeLocator(); + assertEquals(Integer.class,locator.findType("java.lang.Integer")); + assertEquals(String.class,locator.findType("java.lang.String")); + + List prefixes = locator.getImportPrefixes(); + assertEquals(2,prefixes.size()); + assertTrue(prefixes.contains("java.lang")); + assertTrue(prefixes.contains("java.util")); + + assertEquals(Boolean.class,locator.findType("Boolean")); + assertEquals(java.util.List.class,locator.findType("List")); + + try { + locator.findType("URL"); + fail("Should have failed"); + } catch (EvaluationException ee) { + SpelException sEx = (SpelException)ee; + assertEquals(SpelMessages.TYPE_NOT_FOUND,sEx.getMessageUnformatted()); + } + locator.registerImport("java.net"); + assertEquals(java.net.URL.class,locator.findType("URL")); + } +} diff --git a/org.springframework.expression/src/test/java/org/springframework/expression/spel/TemplateExpressionParsingTests.java b/org.springframework.expression/src/test/java/org/springframework/expression/spel/TemplateExpressionParsingTests.java index e61c1d5b8cf..2b3fe300902 100644 --- a/org.springframework.expression/src/test/java/org/springframework/expression/spel/TemplateExpressionParsingTests.java +++ b/org.springframework.expression/src/test/java/org/springframework/expression/spel/TemplateExpressionParsingTests.java @@ -18,6 +18,7 @@ package org.springframework.expression.spel; import org.springframework.expression.EvaluationContext; import org.springframework.expression.Expression; +import org.springframework.expression.ParseException; import org.springframework.expression.ParserContext; import org.springframework.expression.spel.antlr.SpelAntlrExpressionParser; import org.springframework.expression.spel.support.StandardEvaluationContext; @@ -63,6 +64,21 @@ public class TemplateExpressionParsingTests extends ExpressionTestCase { assertEquals("The quick brown fox jumped over the lazy dog", o.toString()); } + public void testParsingSimpleTemplateExpression04() throws Exception { + SpelAntlrExpressionParser parser = new SpelAntlrExpressionParser(); + Expression expr = parser.parseExpression("${'hello'} world", DEFAULT_TEMPLATE_PARSER_CONTEXT); + Object o = expr.getValue(); + assertEquals("hello world", o.toString()); + + expr = parser.parseExpression("", DEFAULT_TEMPLATE_PARSER_CONTEXT); + o = expr.getValue(); + assertEquals("", o.toString()); + + expr = parser.parseExpression("abc", DEFAULT_TEMPLATE_PARSER_CONTEXT); + o = expr.getValue(); + assertEquals("abc", o.toString()); + } + public void testCompositeStringExpression() throws Exception { SpelAntlrExpressionParser parser = new SpelAntlrExpressionParser(); Expression ex = parser.parseExpression("hello ${'world'}", DEFAULT_TEMPLATE_PARSER_CONTEXT); @@ -74,6 +90,36 @@ public class TemplateExpressionParsingTests extends ExpressionTestCase { assertEquals("hello ${'world'}", ex.getExpressionString()); assertFalse(ex.isWritable(new StandardEvaluationContext())); } + + public void testParsingNormalExpressionThroughTemplateParser() throws Exception { + Expression expr = parser.parseExpression("1+2+3"); + assertEquals(6,expr.getValue()); + expr = parser.parseExpression("1+2+3",null); + assertEquals(6,expr.getValue()); + } + + public void testErrorCases() throws Exception { + try { + parser.parseExpression("hello ${'world'", DEFAULT_TEMPLATE_PARSER_CONTEXT); + fail("Should have failed"); + } catch (ParseException pe) { + assertEquals("No ending suffix '}' for expression starting at character 6: ${'world'",pe.getMessage()); + assertEquals("hello ${'world'",pe.getExpressionString()); + } + try { + parser.parseExpression("hello ${'wibble'${'world'}", DEFAULT_TEMPLATE_PARSER_CONTEXT); + fail("Should have failed"); + } catch (ParseException pe) { + assertEquals("No ending suffix '}' for expression starting at character 6: ${'wibble'${'world'}",pe.getMessage()); + } + try { + parser.parseExpression("hello ${} world", DEFAULT_TEMPLATE_PARSER_CONTEXT); + fail("Should have failed"); + } catch (ParseException pe) { + assertEquals("No expression defined within delimiter '${}' at character 6",pe.getMessage()); + } + + } private void checkString(String expectedString, Object value) { if (!(value instanceof String)) {