initial mvc message display

This commit is contained in:
Keith Donald
2008-04-01 18:50:56 +00:00
parent bc35b3d8a6
commit e9f070b3e3
10 changed files with 169 additions and 66 deletions

View File

@@ -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();
}

View File

@@ -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.

View File

@@ -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);
}
}

View File

@@ -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() {

View File

@@ -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");
}

View File

@@ -4,7 +4,6 @@
<wb-resource deploy-path="/" source-path="/src/main/webapp"/>
<wb-resource deploy-path="/WEB-INF/lib" source-path="/target/war-expanded/WEB-INF/lib"/>
<wb-resource deploy-path="/WEB-INF/classes" source-path="/src/main/resources"/>
<wb-resource deploy-path="/WEB-INF/classes" source-path="/src/test/resources"/>
<property name="java-output-path" value="target/classes"/>
<property name="context-root" value="/swf-booking-faces"/>
</wb-module>

View File

@@ -2,11 +2,8 @@
<project-modules id="moduleCoreId" project-version="1.5.0">
<wb-module deploy-name="swf-booking-mvc">
<wb-resource deploy-path="/" source-path="/src/main/webapp"/>
<wb-resource deploy-path="/WEB-INF/lib" source-path="/lib/global"/>
<wb-resource deploy-path="/WEB-INF/classes" source-path="/src/main/java"/>
<wb-resource deploy-path="/WEB-INF/lib" source-path="/target/war-expanded/WEB-INF/lib"/>
<wb-resource deploy-path="/WEB-INF/classes" source-path="/src/main/resources"/>
<wb-resource deploy-path="/WEB-INF/classes" source-path="/src/test/java"/>
<wb-resource deploy-path="/WEB-INF/classes" source-path="/src/test/resources"/>
<property name="java-output-path" value="target/classes"/>
<property name="context-root" value="/swf-booking-mvc"/>
</wb-module>

View File

@@ -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

View File

@@ -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;
}
}
}

View File

@@ -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);
}
}