From 3e99a26b8e78cfbb7b2cfaf63a126e62e51b6231 Mon Sep 17 00:00:00 2001 From: Keith Donald Date: Mon, 14 Jan 2008 23:20:51 +0000 Subject: [PATCH] simplification of EL parsing api improvements to messages API tests --- .../binding/expression/ExpressionParser.java | 32 +-- .../expression/ExpressionVariable.java | 19 +- .../expression/el/ELExpressionParser.java | 42 +--- .../expression/ognl/OgnlExpression.java | 20 +- .../expression/ognl/OgnlExpressionParser.java | 6 +- .../support/AbstractExpressionParser.java | 31 +-- .../binding/message/MessageBuilder.java | 173 ++++++++++++++ .../binding/message/Messages.java | 225 ------------------ .../el/ELExpressionParserTests.java | 48 +++- .../DefaultMessageContextFactoryTests.java | 92 +++++++ 10 files changed, 356 insertions(+), 332 deletions(-) create mode 100644 spring-binding/src/main/java/org/springframework/binding/message/MessageBuilder.java delete mode 100644 spring-binding/src/main/java/org/springframework/binding/message/Messages.java create mode 100644 spring-binding/src/test/java/org/springframework/binding/message/DefaultMessageContextFactoryTests.java 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 44477d27..9906f075 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 @@ -24,33 +24,7 @@ package org.springframework.binding.expression; 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. - */ - public boolean isEvalExpressionString(String string); - - /** - * Parse the raw string into an "eval" expression string that when parsed produces a dynamic value when evaluated - * 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 - */ - public String parseEvalExpressionString(String string) throws ParserException; - - /** - * 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}". + * Parse the provided expression string, returning an expression evaluator capable of evaluating it. * @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. @@ -58,8 +32,8 @@ public interface ExpressionParser { * example, Boolean.class for an expression that is expected to get or set a boolean value. Typically * used to facilitate type conversion by the expression evaluator; for example, if a evaluated expression equates to * a String value 'true', with an expected Boolean result the string value could be converted to a typed Boolean - * value. - * @param expressionVariables variables providing aliases for this expression during evaluation. Optional. + * value (required). + * @param expressionVariables variables providing aliases for this expression during evaluation (optional). * @return the evaluator for the parsed expression * @throws ParserException an exception occurred during parsing */ diff --git a/spring-binding/src/main/java/org/springframework/binding/expression/ExpressionVariable.java b/spring-binding/src/main/java/org/springframework/binding/expression/ExpressionVariable.java index 65820c80..23340df3 100644 --- a/spring-binding/src/main/java/org/springframework/binding/expression/ExpressionVariable.java +++ b/spring-binding/src/main/java/org/springframework/binding/expression/ExpressionVariable.java @@ -3,28 +3,25 @@ package org.springframework.binding.expression; import org.springframework.util.Assert; /** - * A simple, convenient alias for a more-complex expression. - * - * TODO - consider making the valueExpressionString a parsed Expression object for more flexibility. - * + * An expression variable. * @author Keith Donald */ public class ExpressionVariable { private String name; - private String valueExpressionString; + private String value; /** * Creates a new expression variable * @param name the name of the variable, acting as an convenient alias - * @param valueExpressionString the complex expression to be aliased in string form + * @param value the initial value of the variable */ - public ExpressionVariable(String name, String valueExpressionString) { + public ExpressionVariable(String name, String value) { Assert.hasText(name, "The expression variable must be named"); - Assert.hasText(valueExpressionString, "The expression value expression string is required"); + Assert.hasText(value, "The expression variable value is required"); this.name = name; - this.valueExpressionString = valueExpressionString; + this.value = value; } /** @@ -39,8 +36,8 @@ public class ExpressionVariable { * Returns the expression that will be evaluated when the variable is referenced by its name in another expression. * @return the expression value. */ - public String getValueExpressionString() { - return valueExpressionString; + public String getValue() { + return value; } public boolean equals(Object o) { diff --git a/spring-binding/src/main/java/org/springframework/binding/expression/el/ELExpressionParser.java b/spring-binding/src/main/java/org/springframework/binding/expression/el/ELExpressionParser.java index e9d6af6f..e4a402b6 100644 --- a/spring-binding/src/main/java/org/springframework/binding/expression/el/ELExpressionParser.java +++ b/spring-binding/src/main/java/org/springframework/binding/expression/el/ELExpressionParser.java @@ -22,16 +22,6 @@ import org.springframework.binding.expression.ParserException; */ public class ELExpressionParser implements ExpressionParser { - /** - * The expression prefix. - */ - private static final String EXPRESSION_PREFIX = "#{"; - - /** - * The expression suffix. - */ - private static final String EXPRESSION_SUFFIX = "}"; - /** * The ExpressionFactory for constructing EL expressions */ @@ -55,40 +45,29 @@ public class ELExpressionParser implements ExpressionParser { this.contextFactories.put(expressionTargetType, contextFactory); } - public boolean isEvalExpressionString(String expressionString) { - return expressionString.startsWith(EXPRESSION_PREFIX) && expressionString.endsWith(EXPRESSION_SUFFIX); - } - - public String parseEvalExpressionString(String string) { - return encloseInDelimitersIfNecessary(string); - } - public Expression parseExpression(String expressionString, Class expressionTargetType, Class expectedEvaluationResultType, ExpressionVariable[] expressionVariables) throws ParserException { - ParserELContext context = new ParserELContext(); + if (expectedEvaluationResultType == null) { + throw new ParserException(expressionString, "The 'expectedEvaluationResultType' argument is required; " + + "specify Object.class if the type is unknown", new NullPointerException()); + } try { + ParserELContext context = new ParserELContext(); context.mapVariables(expressionVariables, expressionFactory); ValueExpression expression = expressionFactory.createValueExpression(context, expressionString, expectedEvaluationResultType); - ELContextFactory contextFactory = getContextFactory(expressionString, expressionTargetType); + ELContextFactory contextFactory = getContextFactory(expressionTargetType, expressionString); return new ELExpression(contextFactory, expression, context.getVariableMapper()); } catch (ELException ex) { throw new ParserException(expressionString, ex); } } - private String encloseInDelimitersIfNecessary(String expressionString) { - if (isEvalExpressionString(expressionString)) { - return expressionString; - } else { - return EXPRESSION_PREFIX + expressionString + EXPRESSION_SUFFIX; - } - } - - private ELContextFactory getContextFactory(String expressionString, Class expressionTargetType) { + private ELContextFactory getContextFactory(Class expressionTargetType, String expressionString) { if (!contextFactories.containsKey(expressionTargetType)) { throw new ParserException(expressionString, new IllegalArgumentException( - "No ELContextFactory registered for expressionTargetType [" + expressionTargetType + "]")); + "No ELContextFactory registered for expressionTargetType [" + expressionTargetType + "]; " + + "Please ensure a factory is registered for this type.")); } return (ELContextFactory) contextFactories.get(expressionTargetType); } @@ -113,8 +92,7 @@ public class ELExpressionParser implements ExpressionParser { variableMapper = new VariableMapperImpl(); for (int i = 0; i < variables.length; i++) { ExpressionVariable var = variables[i]; - ValueExpression expr = expressionFactory.createValueExpression(this, - var.getValueExpressionString(), Object.class); + ValueExpression expr = expressionFactory.createValueExpression(this, var.getValue(), Object.class); variableMapper.setVariable(var.getName(), expr); } } diff --git a/spring-binding/src/main/java/org/springframework/binding/expression/ognl/OgnlExpression.java b/spring-binding/src/main/java/org/springframework/binding/expression/ognl/OgnlExpression.java index 5fca7b5f..f89d7cb2 100644 --- a/spring-binding/src/main/java/org/springframework/binding/expression/ognl/OgnlExpression.java +++ b/spring-binding/src/main/java/org/springframework/binding/expression/ognl/OgnlExpression.java @@ -16,6 +16,8 @@ package org.springframework.binding.expression.ognl; import java.util.Collections; +import java.util.HashMap; +import java.util.Map; import ognl.Ognl; import ognl.OgnlException; @@ -23,6 +25,7 @@ import ognl.OgnlException; import org.springframework.binding.expression.EvaluationAttempt; import org.springframework.binding.expression.EvaluationException; import org.springframework.binding.expression.Expression; +import org.springframework.binding.expression.ExpressionVariable; import org.springframework.binding.expression.SetValueAttempt; import org.springframework.util.Assert; @@ -41,12 +44,18 @@ class OgnlExpression implements Expression { */ private Object expression; + /** + * Expression variable initial values. + */ + private ExpressionVariable[] variables; + /** * Creates a new OGNL expression. * @param expression the parsed expression */ - public OgnlExpression(Object expression) { + public OgnlExpression(Object expression, ExpressionVariable[] variables) { this.expression = expression; + this.variables = variables; } public int hashCode() { @@ -66,8 +75,13 @@ class OgnlExpression implements Expression { public Object getValue(Object target) throws EvaluationException { Assert.notNull(target, "The target object to evaluate is required"); try { - // TODO context map - return Ognl.getValue(expression, Collections.EMPTY_MAP, target); + Map context; + if (variables != null && variables.length > 0) { + context = new HashMap(variables.length); + } else { + context = Collections.EMPTY_MAP; + } + return Ognl.getValue(expression, context, target); } catch (OgnlException e) { if (e.getReason() != null && e.getReason() != e) { // unwrap the OgnlException since the actual exception is wrapped inside it diff --git a/spring-binding/src/main/java/org/springframework/binding/expression/ognl/OgnlExpressionParser.java b/spring-binding/src/main/java/org/springframework/binding/expression/ognl/OgnlExpressionParser.java index 49583209..e210bdcf 100644 --- a/spring-binding/src/main/java/org/springframework/binding/expression/ognl/OgnlExpressionParser.java +++ b/spring-binding/src/main/java/org/springframework/binding/expression/ognl/OgnlExpressionParser.java @@ -21,6 +21,7 @@ import ognl.OgnlRuntime; import ognl.PropertyAccessor; import org.springframework.binding.expression.Expression; +import org.springframework.binding.expression.ExpressionVariable; import org.springframework.binding.expression.ParserException; import org.springframework.binding.expression.support.AbstractExpressionParser; @@ -31,9 +32,10 @@ import org.springframework.binding.expression.support.AbstractExpressionParser; */ public class OgnlExpressionParser extends AbstractExpressionParser { - protected Expression doParseExpression(String expressionString) throws ParserException { + protected Expression doParseExpression(String expressionString, Class expressionTargetType, + Class expectedEvaluationResultType, ExpressionVariable[] expressionVariables) throws ParserException { try { - return new OgnlExpression(Ognl.parseExpression(expressionString)); + return new OgnlExpression(Ognl.parseExpression(expressionString), expressionVariables); } catch (OgnlException e) { throw new ParserException(expressionString, e); } 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 f2e20907..e62b9b95 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 @@ -80,19 +80,11 @@ public abstract class AbstractExpressionParser implements ExpressionParser { this.expressionSuffix = expressionSuffix; } - public boolean isEvalExpressionString(String string) { - return string.startsWith(expressionPrefix) && string.endsWith(expressionSuffix); - } - - public String parseEvalExpressionString(String string) { - return encloseInDelimitersIfNecessary(string); - } - 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); + Expression[] expressions = parseExpressions(expressionString, expressionTargetType, + expectedEvaluationResultType, expressionVariables); if (expressions.length == 1) { return expressions[0]; } else { @@ -100,14 +92,6 @@ public abstract class AbstractExpressionParser implements ExpressionParser { } } - private String encloseInDelimitersIfNecessary(String expressionString) { - if (isEvalExpressionString(expressionString)) { - return expressionString; - } else { - return expressionPrefix + expressionString + expressionSuffix; - } - } - /** * 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 @@ -117,7 +101,8 @@ public abstract class AbstractExpressionParser implements ExpressionParser { * @return the parsed expressions * @throws ParserException when the expressions cannot be parsed */ - private Expression[] parseExpressions(String expressionString) throws ParserException { + private Expression[] parseExpressions(String expressionString, Class expressionTargetType, + Class expectedEvaluationResultType, ExpressionVariable[] expressionVariables) throws ParserException { List expressions = new LinkedList(); int startIdx = 0; while (startIdx < expressionString.length()) { @@ -147,7 +132,8 @@ public abstract class AbstractExpressionParser implements ExpressionParser { + getExpressionPrefix() + getExpressionSuffix() + "' at character " + prefixIndex, null); } else { String expr = expressionString.substring(prefixIndex + getExpressionPrefix().length(), suffixIndex); - expressions.add(doParseExpression(expr)); + expressions.add(doParseExpression(expr, expressionTargetType, expectedEvaluationResultType, + expressionVariables)); startIdx = suffixIndex + 1; } } else { @@ -165,8 +151,9 @@ public abstract class AbstractExpressionParser implements ExpressionParser { * Template method for parsing a filtered expression string. Subclasses should override. * @param expressionString the expression string * @return the parsed expression - * @throws ParserException an exception occured during parsing + * @throws ParserException an exception occurred during parsing */ - protected abstract Expression doParseExpression(String expressionString) throws ParserException; + protected abstract Expression doParseExpression(String expressionString, Class expressionTargetType, + Class expectedEvaluationResultType, ExpressionVariable[] expressionVariables) throws ParserException; } \ No newline at end of file diff --git a/spring-binding/src/main/java/org/springframework/binding/message/MessageBuilder.java b/spring-binding/src/main/java/org/springframework/binding/message/MessageBuilder.java new file mode 100644 index 00000000..656e49aa --- /dev/null +++ b/spring-binding/src/main/java/org/springframework/binding/message/MessageBuilder.java @@ -0,0 +1,173 @@ +package org.springframework.binding.message; + +import java.util.Locale; + +import org.springframework.context.MessageSource; +import org.springframework.context.MessageSourceResolvable; + +/** + * A convenient builder for building {@link MessageResolver} objects programmatically. Often used by model code such as + * validation logic to conveniently record validation messages. Supports the production of message resolvers that + * hard-code their message text, as well as message resolvers that retrieve their text from a + * {@link MessageSource message resource bundle}. + * + * Usage example: + *

+ * + * new MessageBuilder().error().source(this).code("mycode").args(new Object[] { arg1, arg2 }).defaultText("Fallback text") + * .build(); + * + *

+ * @author Keith Donald + */ +public class MessageBuilder { + + private Object source; + + private String[] codes; + + private Severity severity; + + private Object[] args; + + private String defaultText; + + /** + * Records that the message being built is from the source provided. + * @param source the source generating the message + * @return this, for fluent API usage + */ + public MessageBuilder source(Object source) { + this.source = source; + return this; + } + + /** + * Records that the message being built should have its text resolved using the code provided. + * @param code the message code + * @return this, for fluent API usage + */ + public MessageBuilder code(String code) { + codes = new String[] { code }; + return this; + } + + /** + * Records that the message being built should have its text resolved using the codes provided. The codes are tried + * in-order until their is a match. + * @param codes the message codes + * @return this, for fluent API usage + */ + public MessageBuilder codes(String[] codes) { + this.codes = codes; + return this; + } + + /** + * Records that the message being built is an informational message. + * @return this, for fluent API usage + */ + public MessageBuilder info() { + severity = Severity.INFO; + return this; + } + + /** + * Records that the message being built is a warning message. + * @return this, for fluent API usage + */ + public MessageBuilder warning() { + severity = Severity.WARNING; + return this; + } + + /** + * Records that the message being built is an error message. + * @return this, for fluent API usage + */ + public MessageBuilder error() { + severity = Severity.ERROR; + return this; + } + + /** + * Records that the message being built has a single argument. + * @param arg the message argument + * @return this, for fluent API usage + */ + public MessageBuilder arg(Object arg) { + this.args = new Object[] { arg }; + return this; + } + + /** + * Records that the message being built has arguments. + * @param args the message arguments + * @return this, for fluent API usage + */ + public MessageBuilder args(Object[] args) { + this.args = args; + return this; + } + + /** + * Records the fallback text of the message being built. If the message has no codes, this will always be used as + * the text. + * @param text the default text + * @return this, for fluent API usage + */ + public MessageBuilder defaultText(String text) { + defaultText = text; + return this; + } + + /** + * Builds the message that will be resolved. Called after the end of recording builder instructions. + * @return the built message resolver + */ + public MessageResolver build() { + if (severity == null) { + severity = Severity.INFO; + } + if (codes == null && defaultText == null) { + throw new IllegalArgumentException( + "A message code or the message text is required to build this message resolver"); + } + return new BuiltMessageResolver(source, codes, severity, args, defaultText); + } + + private static class BuiltMessageResolver implements MessageResolver, MessageSourceResolvable { + private Object source; + private String[] codes; + private Severity severity; + private Object[] args; + private String defaultText; + + public BuiltMessageResolver(Object source, String[] codes, Severity severity, Object[] args, String defaultText) { + this.source = source; + this.codes = codes; + this.severity = severity; + this.args = args; + this.defaultText = defaultText; + } + + public Message resolveMessage(MessageSource messageSource, Locale locale) { + return new Message(source, messageSource.getMessage(this, locale), severity); + } + + // implementing MessageSourceResolver + + public String[] getCodes() { + return codes; + } + + public Object[] getArguments() { + return args; + } + + public String getDefaultMessage() { + return defaultText; + } + } + +} diff --git a/spring-binding/src/main/java/org/springframework/binding/message/Messages.java b/spring-binding/src/main/java/org/springframework/binding/message/Messages.java deleted file mode 100644 index ccb8c72f..00000000 --- a/spring-binding/src/main/java/org/springframework/binding/message/Messages.java +++ /dev/null @@ -1,225 +0,0 @@ -package org.springframework.binding.message; - -import java.util.Locale; - -import org.springframework.context.MessageSource; - -/** - * A convenient factory for creating {@link MessageResolver} objects programmatically. Often used by model code such as - * validation logic to conveniently record validation messages. Supports the production of "text" message resolvers that - * hard-code their message text, as well as message resolvers that retrieve their text from a - * {@link MessageSource message resource bundle}. - * - * @author Keith Donald - */ -public class Messages implements MessageResolver { - - private Object source; - - private String code; - - private Severity severity; - - private Object[] args; - - private String defaultText; - - private Messages(Object source, String code, Severity severity, Object[] args, String defaultText) { - this.source = source; - this.code = code; - this.severity = severity; - this.args = args; - this.defaultText = defaultText; - } - - public Message resolveMessage(MessageSource messageSource, Locale locale) { - if (messageSource != null && (code != null && code.length() > 0)) { - return new Message(source, getMessageText(messageSource, locale), severity); - } else { - return new Message(source, defaultText, severity); - } - } - - /** - * Creates a message resolver that creates a INFO {@link Message} with the text provided. - * @param text the raw message text that will be used as-is - * @return the message resolver - */ - public static Messages text(String text) { - return new Messages(null, null, Severity.INFO, null, text); - } - - /** - * Creates a message resolver that creates a {@link Message} with the text and severity provided. - * @param text the raw message text that will be used as-is - * @param severity the desired message severity - * @return the message resolver - */ - public static Messages text(String text, Severity severity) { - return new Messages(null, null, severity, null, text); - } - - /** - * Creates a message resolver that creates a {@link Severity#INFO info} {@link Message message} with its text - * resolved from a message bundle by using the provided message code. - * @param code the message code - * @return the message resolver - */ - public static Messages info(String code) { - return new Messages(null, code, Severity.INFO, null, null); - } - - /** - * Creates a message resolver that creates a {@link Severity#WARNING warning} {@link Message message} with its text - * resolved from a message bundle by using the provided message code. - * @param code the message code - * @return the message resolver - */ - public static Messages warning(String code) { - return new Messages(null, code, Severity.WARNING, null, null); - } - - /** - * Creates a message resolver that creates a {@link Severity#ERROR error} {@link Message message} with its text - * resolved from a message bundle by using the provided message code. - * @param code the message code - * @return the message resolver - */ - public static Messages error(String code) { - return new Messages(null, code, Severity.ERROR, null, null); - } - - /** - * Creates a message resolver that creates a {@link Severity#INFO info} {@link Message message} with its text - * resolved from a message bundle by using the provided message code and message arguments. - * @param code the message code - * @param args the message arguments - * @return the message resolver - */ - public static Messages info(String code, Object[] args) { - return new Messages(null, code, Severity.INFO, args, null); - } - - /** - * Creates a message resolver that creates a {@link Severity#WARNING warning} {@link Message message} with its text - * resolved from a message bundle by using the provided message code and message arguments. - * @param code the message code - * @param args the message arguments - * @return the message resolver - */ - public static Messages warning(String code, Object[] args) { - return new Messages(null, code, Severity.WARNING, args, null); - } - - /** - * Creates a message resolver that creates a {@link Severity#ERROR error} {@link Message message} with its text - * resolved from a message bundle by using the provided message code and message arguments. - * @param code the message code - * @param args the message arguments - * @return the message resolver - */ - public static Messages error(String code, Object[] args) { - return new Messages(null, code, Severity.ERROR, args, null); - } - - /** - * Creates a message resolver that creates a {@link Severity#INFO info} {@link Message message} from the source with - * the text provided. - * @param source the source of the message - * @param text the message text - * @return the message resolver - */ - public static Messages text(Object source, String text) { - return new Messages(source, null, Severity.INFO, null, text); - } - - /** - * Creates a message resolver that creates a {@link Message message} from the source with the text and severity - * provided. - * @param source the source of the message - * @param text the message text - * @param severity the message severity - * @return the message resolver - */ - public static Messages text(Object source, String text, Severity severity) { - return new Messages(source, null, severity, null, text); - } - - /** - * Creates a message resolver that creates a {@link Severity#INFO info} {@link Message message} from the source with - * its text resolved from a message bundle by using the provided message code. - * @param source the source of the message - * @param code the message code - * @return the message resolver - */ - public static Messages info(Object source, String code) { - return new Messages(source, code, Severity.INFO, null, null); - } - - /** - * Creates a message resolver that creates a {@link Severity#WARNING warning} {@link Message message} from the - * source with its text resolved from a message bundle by using the provided message code. - * @param source the source of the message - * @param code the message code - * @return the message resolver - */ - public static Messages warning(Object source, String code) { - return new Messages(source, code, Severity.WARNING, null, null); - } - - /** - * Creates a message resolver that creates a {@link Severity#ERROR error} {@link Message message} from the source - * with its text resolved from a message bundle by using the provided message code. - * @param source the source of the message - * @param code the message code - * @return the message resolver - */ - public static Messages error(Object source, String code) { - return new Messages(source, code, Severity.ERROR, null, null); - } - - /** - * Creates a message resolver that creates a {@link Severity#INFO info} {@link Message message} from the source with - * its text resolved from a message bundle by using the provided message code and message arguments. - * @param source the source of the message - * @param code the message code - * @param args the message arguments - * @return the message resolver - */ - public static Messages info(Object source, String code, Object[] args) { - return new Messages(source, code, Severity.INFO, args, null); - } - - /** - * Creates a message resolver that creates a {@link Severity#WARNING warning} {@link Message message} from the - * source with its text resolved from a message bundle by using the provided message code and message arguments. - * @param source the source of the message - * @param code the message code - * @param args the message arguments - * @return the message resolver - */ - public static Messages warning(Object source, String code, Object[] args) { - return new Messages(source, code, Severity.WARNING, args, null); - } - - /** - * Creates a message resolver that creates a {@link Severity#ERROR error} {@link Message message} from the source - * with its text resolved from a message bundle by using the provided message code and message arguments. - * @param source the source of the message - * @param code the message code - * @param args the message arguments - * @return the message resolver - */ - public static Messages error(Object source, String code, Object[] args) { - return new Messages(source, code, Severity.ERROR, args, null); - } - - private String getMessageText(MessageSource source, Locale locale) { - if (defaultText == null) { - return source.getMessage(code, args, locale); - } else { - return source.getMessage(code, args, defaultText, locale); - } - } - -} diff --git a/spring-binding/src/test/java/org/springframework/binding/expression/el/ELExpressionParserTests.java b/spring-binding/src/test/java/org/springframework/binding/expression/el/ELExpressionParserTests.java index a8938d41..6dfbceb6 100644 --- a/spring-binding/src/test/java/org/springframework/binding/expression/el/ELExpressionParserTests.java +++ b/spring-binding/src/test/java/org/springframework/binding/expression/el/ELExpressionParserTests.java @@ -2,6 +2,7 @@ package org.springframework.binding.expression.el; import java.util.ArrayList; import java.util.List; +import java.util.Map; import javax.el.ELContext; import javax.el.ELResolver; @@ -13,6 +14,7 @@ import junit.framework.TestCase; import org.jboss.el.ExpressionFactoryImpl; import org.springframework.binding.expression.Expression; import org.springframework.binding.expression.ExpressionVariable; +import org.springframework.binding.expression.ParserException; public class ELExpressionParserTests extends TestCase { @@ -40,6 +42,20 @@ public class ELExpressionParserTests extends TestCase { } } + public void testParseEvalExpressionExpectedResultTypeNull() { + String expressionString = "#{value}"; + Class expressionTargetType = null; + Class expectedEvaluationResultType = null; + ExpressionVariable[] expressionVariables = null; + try { + parser.parseExpression(expressionString, expressionTargetType, expectedEvaluationResultType, + expressionVariables); + fail("Should have failed"); + } catch (ParserException e) { + + } + } + public void testParseEvalExpression() { String expressionString = "#{value}"; Class expressionTargetType = TestBean.class; @@ -51,15 +67,31 @@ public class ELExpressionParserTests extends TestCase { assertEquals("foo", exp.getValue(target)); } - public void testParseLiteralExpressionStringAsEvalExpression() { - String expressionString = "value"; - Class expressionTargetType = TestBean.class; - Class expectedEvaluationResultType = String.class; + public void testParseEvalExpressionNoTargetType() { + String expressionString = "#{value}"; + Class expressionTargetType = null; + Class expectedEvaluationResultType = Object.class; ExpressionVariable[] expressionVariables = null; - Expression exp = parser.parseExpression(parser.parseEvalExpressionString(expressionString), - expressionTargetType, expectedEvaluationResultType, expressionVariables); - TestBean target = new TestBean(); - assertEquals("foo", exp.getValue(target)); + try { + parser.parseExpression(expressionString, expressionTargetType, expectedEvaluationResultType, + expressionVariables); + fail("Should have failed"); + } catch (ParserException e) { + + } + } + + public void testParseEvalExpressionNotRegisteredTargetType() { + String expressionString = "#{value}"; + Class expressionTargetType = Map.class; + Class expectedEvaluationResultType = Object.class; + ExpressionVariable[] expressionVariables = null; + try { + parser.parseExpression(expressionString, expressionTargetType, expectedEvaluationResultType, + expressionVariables); + fail("Should have failed"); + } catch (ParserException e) { + } } public void testParseLiteralExpression() { diff --git a/spring-binding/src/test/java/org/springframework/binding/message/DefaultMessageContextFactoryTests.java b/spring-binding/src/test/java/org/springframework/binding/message/DefaultMessageContextFactoryTests.java new file mode 100644 index 00000000..d769a549 --- /dev/null +++ b/spring-binding/src/test/java/org/springframework/binding/message/DefaultMessageContextFactoryTests.java @@ -0,0 +1,92 @@ +package org.springframework.binding.message; + +import java.io.Serializable; +import java.util.Locale; + +import junit.framework.TestCase; + +import org.springframework.context.support.StaticMessageSource; + +public class DefaultMessageContextFactoryTests extends TestCase { + private DefaultMessageContextFactory factory; + + protected void setUp() { + StaticMessageSource messageSource = new StaticMessageSource(); + factory = new DefaultMessageContextFactory(messageSource); + messageSource.addMessage("message", Locale.getDefault(), "Hello world resolved!"); + messageSource.addMessage("argmessage", Locale.getDefault(), "Hello world {0}!"); + } + + public void testCreateMessageContext() { + MessageContext context = factory.createMessageContext(); + context.addMessage(new MessageBuilder().defaultText("Hello world!").build()); + Message[] messages = context.getMessages(); + assertEquals(1, messages.length); + assertEquals("Hello world!", messages[0].getText()); + assertEquals(Severity.INFO, messages[0].getSeverity()); + assertEquals(null, messages[0].getSource()); + } + + public void testResolveMessage() { + MessageContext context = factory.createMessageContext(); + context.addMessage(new MessageBuilder().warning().source(this).code("message").build()); + Message[] messages = context.getMessages(this); + assertEquals(1, messages.length); + assertEquals("Hello world resolved!", messages[0].getText()); + assertEquals(Severity.WARNING, messages[0].getSeverity()); + assertEquals(this, messages[0].getSource()); + } + + public void testResolveMessageDefaultText() { + MessageContext context = factory.createMessageContext(); + context.addMessage(new MessageBuilder().error().code("bogus").defaultText("Hello world fallback!").build()); + Message[] messages = context.getMessages(null); + assertEquals(1, messages.length); + assertEquals("Hello world fallback!", messages[0].getText()); + assertEquals(Severity.ERROR, messages[0].getSeverity()); + assertEquals(null, messages[0].getSource()); + assertTrue(context instanceof StateManageableMessageContext); + } + + public void testResolveMessageWithArgs() { + MessageContext context = factory.createMessageContext(); + context.addMessage(new MessageBuilder().error().source(this).code("argmessage").arg("Keith").defaultText( + "Hello world fallback!").build()); + Message[] messages = context.getMessages(this); + assertEquals(1, messages.length); + assertEquals("Hello world Keith!", messages[0].getText()); + assertEquals(Severity.ERROR, messages[0].getSeverity()); + assertEquals(this, messages[0].getSource()); + assertTrue(context instanceof StateManageableMessageContext); + } + + public void testResolveMessageWithMultipleCodes() { + MessageContext context = factory.createMessageContext(); + context.addMessage(new MessageBuilder().error().source(this).codes(new String[] { "bogus", "argmessage" }) + .args(new Object[] { "Keith" }).defaultText("Hello world fallback!").build()); + Message[] messages = context.getMessages(this); + assertEquals(1, messages.length); + assertEquals("Hello world Keith!", messages[0].getText()); + assertEquals(Severity.ERROR, messages[0].getSeverity()); + assertEquals(this, messages[0].getSource()); + assertTrue(context instanceof StateManageableMessageContext); + } + + public void testSaveRestoreMessages() { + MessageContext context = factory.createMessageContext(); + context.addMessage(new MessageBuilder().defaultText("Info").build()); + context.addMessage(new MessageBuilder().error().defaultText("Error").build()); + context.addMessage(new MessageBuilder().warning().source(this).code("message").build()); + assertEquals(2, context.getMessages(null).length); + assertEquals(1, context.getMessages(this).length); + assertTrue(context instanceof StateManageableMessageContext); + StateManageableMessageContext manageable = (StateManageableMessageContext) context; + Serializable messages = manageable.createMessagesMemento(); + context = factory.createMessageContext(); + assertEquals(0, context.getMessages().length); + manageable = (StateManageableMessageContext) context; + manageable.restoreMessages(messages); + assertEquals(2, context.getMessages(null).length); + assertEquals(1, context.getMessages(this).length); + } +}