diff --git a/spring-binding/src/main/java/org/springframework/binding/convert/support/ConversionServiceAwareConverter.java b/spring-binding/src/main/java/org/springframework/binding/convert/support/ConversionServiceAwareConverter.java index 1c5bc8b4..fa56d37f 100644 --- a/spring-binding/src/main/java/org/springframework/binding/convert/support/ConversionServiceAwareConverter.java +++ b/spring-binding/src/main/java/org/springframework/binding/convert/support/ConversionServiceAwareConverter.java @@ -32,7 +32,7 @@ public abstract class ConversionServiceAwareConverter extends AbstractConverter private ConversionService conversionService; /** - * Default constructor, expectes to conversion service to be injected using + * Default constructor, expects to conversion service to be injected using * {@link #setConversionService(ConversionService)}. */ protected ConversionServiceAwareConverter() { diff --git a/spring-binding/src/main/java/org/springframework/binding/expression/ExpressionParser.java b/spring-binding/src/main/java/org/springframework/binding/expression/ExpressionParser.java index 3e785009..44477d27 100644 --- a/spring-binding/src/main/java/org/springframework/binding/expression/ExpressionParser.java +++ b/spring-binding/src/main/java/org/springframework/binding/expression/ExpressionParser.java @@ -27,6 +27,8 @@ public interface ExpressionParser { * Is the provided expression string an "eval" expression: meaning an expression that validates to a dynamic value, * and not a literal expression? "Eval" expressions are normally enclosed in delimiters like #{}, where literal * expressions are not delimited. + * + * TODO - candidate for removal in a future milestone: is this really needed? * @param string the string * @return true if the expression is an eval expression string, false otherwise. */ @@ -37,6 +39,8 @@ public interface ExpressionParser { * against a target object. For example, the raw expression string "person.id" might become #{person.id}. If the * string is already an eval expression string, the string argument is returned unchanged. If the string is an * composite expression string that mixes eval and literal expressions, a parser exception is thrown. + * + * TODO - candidate for removal in a future milestone: is this really needed? * @param string the raw string to be transformed into a parseable eval expression string * @return the eval expression spring * @throws ParserException an exception occurred during parsing @@ -47,7 +51,7 @@ public interface ExpressionParser { * Parse the provided expression string, returning an expression evaluator capable of evaluating it. The expression * string may be a literal expression string like "foo", an eval-expression string like #{foo}, or a * composite-expression string like "foo#{foo}bar#{bar}". - * @param expressionString the parseable expression string + * @param expressionString the parseable expression string; cannot be null * @param expressionTargetType the class of target object this expression can successfully evaluate; for example, * Map.class for an expression that is expected to evaluate against Maps. * @param expectedEvaluationResultType the class of object this expression is expected to return or set: for diff --git a/spring-binding/src/main/java/org/springframework/binding/expression/support/AbstractExpressionParser.java b/spring-binding/src/main/java/org/springframework/binding/expression/support/AbstractExpressionParser.java index 5dca7269..f2e20907 100644 --- a/spring-binding/src/main/java/org/springframework/binding/expression/support/AbstractExpressionParser.java +++ b/spring-binding/src/main/java/org/springframework/binding/expression/support/AbstractExpressionParser.java @@ -22,7 +22,7 @@ import org.springframework.binding.expression.Expression; import org.springframework.binding.expression.ExpressionParser; import org.springframework.binding.expression.ExpressionVariable; import org.springframework.binding.expression.ParserException; -import org.springframework.util.StringUtils; +import org.springframework.util.Assert; /** * Abstract base class for expression parsers. @@ -90,6 +90,7 @@ public abstract class AbstractExpressionParser implements ExpressionParser { public Expression parseExpression(String expressionString, Class expressionTargetType, Class expectedEvaluationResultType, ExpressionVariable[] expressionVariables) throws ParserException { + Assert.notNull(expressionString, "The expression string to parse is required"); // TODO variables Expression[] expressions = parseExpressions(expressionString); if (expressions.length == 1) { @@ -118,52 +119,42 @@ public abstract class AbstractExpressionParser implements ExpressionParser { */ private Expression[] parseExpressions(String expressionString) throws ParserException { List expressions = new LinkedList(); - if (StringUtils.hasText(expressionString)) { - int startIdx = 0; - while (startIdx < expressionString.length()) { - int prefixIndex = expressionString.indexOf(getExpressionPrefix(), startIdx); - if (prefixIndex >= startIdx) { - // an expression was found - if (prefixIndex > startIdx) { - expressions.add(new StaticExpression(expressionString.substring(startIdx, prefixIndex))); - startIdx = prefixIndex; - } - int nextPrefixIndex = expressionString.indexOf(getExpressionPrefix(), prefixIndex - + getExpressionPrefix().length()); - int suffixIndex; - if (nextPrefixIndex == -1) { - // this is the last expression in the expression string - suffixIndex = expressionString.lastIndexOf(getExpressionSuffix()); - } else { - // another expression exists after this one in the expression string - suffixIndex = expressionString.lastIndexOf(getExpressionSuffix(), nextPrefixIndex); - } - if (suffixIndex < (prefixIndex + getExpressionPrefix().length())) { - throw new ParserException(expressionString, "No ending suffix '" + getExpressionSuffix() - + "' for expression starting at character " + prefixIndex + ": " - + expressionString.substring(prefixIndex), null); - } else if (suffixIndex == prefixIndex + getExpressionPrefix().length()) { - throw new ParserException(expressionString, "No expression defined within delimiter '" - + getExpressionPrefix() + getExpressionSuffix() + "' at character " + prefixIndex, null); - } else { - String expr = expressionString.substring(prefixIndex + getExpressionPrefix().length(), - suffixIndex); - expressions.add(doParseExpression(expr)); - startIdx = suffixIndex + 1; - } - } else { - if (startIdx == 0) { - // treat entire string as one expression - expressions.add(doParseExpression(expressionString)); - } else { - // no more ${expressions} found in string - expressions.add(new StaticExpression(expressionString.substring(startIdx))); - } - startIdx = expressionString.length(); + int startIdx = 0; + while (startIdx < expressionString.length()) { + int prefixIndex = expressionString.indexOf(getExpressionPrefix(), startIdx); + if (prefixIndex >= startIdx) { + // an expression was found + if (prefixIndex > startIdx) { + expressions.add(new StaticExpression(expressionString.substring(startIdx, prefixIndex))); + startIdx = prefixIndex; } + int nextPrefixIndex = expressionString.indexOf(getExpressionPrefix(), prefixIndex + + getExpressionPrefix().length()); + int suffixIndex; + if (nextPrefixIndex == -1) { + // this is the last expression in the expression string + suffixIndex = expressionString.lastIndexOf(getExpressionSuffix()); + } else { + // another expression exists after this one in the expression string + suffixIndex = expressionString.lastIndexOf(getExpressionSuffix(), nextPrefixIndex); + } + if (suffixIndex < (prefixIndex + getExpressionPrefix().length())) { + throw new ParserException(expressionString, "No ending suffix '" + getExpressionSuffix() + + "' for expression starting at character " + prefixIndex + ": " + + expressionString.substring(prefixIndex), null); + } else if (suffixIndex == prefixIndex + getExpressionPrefix().length()) { + throw new ParserException(expressionString, "No expression defined within delimiter '" + + getExpressionPrefix() + getExpressionSuffix() + "' at character " + prefixIndex, null); + } else { + String expr = expressionString.substring(prefixIndex + getExpressionPrefix().length(), suffixIndex); + expressions.add(doParseExpression(expr)); + startIdx = suffixIndex + 1; + } + } else { + // no more evaluatable ${expressions} found in string + expressions.add(new StaticExpression(expressionString.substring(startIdx))); + startIdx = expressionString.length(); } - } else { - expressions.add(new StaticExpression(expressionString)); } return (Expression[]) expressions.toArray(new Expression[expressions.size()]); } diff --git a/spring-binding/src/main/java/org/springframework/binding/method/TextToMethodSignature.java b/spring-binding/src/main/java/org/springframework/binding/method/TextToMethodSignature.java deleted file mode 100644 index 2cf85125..00000000 --- a/spring-binding/src/main/java/org/springframework/binding/method/TextToMethodSignature.java +++ /dev/null @@ -1,143 +0,0 @@ -/* - * Copyright 2004-2007 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.binding.method; - -import java.util.LinkedList; -import java.util.List; - -import org.springframework.binding.convert.ConversionContext; -import org.springframework.binding.convert.ConversionException; -import org.springframework.binding.convert.ConversionService; -import org.springframework.binding.convert.support.ConversionServiceAwareConverter; -import org.springframework.binding.expression.Expression; - -/** - * Converter that takes an encoded string representation and produces a corresponding MethodSignature - * object. - *

- * This converter supports the following encoded forms: - *

- * - * @see MethodSignature - * - * @author Keith Donald - * @author Erwin Vervaet - */ -public class TextToMethodSignature extends ConversionServiceAwareConverter { - - /** - * Create a new converter that converts strings to MethodSignature objects. - */ - public TextToMethodSignature() { - } - - /** - * Create a new converter that converts strings to MethodSignature objects. - * @param conversionService the conversion service to use - */ - public TextToMethodSignature(ConversionService conversionService) { - super(conversionService); - } - - public Class[] getSourceClasses() { - return new Class[] { String.class }; - } - - public Class[] getTargetClasses() { - return new Class[] { MethodSignature.class }; - } - - protected Object doConvert(Object source, Class targetClass, ConversionContext context) throws Exception { - String encodedMethodSignature = (String) source; - encodedMethodSignature = encodedMethodSignature.trim(); - int openParan = encodedMethodSignature.indexOf('('); - if (openParan == -1) { - // form "foo" - return new MethodSignature(encodedMethodSignature); - } else { - // form "foo(...)" - String methodName = encodedMethodSignature.substring(0, openParan); - int closeParan = encodedMethodSignature.lastIndexOf(')'); - if (closeParan != (encodedMethodSignature.length() - 1)) { - throw new ConversionException(encodedMethodSignature, MethodSignature.class, - "Syntax error: No close parenthesis specified for method parameter list", null); - } - String delimitedParams = encodedMethodSignature.substring(openParan + 1, closeParan); - String[] paramArray = splitParameters(encodedMethodSignature, delimitedParams); - Parameters params = new Parameters(paramArray.length); - for (int i = 0; i < paramArray.length; i++) { - // param could be of the form "type name", "name", "type ${name}" or "${name}" - String param = paramArray[i].trim(); - int space = param.indexOf(' '); - int expr = param.indexOf('{'); - if (space == -1 || (expr != -1 && space > expr)) { - // "name" or "${name}" - params.add(new Parameter(null, parseExpression(param))); - } else { - // "type name" or "type ${name}" - Class type = (Class) fromStringTo(Class.class).execute(param.substring(0, space).trim()); - Expression name = parseExpression(param.substring(space + 1).trim()); - params.add(new Parameter(type, name)); - } - } - return new MethodSignature(methodName, params); - } - } - - /** - * Split given parameter string into individual parameter definitions. - */ - private String[] splitParameters(String encodedMethodSignature, String parameters) { - List res = new LinkedList(); - - int paramStart = 0; - int blockNestingCount = 0; - for (int i = 0; i < parameters.length(); i++) { - switch (parameters.charAt(i)) { - case '{': - blockNestingCount++; - break; - case '}': - blockNestingCount--; - break; - case ',': - if (blockNestingCount == 0) { - // only take comma delimiter into account when not inside - // a block - res.add(parameters.substring(paramStart, i)); - paramStart = i + 1; - } - break; - } - } - if (blockNestingCount != 0) { - throw new ConversionException(encodedMethodSignature, MethodSignature.class, - "Syntax error: Curly braces do not match", null); - } - if (paramStart < parameters.length()) { - res.add(parameters.substring(paramStart)); - } - - return (String[]) res.toArray(new String[res.size()]); - } -} \ No newline at end of file diff --git a/spring-binding/src/test/java/org/springframework/binding/expression/ognl/OgnlExpressionParserTests.java b/spring-binding/src/test/java/org/springframework/binding/expression/ognl/OgnlExpressionParserTests.java index bd0220c5..150bd611 100644 --- a/spring-binding/src/test/java/org/springframework/binding/expression/ognl/OgnlExpressionParserTests.java +++ b/spring-binding/src/test/java/org/springframework/binding/expression/ognl/OgnlExpressionParserTests.java @@ -29,7 +29,7 @@ public class OgnlExpressionParserTests extends TestCase { private TestBean bean = new TestBean(); - public void testParseSimpleDelimited() { + public void testParseSimple() { String exp = "${flag}"; Expression e = parser.parseExpression(exp, null, null, null); assertNotNull(e); @@ -37,20 +37,6 @@ public class OgnlExpressionParserTests extends TestCase { assertFalse(b.booleanValue()); } - public void testParseSimple() { - String exp = "flag"; - Expression e = parser.parseExpression(exp, null, null, null); - assertNotNull(e); - Boolean b = (Boolean) e.getValue(bean); - assertFalse(b.booleanValue()); - } - - public void testParseNull() { - Expression e = parser.parseExpression(null, null, null, null); - assertNotNull(e); - assertNull(e.getValue(bean)); - } - public void testParseEmpty() { Expression e = parser.parseExpression("", null, null, null); assertNotNull(e); diff --git a/spring-binding/src/test/java/org/springframework/binding/mapping/RequiredMappingTests.java b/spring-binding/src/test/java/org/springframework/binding/mapping/RequiredMappingTests.java index 5d3cb573..242ea875 100644 --- a/spring-binding/src/test/java/org/springframework/binding/mapping/RequiredMappingTests.java +++ b/spring-binding/src/test/java/org/springframework/binding/mapping/RequiredMappingTests.java @@ -28,7 +28,7 @@ public class RequiredMappingTests extends TestCase { public void testRequired() { MappingBuilder builder = new MappingBuilder(new OgnlExpressionParser()); - Mapping mapping = builder.source("foo").target("bar").required().value(); + Mapping mapping = builder.source("${foo}").target("${bar}").required().value(); HashMap source = new HashMap(); source.put("foo", "baz"); HashMap target = new HashMap(); @@ -38,7 +38,7 @@ public class RequiredMappingTests extends TestCase { public void testRequiredExceptionOnNull() { MappingBuilder builder = new MappingBuilder(new OgnlExpressionParser()); - Mapping mapping = builder.source("foo").target("bar").required().value(); + Mapping mapping = builder.source("${foo}").target("${bar}").required().value(); HashMap source = new HashMap(); source.put("foo", null); HashMap target = new HashMap(); @@ -50,7 +50,7 @@ public class RequiredMappingTests extends TestCase { public void testRequiredExceptionOnNoKey() { MappingBuilder builder = new MappingBuilder(new OgnlExpressionParser()); - Mapping mapping = builder.source("foo").target("bar").required().value(); + Mapping mapping = builder.source("${foo}").target("${bar}").required().value(); HashMap source = new HashMap(); HashMap target = new HashMap(); try { diff --git a/spring-binding/src/test/java/org/springframework/binding/method/TextToMethodSignatureTests.java b/spring-binding/src/test/java/org/springframework/binding/method/TextToMethodSignatureTests.java deleted file mode 100644 index 76764291..00000000 --- a/spring-binding/src/test/java/org/springframework/binding/method/TextToMethodSignatureTests.java +++ /dev/null @@ -1,138 +0,0 @@ -/* - * Copyright 2004-2007 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.binding.method; - -import junit.framework.TestCase; - -import org.springframework.binding.convert.ConversionException; -import org.springframework.binding.convert.support.DefaultConversionService; -import org.springframework.binding.convert.support.TextToExpression; -import org.springframework.binding.expression.ognl.OgnlExpressionParser; - -/** - * Test case for {@link TextToMethodSignature}. - * - * @author Erwin Vervaet - */ -public class TextToMethodSignatureTests extends TestCase { - - private TextToMethodSignature converter; - - protected void setUp() throws Exception { - DefaultConversionService conversionService = new DefaultConversionService(); - conversionService.addConverter(new TextToExpression(new OgnlExpressionParser())); - converter = new TextToMethodSignature(conversionService); - } - - public void testParseNoArguments() { - MethodSignature signature = (MethodSignature) converter.convert("foo"); - assertEquals("foo", signature.getMethodName()); - assertEquals(0, signature.getParameters().size()); - - signature = (MethodSignature) converter.convert("foo()"); - assertEquals("foo", signature.getMethodName()); - assertEquals(0, signature.getParameters().size()); - } - - public void testSingleArgument() { - MethodSignature signature = (MethodSignature) converter.convert("foo(${flowScope.bar})"); - assertEquals("foo", signature.getMethodName()); - assertEquals(1, signature.getParameters().size()); - assertNull(signature.getParameters().getParameter(0).getType()); - assertEquals("flowScope.bar", signature.getParameters().getParameter(0).getName().toString()); - - signature = (MethodSignature) converter.convert("foo(${'Foo' + flowScope.bar})"); - assertEquals("foo", signature.getMethodName()); - assertEquals(1, signature.getParameters().size()); - assertEquals("\"Foo\" + flowScope.bar", signature.getParameters().getParameter(0).getName().toString()); - } - - public void testSingleArgumentWithType() { - MethodSignature signature = (MethodSignature) converter.convert("foo(java.lang.String ${flowScope.bar})"); - assertEquals("foo", signature.getMethodName()); - assertEquals(1, signature.getParameters().size()); - assertEquals(String.class, signature.getParameters().getParameter(0).getType()); - assertEquals("flowScope.bar", signature.getParameters().getParameter(0).getName().toString()); - - signature = (MethodSignature) converter.convert("foo(long ${flowScope.bar})"); - assertEquals("foo", signature.getMethodName()); - assertEquals(1, signature.getParameters().size()); - assertEquals(Long.class, signature.getParameters().getParameter(0).getType()); - assertEquals("flowScope.bar", signature.getParameters().getParameter(0).getName().toString()); - } - - public void testMultipleArguments() { - MethodSignature signature = (MethodSignature) converter - .convert("foo(${flowScope.bar}, ${externalContext.requestParameterMap.test})"); - assertEquals("foo", signature.getMethodName()); - assertEquals(2, signature.getParameters().size()); - assertNull(signature.getParameters().getParameter(0).getType()); - assertEquals("flowScope.bar", signature.getParameters().getParameter(0).getName().toString()); - assertNull(signature.getParameters().getParameter(1).getType()); - assertEquals("externalContext.requestParameterMap.test", signature.getParameters().getParameter(1).getName() - .toString()); - } - - public void testMultipleArgumentsWithType() { - MethodSignature signature = (MethodSignature) converter - .convert("foo(long ${flowScope.bar}, java.lang.String ${externalContext.requestParameterMap.test})"); - assertEquals("foo", signature.getMethodName()); - assertEquals(2, signature.getParameters().size()); - assertEquals(Long.class, signature.getParameters().getParameter(0).getType()); - assertEquals("flowScope.bar", signature.getParameters().getParameter(0).getName().toString()); - assertEquals(String.class, signature.getParameters().getParameter(1).getType()); - assertEquals("externalContext.requestParameterMap.test", signature.getParameters().getParameter(1).getName() - .toString()); - } - - public void testCollectionConstructionSyntax() { - MethodSignature signature = (MethodSignature) converter.convert("foo({1, 2, 3})"); - assertEquals("foo", signature.getMethodName()); - assertEquals(1, signature.getParameters().size()); - assertNull(signature.getParameters().getParameter(0).getType()); - assertEquals("{ 1, 2, 3 }", signature.getParameters().getParameter(0).getName().toString()); - } - - public void testCollectionConstructionSyntaxWithType() { - MethodSignature signature = (MethodSignature) converter.convert("foo(java.util.List {1, 2, 3})"); - assertEquals("foo", signature.getMethodName()); - assertEquals(1, signature.getParameters().size()); - assertEquals(java.util.List.class, signature.getParameters().getParameter(0).getType()); - assertEquals("{ 1, 2, 3 }", signature.getParameters().getParameter(0).getName().toString()); - - signature = (MethodSignature) converter.convert("foo(a${b,#{1:2},e}f${g,#{3:4},j}k)"); - } - - public void testSyntaxErrors() { - try { - converter.convert("foo("); - fail(); - } catch (ConversionException e) { - } - - try { - converter.convert("foo(long 1, ${bar()}"); - fail(); - } catch (ConversionException e) { - } - - try { - converter.convert("foo(long 1, {1, 2)"); - fail(); - } catch (ConversionException e) { - } - } -}