From 5cc2243c5c18fdf9a7a2d02c373f8fc80a7a48a2 Mon Sep 17 00:00:00 2001 From: Keith Donald Date: Sat, 6 Jun 2009 19:13:27 +0000 Subject: [PATCH] UI message system initial commit; support for adding UI messages of different severities INFO, WARNING, ERROR, FATAL git-svn-id: https://src.springframework.org/svn/spring-framework/trunk@1319 50f2f4bb-b051-0410-bef5-90022cba6387 --- .../.settings/org.eclipse.jdt.core.prefs | 22 +-- .../.settings/org.eclipse.jdt.ui.prefs | 4 +- .../ui/binding/BindingFailure.java | 35 +++- .../ui/message/DefaultMessageResolver.java | 87 ++++++++++ .../springframework/ui/message/Message.java | 42 +++++ .../ui/message/MessageBuilder.java | 158 ++++++++++++++++++ .../ui/message/MessageContext.java | 47 ++++++ .../ui/message/MessageResolver.java | 39 +++++ .../springframework/ui/message/Severity.java | 48 ++++++ .../springframework/ui/message/package.html | 7 + .../support/DefaultMessageContext.java | 147 ++++++++++++++++ .../ui/message/MessageBuilderTests.java | 24 +++ .../support/DefaultMessageContextTests.java | 40 +++++ 13 files changed, 682 insertions(+), 18 deletions(-) create mode 100644 org.springframework.context/src/main/java/org/springframework/ui/message/DefaultMessageResolver.java create mode 100644 org.springframework.context/src/main/java/org/springframework/ui/message/Message.java create mode 100644 org.springframework.context/src/main/java/org/springframework/ui/message/MessageBuilder.java create mode 100644 org.springframework.context/src/main/java/org/springframework/ui/message/MessageContext.java create mode 100644 org.springframework.context/src/main/java/org/springframework/ui/message/MessageResolver.java create mode 100644 org.springframework.context/src/main/java/org/springframework/ui/message/Severity.java create mode 100644 org.springframework.context/src/main/java/org/springframework/ui/message/package.html create mode 100644 org.springframework.context/src/main/java/org/springframework/ui/message/support/DefaultMessageContext.java create mode 100644 org.springframework.context/src/test/java/org/springframework/ui/message/MessageBuilderTests.java create mode 100644 org.springframework.context/src/test/java/org/springframework/ui/message/support/DefaultMessageContextTests.java diff --git a/org.springframework.context/.settings/org.eclipse.jdt.core.prefs b/org.springframework.context/.settings/org.eclipse.jdt.core.prefs index 47b97517557..8dbff52f223 100644 --- a/org.springframework.context/.settings/org.eclipse.jdt.core.prefs +++ b/org.springframework.context/.settings/org.eclipse.jdt.core.prefs @@ -1,4 +1,4 @@ -#Thu Dec 18 06:34:20 PST 2008 +#Sat Jun 06 14:43:50 EDT 2009 eclipse.preferences.version=1 org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6 @@ -55,17 +55,17 @@ org.eclipse.jdt.core.formatter.brace_position_for_switch=end_of_line org.eclipse.jdt.core.formatter.brace_position_for_type_declaration=end_of_line org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment=false org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment=false -org.eclipse.jdt.core.formatter.comment.format_block_comments=true +org.eclipse.jdt.core.formatter.comment.format_block_comments=false org.eclipse.jdt.core.formatter.comment.format_header=false -org.eclipse.jdt.core.formatter.comment.format_html=true -org.eclipse.jdt.core.formatter.comment.format_javadoc_comments=true -org.eclipse.jdt.core.formatter.comment.format_line_comments=true +org.eclipse.jdt.core.formatter.comment.format_html=false +org.eclipse.jdt.core.formatter.comment.format_javadoc_comments=false +org.eclipse.jdt.core.formatter.comment.format_line_comments=false org.eclipse.jdt.core.formatter.comment.format_source_code=true -org.eclipse.jdt.core.formatter.comment.indent_parameter_description=true -org.eclipse.jdt.core.formatter.comment.indent_root_tags=true -org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags=insert -org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter=insert -org.eclipse.jdt.core.formatter.comment.line_length=80 +org.eclipse.jdt.core.formatter.comment.indent_parameter_description=false +org.eclipse.jdt.core.formatter.comment.indent_root_tags=false +org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags=do not insert +org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter=do not insert +org.eclipse.jdt.core.formatter.comment.line_length=120 org.eclipse.jdt.core.formatter.compact_else_if=true org.eclipse.jdt.core.formatter.continuation_indentation=2 org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer=2 @@ -256,7 +256,7 @@ org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line=false org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line=false org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line=false org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line=false -org.eclipse.jdt.core.formatter.lineSplit=80 +org.eclipse.jdt.core.formatter.lineSplit=120 org.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column=false org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column=false org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body=0 diff --git a/org.springframework.context/.settings/org.eclipse.jdt.ui.prefs b/org.springframework.context/.settings/org.eclipse.jdt.ui.prefs index 6350d90736c..c1aa3893997 100644 --- a/org.springframework.context/.settings/org.eclipse.jdt.ui.prefs +++ b/org.springframework.context/.settings/org.eclipse.jdt.ui.prefs @@ -1,4 +1,4 @@ -#Thu Dec 18 06:34:20 PST 2008 +#Sat Jun 06 14:43:50 EDT 2009 eclipse.preferences.version=1 -formatter_profile=org.eclipse.jdt.ui.default.eclipse_profile +formatter_profile=_Spring formatter_settings_version=11 diff --git a/org.springframework.context/src/main/java/org/springframework/ui/binding/BindingFailure.java b/org.springframework.context/src/main/java/org/springframework/ui/binding/BindingFailure.java index 542095d5457..b84c60d252d 100644 --- a/org.springframework.context/src/main/java/org/springframework/ui/binding/BindingFailure.java +++ b/org.springframework.context/src/main/java/org/springframework/ui/binding/BindingFailure.java @@ -3,13 +3,38 @@ package org.springframework.ui.binding; import java.util.Map; public interface BindingFailure { - + + /** + * The code identifying the type of failure. + * This code can be used to resolve the failure message if no explicit {@link #getMessage() message} is configured. + */ String getCode(); - String getSeverity(); - - // TODO - where does arg formatting occur - Map getArgs(); + /** + * The severity of the failure, which measures the impact of the failure on the user. + */ + Severity getSeverity(); + + /** + * An map of arguments that can be used as named parameters in resolvable messages associated with this failure. + * Each constraint defines a set of arguments that are specific to it. For example, a length + * constraint might define arguments of "min" and "max" of Integer values. In the message bundle, you then might see + * "length=The ${label} field value must be between ${min} and ${max}". Returns an empty map if no arguments are present. + */ + Map getArguments(); + + /** + * The message summarizing this failure. May be a literal string or a resolvable message code. Can be null. + * If null, the failure message will be resolved using the failure code. + */ + String getDefaultMessage(); + /** + * A map of details providing additional information about this failure. Each entry in this map is a failure detail + * item that has a name and value. The name uniquely identifies the failure detail and describes its purpose; + * for example, a "cause" or "recommendedAction". The value is the failure detail message, either a literal string or + * resolvable code. If resolvable, the detail code is resolved relative to this failure. + * Returns an empty map if no details are present. + */ Map getDetails(); } diff --git a/org.springframework.context/src/main/java/org/springframework/ui/message/DefaultMessageResolver.java b/org.springframework.context/src/main/java/org/springframework/ui/message/DefaultMessageResolver.java new file mode 100644 index 00000000000..016de967838 --- /dev/null +++ b/org.springframework.context/src/main/java/org/springframework/ui/message/DefaultMessageResolver.java @@ -0,0 +1,87 @@ +/* + * Copyright 2004-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.ui.message; + +import java.util.Locale; + +import org.springframework.context.MessageSource; +import org.springframework.context.MessageSourceResolvable; +import org.springframework.core.style.ToStringCreator; + +class DefaultMessageResolver implements MessageResolver, MessageSourceResolvable { + + private Severity severity; + + private String[] codes; + + private Object[] args; + + private String defaultText; + + public DefaultMessageResolver(Severity severity, String[] codes, Object[] args, String defaultText) { + this.severity = severity; + this.codes = codes; + this.args = args; + this.defaultText = defaultText; + } + + // implementing MessageResolver + + public Message resolveMessage(MessageSource messageSource, Locale locale) { + String text = messageSource.getMessage(this, locale); + return new TextMessage(severity, text); + } + + // implementing MessageSourceResolver + + public String[] getCodes() { + return codes; + } + + public Object[] getArguments() { + return args; + } + + public String getDefaultMessage() { + return defaultText; + } + + public String toString() { + return new ToStringCreator(this).append("severity", severity).append("codes", codes).append("args", args).append("defaultText", defaultText).toString(); + } + + static class TextMessage implements Message { + + private Severity severity; + + private String text; + + public TextMessage(Severity severity, String text) { + this.severity = severity; + this.text = text; + } + + public Severity getSeverity() { + return severity; + } + + public String getText() { + return text; + } + + } + +} \ No newline at end of file diff --git a/org.springframework.context/src/main/java/org/springframework/ui/message/Message.java b/org.springframework.context/src/main/java/org/springframework/ui/message/Message.java new file mode 100644 index 00000000000..84f880f161d --- /dev/null +++ b/org.springframework.context/src/main/java/org/springframework/ui/message/Message.java @@ -0,0 +1,42 @@ +/* + * Copyright 2004-2009 the original author oimport java.io.Serializable; + +import org.springframework.core.style.ToStringCreator; +ou 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.ui.message; + +/** + * Communicates information about an event to the user. + * For example, a validation message may inform a web application user a business rule was violated. + * A message is attached to a receiving element, has text providing the basis for communication, + * and has severity indicating the priority or intensity of the message for its receiver. + * + * @author Keith Donald + */ +public interface Message { + + /** + * The severity of this message. + * The severity indicates the intensity or priority of the communication. + * @return the message severity + */ + public Severity getSeverity(); + + /** + * The message text. + * The text is the message's communication payload. + * @return the message text + */ + public String getText(); + +} diff --git a/org.springframework.context/src/main/java/org/springframework/ui/message/MessageBuilder.java b/org.springframework.context/src/main/java/org/springframework/ui/message/MessageBuilder.java new file mode 100644 index 00000000000..551c96f5ffc --- /dev/null +++ b/org.springframework.context/src/main/java/org/springframework/ui/message/MessageBuilder.java @@ -0,0 +1,158 @@ +/* + * Copyright 2004-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.ui.message; + +import java.util.ArrayList; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; + +import org.springframework.context.MessageSource; +import org.springframework.context.MessageSourceResolvable; +import org.springframework.core.style.ToStringCreator; + +/** + * 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}. + * + * Usage example: + *

