diff --git a/spring-binding/src/main/java/org/springframework/binding/message/DefaultMessageContext.java b/spring-binding/src/main/java/org/springframework/binding/message/DefaultMessageContext.java
index f76aa320..f770eee8 100644
--- a/spring-binding/src/main/java/org/springframework/binding/message/DefaultMessageContext.java
+++ b/spring-binding/src/main/java/org/springframework/binding/message/DefaultMessageContext.java
@@ -26,6 +26,7 @@ import java.util.Map;
import org.springframework.context.MessageSource;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.core.style.ToStringCreator;
+import org.springframework.util.Assert;
import org.springframework.util.CachingMapDecorator;
/**
@@ -47,12 +48,36 @@ class DefaultMessageContext implements StateManageableMessageContext {
this.messageSource = messageSource;
}
- public Serializable createMessagesMemento() {
- return new HashMap(objectMessages);
+ // implementing message context
+
+ public Message[] getAllMessages() {
+ List messages = new ArrayList();
+ Iterator it = objectMessages.keySet().iterator();
+ while (it.hasNext()) {
+ messages.addAll((List) objectMessages.get(it.next()));
+ }
+ return (Message[]) messages.toArray(new Message[messages.size()]);
}
- public void restoreMessages(Serializable messagesMemento) {
- this.objectMessages.putAll((Map) messagesMemento);
+ public Message[] getMessagesBySource(Object source) {
+ List messages = (List) objectMessages.get(source);
+ return (Message[]) messages.toArray(new Message[messages.size()]);
+ }
+
+ public Message[] getMessagesByCriteria(MessageCriteria criteria) {
+ Assert.notNull(criteria, "The message criteria is required");
+ List messages = new ArrayList();
+ Iterator it = objectMessages.values().iterator();
+ while (it.hasNext()) {
+ List sourceMessages = (List) it.next();
+ for (Iterator it2 = sourceMessages.iterator(); it2.hasNext();) {
+ Message message = (Message) it2.next();
+ if (criteria.test(message)) {
+ messages.add(message);
+ }
+ }
+ }
+ return (Message[]) messages.toArray(new Message[messages.size()]);
}
public void addMessage(MessageResolver messageResolver) {
@@ -62,24 +87,20 @@ class DefaultMessageContext implements StateManageableMessageContext {
messages.add(message);
}
- public Message[] getMessages() {
- List messages = new ArrayList();
- Iterator i = objectMessages.keySet().iterator();
- while (i.hasNext()) {
- messages.addAll((List) objectMessages.get(i.next()));
- }
- return (Message[]) messages.toArray(new Message[messages.size()]);
- }
-
- public Message[] getMessages(Object source) {
- List messages = (List) objectMessages.get(source);
- return (Message[]) messages.toArray(new Message[messages.size()]);
- }
-
public void clearMessages() {
objectMessages.clear();
}
+ // implementing state manageable message context
+
+ public Serializable createMessagesMemento() {
+ return new HashMap(objectMessages);
+ }
+
+ public void restoreMessages(Serializable messagesMemento) {
+ this.objectMessages.putAll((Map) messagesMemento);
+ }
+
public String toString() {
return new ToStringCreator(this).append("objectMessages", objectMessages).toString();
}
diff --git a/spring-binding/src/main/java/org/springframework/binding/message/MessageContext.java b/spring-binding/src/main/java/org/springframework/binding/message/MessageContext.java
index 7440999c..8dee7754 100644
--- a/spring-binding/src/main/java/org/springframework/binding/message/MessageContext.java
+++ b/spring-binding/src/main/java/org/springframework/binding/message/MessageContext.java
@@ -9,14 +9,20 @@ public interface MessageContext {
* Get all messages in this context. The messages returned should be suitable for display as-is.
* @return the messages
*/
- public Message[] getMessages();
+ public Message[] getAllMessages();
/**
- * Get all messages in this context from the source provided.
- * @param source the source that recorded the message
+ * Get all messages in this context for the source provided.
+ * @param source the source associated with messages, or null for global messages
* @return the source's messages
*/
- public Message[] getMessages(Object source);
+ public Message[] getMessagesBySource(Object source);
+
+ /**
+ * Get all messages that meet the given result criteria.
+ * @param criteria the message criteria
+ */
+ public Message[] getMessagesByCriteria(MessageCriteria criteria);
/**
* Add a new message to this context.
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
index d769a549..abf6de0c 100644
--- a/spring-binding/src/test/java/org/springframework/binding/message/DefaultMessageContextFactoryTests.java
+++ b/spring-binding/src/test/java/org/springframework/binding/message/DefaultMessageContextFactoryTests.java
@@ -20,7 +20,7 @@ public class DefaultMessageContextFactoryTests extends TestCase {
public void testCreateMessageContext() {
MessageContext context = factory.createMessageContext();
context.addMessage(new MessageBuilder().defaultText("Hello world!").build());
- Message[] messages = context.getMessages();
+ Message[] messages = context.getAllMessages();
assertEquals(1, messages.length);
assertEquals("Hello world!", messages[0].getText());
assertEquals(Severity.INFO, messages[0].getSeverity());
@@ -30,7 +30,7 @@ public class DefaultMessageContextFactoryTests extends TestCase {
public void testResolveMessage() {
MessageContext context = factory.createMessageContext();
context.addMessage(new MessageBuilder().warning().source(this).code("message").build());
- Message[] messages = context.getMessages(this);
+ Message[] messages = context.getMessagesBySource(this);
assertEquals(1, messages.length);
assertEquals("Hello world resolved!", messages[0].getText());
assertEquals(Severity.WARNING, messages[0].getSeverity());
@@ -40,7 +40,7 @@ public class DefaultMessageContextFactoryTests extends TestCase {
public void testResolveMessageDefaultText() {
MessageContext context = factory.createMessageContext();
context.addMessage(new MessageBuilder().error().code("bogus").defaultText("Hello world fallback!").build());
- Message[] messages = context.getMessages(null);
+ Message[] messages = context.getAllMessages();
assertEquals(1, messages.length);
assertEquals("Hello world fallback!", messages[0].getText());
assertEquals(Severity.ERROR, messages[0].getSeverity());
@@ -52,7 +52,7 @@ public class DefaultMessageContextFactoryTests extends TestCase {
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);
+ Message[] messages = context.getAllMessages();
assertEquals(1, messages.length);
assertEquals("Hello world Keith!", messages[0].getText());
assertEquals(Severity.ERROR, messages[0].getSeverity());
@@ -64,7 +64,7 @@ public class DefaultMessageContextFactoryTests extends TestCase {
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);
+ Message[] messages = context.getMessagesBySource(this);
assertEquals(1, messages.length);
assertEquals("Hello world Keith!", messages[0].getText());
assertEquals(Severity.ERROR, messages[0].getSeverity());
@@ -77,16 +77,16 @@ public class DefaultMessageContextFactoryTests extends TestCase {
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);
+ assertEquals(2, context.getMessagesBySource(null).length);
+ assertEquals(1, context.getMessagesBySource(this).length);
assertTrue(context instanceof StateManageableMessageContext);
StateManageableMessageContext manageable = (StateManageableMessageContext) context;
Serializable messages = manageable.createMessagesMemento();
context = factory.createMessageContext();
- assertEquals(0, context.getMessages().length);
+ assertEquals(0, context.getAllMessages().length);
manageable = (StateManageableMessageContext) context;
manageable.restoreMessages(messages);
- assertEquals(2, context.getMessages(null).length);
- assertEquals(1, context.getMessages(this).length);
+ assertEquals(2, context.getMessagesBySource(null).length);
+ assertEquals(1, context.getMessagesBySource(this).length);
}
}
diff --git a/spring-faces/src/main/java/org/springframework/faces/webflow/FlowFacesContext.java b/spring-faces/src/main/java/org/springframework/faces/webflow/FlowFacesContext.java
index 066ed476..f2ba165f 100644
--- a/spring-faces/src/main/java/org/springframework/faces/webflow/FlowFacesContext.java
+++ b/spring-faces/src/main/java/org/springframework/faces/webflow/FlowFacesContext.java
@@ -119,7 +119,7 @@ public class FlowFacesContext extends FacesContext {
* associated with any specific UIComponent. If no such messages have been queued, return null.
*/
public FacesMessage.Severity getMaximumSeverity() {
- if (context.getMessageContext().getMessages().length == 0) {
+ if (context.getMessageContext().getAllMessages().length == 0) {
return null;
}
FacesMessage.Severity max = FacesMessage.SEVERITY_INFO;
@@ -241,11 +241,11 @@ public class FlowFacesContext extends FacesContext {
private int currentIndex = -1;
protected FacesMessageIterator() {
- this.messages = context.getMessageContext().getMessages();
+ this.messages = context.getMessageContext().getAllMessages();
}
protected FacesMessageIterator(String clientId) {
- this.messages = context.getMessageContext().getMessages(clientId);
+ this.messages = context.getMessageContext().getMessagesBySource(clientId);
}
public boolean hasNext() {
@@ -282,7 +282,7 @@ public class FlowFacesContext extends FacesContext {
int currentIndex = -1;
protected ClientIdIterator() {
- this.messages = context.getMessageContext().getMessages();
+ this.messages = context.getMessageContext().getAllMessages();
}
public boolean hasNext() {
diff --git a/spring-faces/src/test/java/org/springframework/faces/webflow/FlowFacesContextTests.java b/spring-faces/src/test/java/org/springframework/faces/webflow/FlowFacesContextTests.java
index bcc7dbc6..f5ede8a5 100644
--- a/spring-faces/src/test/java/org/springframework/faces/webflow/FlowFacesContextTests.java
+++ b/spring-faces/src/test/java/org/springframework/faces/webflow/FlowFacesContextTests.java
@@ -1,7 +1,5 @@
package org.springframework.faces.webflow;
-import org.easymock.EasyMock;
-
import java.util.Iterator;
import javax.faces.application.FacesMessage;
@@ -9,8 +7,10 @@ import javax.faces.context.FacesContext;
import junit.framework.TestCase;
+import org.easymock.EasyMock;
import org.springframework.binding.message.Message;
import org.springframework.binding.message.MessageContext;
+import org.springframework.binding.message.MessageCriteria;
import org.springframework.binding.message.MessageResolver;
import org.springframework.binding.message.Severity;
import org.springframework.webflow.execution.RequestContext;
@@ -106,17 +106,19 @@ public class FlowFacesContextTests extends TestCase {
messageCount++;
}
- public Message[] getMessages() {
+ public Message[] getAllMessages() {
return null;
}
- public Message[] getMessages(Object source) {
- // TODO Auto-generated method stub
+ public Message[] getMessagesBySource(Object source) {
+ throw new UnsupportedOperationException("Auto-generated method stub");
+ }
+
+ public Message[] getMessagesByCriteria(MessageCriteria criteria) {
throw new UnsupportedOperationException("Auto-generated method stub");
}
public void clearMessages() {
- // TODO Auto-generated method stub
throw new UnsupportedOperationException("Auto-generated method stub");
}
}
@@ -136,16 +138,19 @@ public class FlowFacesContextTests extends TestCase {
}
- public Message[] getMessages() {
+ public Message[] getAllMessages() {
return messages;
}
- public Message[] getMessages(Object source) {
+ public Message[] getMessagesBySource(Object source) {
return new Message[] { messages[1] };
}
+ public Message[] getMessagesByCriteria(MessageCriteria criteria) {
+ throw new UnsupportedOperationException("Auto-generated method stub");
+ }
+
public void clearMessages() {
- // TODO Auto-generated method stub
throw new UnsupportedOperationException("Auto-generated method stub");
}
diff --git a/spring-webflow-samples/booking-faces/.settings/org.eclipse.wst.common.component b/spring-webflow-samples/booking-faces/.settings/org.eclipse.wst.common.component
index 1c05f365..cbbbabaf 100755
--- a/spring-webflow-samples/booking-faces/.settings/org.eclipse.wst.common.component
+++ b/spring-webflow-samples/booking-faces/.settings/org.eclipse.wst.common.component
@@ -4,7 +4,6 @@
-
diff --git a/spring-webflow-samples/booking-mvc/.settings/org.eclipse.wst.common.component b/spring-webflow-samples/booking-mvc/.settings/org.eclipse.wst.common.component
index 911151b0..297ca111 100755
--- a/spring-webflow-samples/booking-mvc/.settings/org.eclipse.wst.common.component
+++ b/spring-webflow-samples/booking-mvc/.settings/org.eclipse.wst.common.component
@@ -2,11 +2,8 @@
-
-
+
-
-
diff --git a/spring-webflow-samples/booking-mvc/src/main/java/org/springframework/webflow/samples/booking/Booking.java b/spring-webflow-samples/booking-mvc/src/main/java/org/springframework/webflow/samples/booking/Booking.java
index e44bc8ab..5e42e88e 100755
--- a/spring-webflow-samples/booking-mvc/src/main/java/org/springframework/webflow/samples/booking/Booking.java
+++ b/spring-webflow-samples/booking-mvc/src/main/java/org/springframework/webflow/samples/booking/Booking.java
@@ -63,7 +63,11 @@ public class Booking implements Serializable {
@Transient
public int getNights() {
- return (int) (checkoutDate.getTime() - checkinDate.getTime()) / 1000 / 60 / 60 / 24;
+ if (checkinDate == null || checkoutDate == null) {
+ return 0;
+ } else {
+ return (int) (checkoutDate.getTime() - checkinDate.getTime()) / 1000 / 60 / 60 / 24;
+ }
}
@Id
diff --git a/spring-webflow/src/main/java/org/springframework/webflow/mvc/view/BindingModel.java b/spring-webflow/src/main/java/org/springframework/webflow/mvc/view/BindingModel.java
index 6030a491..4afb9015 100644
--- a/spring-webflow/src/main/java/org/springframework/webflow/mvc/view/BindingModel.java
+++ b/spring-webflow/src/main/java/org/springframework/webflow/mvc/view/BindingModel.java
@@ -1,5 +1,6 @@
package org.springframework.webflow.mvc.view;
+import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@@ -11,7 +12,12 @@ import org.springframework.binding.format.FormatterRegistry;
import org.springframework.binding.mapping.MappingResult;
import org.springframework.binding.mapping.MappingResults;
import org.springframework.binding.mapping.MappingResultsCriteria;
+import org.springframework.binding.message.Message;
import org.springframework.binding.message.MessageContext;
+import org.springframework.binding.message.MessageCriteria;
+import org.springframework.binding.message.Severity;
+import org.springframework.validation.FieldError;
+import org.springframework.validation.ObjectError;
public class BindingModel extends ViewRenderingErrors {
@@ -23,11 +29,14 @@ public class BindingModel extends ViewRenderingErrors {
private MappingResults mappingResults;
+ private MessageContext messageContext;
+
public BindingModel(Object boundObject, ExpressionParser expressionParser, FormatterRegistry formatterRegistry,
MessageContext messageContext) {
this.boundObject = boundObject;
this.expressionParser = expressionParser;
this.formatterRegistry = formatterRegistry;
+ this.messageContext = messageContext;
}
public void setMappingResults(MappingResults results) {
@@ -35,18 +44,15 @@ public class BindingModel extends ViewRenderingErrors {
}
public List getAllErrors() {
- // TODO
- return Collections.EMPTY_LIST;
+ return toErrors(messageContext.getMessagesByCriteria(ERRORS_ANY_SOURCE));
}
public List getGlobalErrors() {
- // TODO
- return Collections.EMPTY_LIST;
+ return toErrors(messageContext.getMessagesByCriteria(ERRORS_NULL_SOURCE));
}
public List getFieldErrors(String field) {
- // TODO
- return Collections.EMPTY_LIST;
+ return toErrors(messageContext.getMessagesByCriteria(new FieldErrorMessage(field)));
}
public Class getFieldType(String field) {
@@ -61,15 +67,14 @@ public class BindingModel extends ViewRenderingErrors {
return fieldError.getResult().getOriginalValue();
}
}
- return getFormattedValue(field);
+ return getFormattedValue(parseFieldExpression(field));
}
private Expression parseFieldExpression(String field) {
return expressionParser.parseExpression(field, new FluentParserContext().evaluate(boundObject.getClass()));
}
- private Object getFormattedValue(String field) {
- Expression fieldExpression = parseFieldExpression(field);
+ private Object getFormattedValue(Expression fieldExpression) {
Formatter formatter = getFormatter(fieldExpression);
if (formatter != null) {
return formatter.format(fieldExpression.getValue(boundObject));
@@ -87,6 +92,23 @@ public class BindingModel extends ViewRenderingErrors {
}
}
+ private List toErrors(Message[] messages) {
+ if (messages == null || messages.length == 0) {
+ return Collections.EMPTY_LIST;
+ }
+ ArrayList errors = new ArrayList(messages.length);
+ for (int i = 0; i < messages.length; i++) {
+ Message message = messages[i];
+ if (message.getSource() == null) {
+ errors.add(new ObjectError("boundObject", null, null, message.getText()));
+ } else {
+ errors.add(new FieldError("boundObject", (String) message.getSource(), null, false, null, null, message
+ .getText()));
+ }
+ }
+ return errors;
+ }
+
private static class FieldErrorResult implements MappingResultsCriteria {
private String field;
@@ -104,4 +126,28 @@ public class BindingModel extends ViewRenderingErrors {
}
}
+ private static final MessageCriteria ERRORS_ANY_SOURCE = new MessageCriteria() {
+ public boolean test(Message message) {
+ return message.getSeverity() == Severity.ERROR;
+ }
+ };
+
+ private static final MessageCriteria ERRORS_NULL_SOURCE = new MessageCriteria() {
+ public boolean test(Message message) {
+ return message.getSource() == null && message.getSeverity() == Severity.ERROR;
+ }
+ };
+
+ private static class FieldErrorMessage implements MessageCriteria {
+ private String field;
+
+ public FieldErrorMessage(String field) {
+ this.field = field;
+ }
+
+ public boolean test(Message message) {
+ return field.equals(message.getSource()) && message.getSeverity() == Severity.ERROR;
+ }
+ }
+
}
diff --git a/spring-webflow/src/main/java/org/springframework/webflow/mvc/view/MvcView.java b/spring-webflow/src/main/java/org/springframework/webflow/mvc/view/MvcView.java
index 7cdbb604..445d3184 100644
--- a/spring-webflow/src/main/java/org/springframework/webflow/mvc/view/MvcView.java
+++ b/spring-webflow/src/main/java/org/springframework/webflow/mvc/view/MvcView.java
@@ -18,6 +18,7 @@ package org.springframework.webflow.mvc.view;
import java.io.IOException;
import java.util.HashMap;
import java.util.Iterator;
+import java.util.List;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
@@ -39,7 +40,8 @@ import org.springframework.binding.mapping.MappingResultsCriteria;
import org.springframework.binding.mapping.impl.DefaultMapper;
import org.springframework.binding.mapping.impl.DefaultMapping;
import org.springframework.binding.mapping.impl.DefaultMappingContext;
-import org.springframework.binding.mapping.results.TargetAccessError;
+import org.springframework.binding.message.MessageBuilder;
+import org.springframework.binding.message.MessageResolver;
import org.springframework.validation.BindingResult;
import org.springframework.webflow.core.collection.ParameterMap;
import org.springframework.webflow.definition.TransitionDefinition;
@@ -51,7 +53,9 @@ import org.springframework.webflow.expression.DefaultExpressionParserFactory;
class MvcView implements View {
- private static final MappingResultsCriteria PROPERTY_NOT_FOUND_ERRORS = new PropertyNotFoundErrors();
+ private static final MappingResultsCriteria PROPERTY_NOT_FOUND_ERROR = new PropertyNotFoundError();
+
+ private static final MappingResultsCriteria MAPPING_ERROR = new MappingError();
private org.springframework.web.servlet.View view;
@@ -113,6 +117,7 @@ class MvcView implements View {
mappingResults = bind(model);
if (mappingResults.hasErrorResults() && !onlyPropertyNotFoundErrorsPresent(mappingResults)) {
viewErrors = true;
+ addErrorMessages(mappingResults);
}
}
}
@@ -187,7 +192,22 @@ class MvcView implements View {
}
private boolean onlyPropertyNotFoundErrorsPresent(MappingResults results) {
- return results.getResults(PROPERTY_NOT_FOUND_ERRORS).size() == mappingResults.getErrorResults().size();
+ return results.getResults(PROPERTY_NOT_FOUND_ERROR).size() == mappingResults.getErrorResults().size();
+ }
+
+ private void addErrorMessages(MappingResults results) {
+ List errors = results.getResults(MAPPING_ERROR);
+ for (Iterator it = errors.iterator(); it.hasNext();) {
+ MappingResult error = (MappingResult) it.next();
+ context.getMessageContext().addMessage(message(error));
+ }
+ }
+
+ private MessageResolver message(MappingResult error) {
+ String field = error.getMapping().getTargetExpression().getExpressionString();
+ String errorCode = error.getResult().getErrorCode();
+ return new MessageBuilder().error().source(field).code(errorCode).arg(field).defaultText(
+ errorCode + " on " + field).build();
}
private void determineEventId(RequestContext context) {
@@ -237,10 +257,15 @@ class MvcView implements View {
return null;
}
- private static class PropertyNotFoundErrors implements MappingResultsCriteria {
+ private static class PropertyNotFoundError implements MappingResultsCriteria {
public boolean test(MappingResult result) {
- return result.getResult() instanceof TargetAccessError
- && result.getResult().getErrorCode().equals("propertyNotFound");
+ return result.getResult().isError() && "propertyNotFound".equals(result.getResult().getErrorCode());
+ }
+ }
+
+ private static class MappingError implements MappingResultsCriteria {
+ public boolean test(MappingResult result) {
+ return result.getResult().isError() && !PROPERTY_NOT_FOUND_ERROR.test(result);
}
}