+ *

+ * new MessageBuilder().
+ *     severity(Severity.ERROR).
+ *     code("invalidFormat").
+ *     arg("mathForm.decimalField").
+ *     arg("#,###.##").
+ *     defaultText("The decimal field must be in format #,####.##").
+ *     build();
+ * 
+ *

+ * @author Keith Donald + */ +public class MessageBuilder { + + private Set codes = new LinkedHashSet(); + + private Severity severity; + + private List args = new ArrayList(); + + private String defaultText; + + /** + * Records the severity of the message. + * @return this, for fluent API usage + */ + public MessageBuilder severity(Severity severity) { + this.severity = severity; + return this; + } + + /** + * Records that the message being built should try and resolve its text using the code provided. + * Adds the code to the codes list. Successive calls to this method add additional codes. + * Codes are applied in the order they are added. + * @param code the message code + * @return this, for fluent API usage + */ + public MessageBuilder code(String code) { + codes.add(code); + return this; + } + + /** + * Records that the message being built has a variable argument. + * Adds the arg to the args list. Successive calls to this method add additional args. + * Args are applied in the order they are added. + * @param arg the message argument value + * @return this, for fluent API usage + */ + public MessageBuilder arg(Object arg) { + args.add(arg); + return this; + } + + /** + * Records that the message being built has a variable argument, whose display value is also {@link MessageSourceResolvable}. + * Adds the arg to the args list. Successive calls to this method add additional resolvable args. + * Args are applied in the order they are added. + * @param arg the resolvable message argument + * @return this, for fluent API usage + */ + public MessageBuilder resolvableArg(Object arg) { + args.add(new ResolvableArgument(arg)); + 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. + * If the message has codes but none can be resolved, 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. + * Call after 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"); + } + String[] codesArray = (String[]) codes.toArray(new String[codes.size()]); + Object[] argsArray = args.toArray(new Object[args.size()]); + return new DefaultMessageResolver(severity, codesArray, argsArray, defaultText); + } + + private static class ResolvableArgument implements MessageSourceResolvable { + + private Object arg; + + public ResolvableArgument(Object arg) { + this.arg = arg; + } + + public Object[] getArguments() { + return null; + } + + public String[] getCodes() { + return new String[] { arg.toString() }; + } + + public String getDefaultMessage() { + return arg.toString(); + } + + public String toString() { + return new ToStringCreator(this).append("arg", arg).toString(); + } + + } + +} \ No newline at end of file diff --git a/org.springframework.context/src/main/java/org/springframework/ui/message/MessageContext.java b/org.springframework.context/src/main/java/org/springframework/ui/message/MessageContext.java new file mode 100644 index 00000000000..554e6fdf5b3 --- /dev/null +++ b/org.springframework.context/src/main/java/org/springframework/ui/message/MessageContext.java @@ -0,0 +1,47 @@ +/* + * Copyright 2004-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.ui.message; + +import java.util.List; +import java.util.Map; + +/** + * A context for recording and retrieving messages for display in a user interface. + */ +public interface MessageContext { + + /** + * Return all messages in this context indexed by the UI element they are associated with. + * @return the message map + */ + public Map> getMessages(); + + /** + * Get all messages on the UI element provided. + * Returns an empty list if no messages have been recorded against the element. + * Messages are returned in the order they were added. + * @param element the id of the element to lookup messages against + */ + public List getMessages(String element); + + /** + * Add a new message to this context. + * @param element the id of the UI element the message should be associated with + * @param messageResolver the resolver that will resolve the message to be added + */ + public void addMessage(String element, MessageResolver messageResolver); + +} diff --git a/org.springframework.context/src/main/java/org/springframework/ui/message/MessageResolver.java b/org.springframework.context/src/main/java/org/springframework/ui/message/MessageResolver.java new file mode 100644 index 00000000000..b8113562b9e --- /dev/null +++ b/org.springframework.context/src/main/java/org/springframework/ui/message/MessageResolver.java @@ -0,0 +1,39 @@ +/* + * Copyright 2004-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.ui.message; + +import java.util.Locale; + +import org.springframework.context.MessageSource; + +/** + * A factory for a Message. Allows a Message to be internationalized and to be resolved from a + * {@link MessageSource message resource bundle}. + * + * @author Keith Donald + * @see Message + * @see MessageSource + */ +public interface MessageResolver { + + /** + * Resolve the message from the message source using the current locale. + * @param messageSource the message source, an abstraction for a resource bundle + * @param locale the current locale of this request + * @return the resolved message + */ + public Message resolveMessage(MessageSource messageSource, Locale locale); +} diff --git a/org.springframework.context/src/main/java/org/springframework/ui/message/Severity.java b/org.springframework.context/src/main/java/org/springframework/ui/message/Severity.java new file mode 100644 index 00000000000..96d989b4d0c --- /dev/null +++ b/org.springframework.context/src/main/java/org/springframework/ui/message/Severity.java @@ -0,0 +1,48 @@ +/* + * Copyright 2004-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.ui.message; + +/** + * Enum exposing supported message severities. + * + * @author Keith Donald + * @author Jeremy Grelle + * @see Message + */ +public enum Severity { + + /** + * The "Informational" severity. Used to indicate a successful operation or result. + */ + INFO, + + /** + * The "Warning" severity. Used to indicate there is a minor problem, or to inform the message receiver of possible + * misuse, or to indicate a problem may arise in the future. + */ + WARNING, + + /** + * The "Error" severity. Used to indicate a significant problem like a business rule violation. + */ + ERROR, + + /** + * The "Fatal" severity. Used to indicate a fatal problem like a system error. + */ + FATAL + +} diff --git a/org.springframework.context/src/main/java/org/springframework/ui/message/package.html b/org.springframework.context/src/main/java/org/springframework/ui/message/package.html new file mode 100644 index 00000000000..bc513ce3938 --- /dev/null +++ b/org.springframework.context/src/main/java/org/springframework/ui/message/package.html @@ -0,0 +1,7 @@ + + +

+A system for creating and managing localized messages to display in a UI. +

+ + \ No newline at end of file diff --git a/org.springframework.context/src/main/java/org/springframework/ui/message/support/DefaultMessageContext.java b/org.springframework.context/src/main/java/org/springframework/ui/message/support/DefaultMessageContext.java new file mode 100644 index 00000000000..dea0def44f0 --- /dev/null +++ b/org.springframework.context/src/main/java/org/springframework/ui/message/support/DefaultMessageContext.java @@ -0,0 +1,147 @@ +/* + * Copyright 2004-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.ui.message.support; + +import java.text.MessageFormat; +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; + +import org.springframework.context.MessageSource; +import org.springframework.context.i18n.LocaleContextHolder; +import org.springframework.context.support.AbstractMessageSource; +import org.springframework.core.style.ToStringCreator; +import org.springframework.ui.message.Message; +import org.springframework.ui.message.MessageContext; +import org.springframework.ui.message.MessageResolver; +import org.springframework.ui.message.Severity; +import org.springframework.util.CachingMapDecorator; + +import edu.emory.mathcs.backport.java.util.Collections; + +/** + * The default message context implementation. + * Uses a {@link MessageSource} to resolve messages that are added by callers. + * + * @author Keith Donald + */ +public class DefaultMessageContext implements MessageContext { + + private MessageSource messageSource; + + @SuppressWarnings("serial") + private Map> messagesByElement = new CachingMapDecorator>(new LinkedHashMap>()) { + protected ArrayList create(String element) { + return new ArrayList(); + } + }; + + /** + * Creates a new default message context. + * Defaults to a message source that simply resolves default text and cannot resolve localized message codes. + */ + public DefaultMessageContext() { + init(null); + } + + /** + * Creates a new default message context. + * @param messageSource the message source to resolve messages added to this context + */ + public DefaultMessageContext(MessageSource messageSource) { + init(messageSource); + } + + /** + * The message source configured to resolve message text. + * @return the message source + */ + public MessageSource getMessageSource() { + return messageSource; + } + + // implementing message context + + @SuppressWarnings("unchecked") + public Map> getMessages() { + return Collections.unmodifiableMap(messagesByElement); + } + + @SuppressWarnings("unchecked") + public List getMessages(String element) { + List messages = messagesByElement.get(element); + if (messages.isEmpty()) { + return Collections.emptyList(); + } + return Collections.unmodifiableList(messages); + } + + public void addMessage(String element, MessageResolver messageResolver) { + List messages = messagesByElement.get(element); + messages.add(new ResolvableMessage(messageResolver)); + } + + public String toString() { + return new ToStringCreator(this).append("messagesByElement", messagesByElement).toString(); + } + + // internal helpers + + private void init(MessageSource messageSource) { + setMessageSource(messageSource); + } + + private void setMessageSource(MessageSource messageSource) { + if (messageSource == null) { + messageSource = new DefaultTextFallbackMessageSource(); + } + this.messageSource = messageSource; + } + + private static class DefaultTextFallbackMessageSource extends AbstractMessageSource { + protected MessageFormat resolveCode(String code, Locale locale) { + return null; + } + } + + class ResolvableMessage implements Message { + + private MessageResolver resolver; + + private Message resolvedMessage; + + public ResolvableMessage(MessageResolver resolver) { + this.resolver = resolver; + } + + public Severity getSeverity() { + return getMessage().getSeverity(); + } + + public String getText() { + return getMessage().getText(); + } + + public Message getMessage() { + if (resolvedMessage == null) { + resolvedMessage = resolver.resolveMessage(messageSource, LocaleContextHolder.getLocale()); + } + return resolvedMessage; + } + } +} \ No newline at end of file diff --git a/org.springframework.context/src/test/java/org/springframework/ui/message/MessageBuilderTests.java b/org.springframework.context/src/test/java/org/springframework/ui/message/MessageBuilderTests.java new file mode 100644 index 00000000000..96eb3bab0f7 --- /dev/null +++ b/org.springframework.context/src/test/java/org/springframework/ui/message/MessageBuilderTests.java @@ -0,0 +1,24 @@ +package org.springframework.ui.message; + +import static org.junit.Assert.assertEquals; + +import java.util.Locale; + +import org.junit.Test; +import org.springframework.context.support.StaticMessageSource; + +public class MessageBuilderTests { + private MessageBuilder builder = new MessageBuilder(); + + @Test + public void buildMessage() { + MessageResolver resolver = builder.severity(Severity.ERROR).code("invalidFormat").resolvableArg("mathForm.decimalField") + .arg("#,###.##").defaultText("Field must be in format #,###.##").build(); + StaticMessageSource messageSource = new StaticMessageSource(); + messageSource.addMessage("invalidFormat", Locale.US, "{0} must be in format {1}"); + messageSource.addMessage("mathForm.decimalField", Locale.US, "Decimal Field"); + Message message = resolver.resolveMessage(messageSource, Locale.US); + assertEquals(Severity.ERROR, message.getSeverity()); + assertEquals("Decimal Field must be in format #,###.##", message.getText()); + } +} diff --git a/org.springframework.context/src/test/java/org/springframework/ui/message/support/DefaultMessageContextTests.java b/org.springframework.context/src/test/java/org/springframework/ui/message/support/DefaultMessageContextTests.java new file mode 100644 index 00000000000..79d6d028d2c --- /dev/null +++ b/org.springframework.context/src/test/java/org/springframework/ui/message/support/DefaultMessageContextTests.java @@ -0,0 +1,40 @@ +package org.springframework.ui.message.support; + +import static org.junit.Assert.*; + +import java.util.List; +import java.util.Locale; +import java.util.Map; + +import org.junit.Before; +import org.junit.Test; +import org.springframework.context.support.StaticMessageSource; +import org.springframework.ui.message.Message; +import org.springframework.ui.message.MessageBuilder; +import org.springframework.ui.message.MessageResolver; +import org.springframework.ui.message.Severity; + +public class DefaultMessageContextTests { + + private DefaultMessageContext context; + + @Before + public void setUp() { + StaticMessageSource messageSource = new StaticMessageSource(); + messageSource.addMessage("invalidFormat", Locale.US, "{0} must be in format {1}"); + messageSource.addMessage("mathForm.decimalField", Locale.US, "Decimal Field"); + context = new DefaultMessageContext(messageSource); + } + + @Test + public void addMessage() { + MessageBuilder builder = new MessageBuilder(); + MessageResolver message = builder.severity(Severity.ERROR).code("invalidFormat").resolvableArg( + "mathForm.decimalField").arg("#,###.##").defaultText("Field must be in format #,###.##").build(); + context.addMessage("mathForm.decimalField", message); + Map> messages = context.getMessages(); + assertEquals(1, messages.size()); + assertEquals("Decimal Field must be in format #,###.##", messages.get("mathForm.decimalField").get(0).getText()); + assertEquals("Decimal Field must be in format #,###.##", context.getMessages("mathForm.decimalField").get(0).getText()); + } +}