several bug fixes

This commit is contained in:
Keith Donald
2008-04-01 22:49:10 +00:00
parent 4df670ae70
commit c579f1852b
9 changed files with 251 additions and 9 deletions

View File

@@ -52,9 +52,8 @@ class DefaultMessageContext implements StateManageableMessageContext {
public Message[] getAllMessages() {
List messages = new ArrayList();
Iterator it = objectMessages.keySet().iterator();
while (it.hasNext()) {
messages.addAll((List) objectMessages.get(it.next()));
for (Iterator it = objectMessages.values().iterator(); it.hasNext();) {
messages.addAll((List) it.next());
}
return (Message[]) messages.toArray(new Message[messages.size()]);
}
@@ -80,6 +79,20 @@ class DefaultMessageContext implements StateManageableMessageContext {
return (Message[]) messages.toArray(new Message[messages.size()]);
}
public boolean hasErrorMessages() {
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 (message.getSeverity() == Severity.ERROR) {
return true;
}
}
}
return false;
}
public void addMessage(MessageResolver messageResolver) {
Locale currentLocale = LocaleContextHolder.getLocale();
Message message = messageResolver.resolveMessage(messageSource, currentLocale);

View File

@@ -1,6 +1,7 @@
package org.springframework.binding.message;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Locale;
@@ -84,6 +85,18 @@ public class MessageBuilder {
return this;
}
/**
* Records that the message being built should try and resolve its text using the codes provided. Adds the codes to
* the codes list. Successive calls to this method add additional codes. Codes are applied in the order they are
* added.
* @param codes the message codes
* @return this, for fluent API usage
*/
public MessageBuilder codes(String[] codes) {
this.codes.add(Arrays.asList(codes));
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.
@@ -95,6 +108,17 @@ public class MessageBuilder {
return this;
}
/**
* Records that the message being built has variable arguments. Adds the args 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 args(Object[] args) {
this.args.add(Arrays.asList(args));
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
@@ -107,6 +131,20 @@ public class MessageBuilder {
return this;
}
/**
* Records that the message being built has variable arguments, whose display values are also
* {@link MessageSourceResolvable} instances. Adds the args to the args list. Successive calls to this method add
* additional resolvable args. Args are applied in the order they are added.
* @param args the resolvable message arguments
* @return this, for fluent API usage
*/
public MessageBuilder resolvableArgs(Object[] args) {
for (int i = 0; i < args.length; i++) {
this.args.add(new ResolvableArgument(args[i]));
}
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 alway be used as the text.

View File

@@ -24,6 +24,12 @@ public interface MessageContext {
*/
public Message[] getMessagesByCriteria(MessageCriteria criteria);
/**
* Returns true if there are error messages in this context.
* @return error messages
*/
public boolean hasErrorMessages();
/**
* Add a new message to this context.
* @param messageResolver the resolver that will resolve the message to be added

View File

@@ -118,6 +118,10 @@ public class FlowFacesContextTests extends TestCase {
throw new UnsupportedOperationException("Auto-generated method stub");
}
public boolean hasErrorMessages() {
throw new UnsupportedOperationException("Auto-generated method stub");
}
public void clearMessages() {
throw new UnsupportedOperationException("Auto-generated method stub");
}
@@ -150,6 +154,10 @@ public class FlowFacesContextTests extends TestCase {
throw new UnsupportedOperationException("Auto-generated method stub");
}
public boolean hasErrorMessages() {
throw new UnsupportedOperationException("Auto-generated method stub");
}
public void clearMessages() {
throw new UnsupportedOperationException("Auto-generated method stub");
}

View File

@@ -178,7 +178,7 @@ public class Booking implements Serializable {
this.creditCardExpiryYear = creditCardExpiryYear;
}
public boolean validate(MessageContext context) {
public boolean validateEnterBookingDetails(MessageContext context) {
Calendar calendar = Calendar.getInstance();
calendar.add(Calendar.DAY_OF_MONTH, -1);
boolean valid = true;

View File

@@ -176,7 +176,7 @@ public class Booking implements Serializable {
this.creditCardExpiryYear = creditCardExpiryYear;
}
public boolean validate(MessageContext context) {
public boolean validateEnterBookingDetails(MessageContext context) {
Calendar calendar = Calendar.getInstance();
calendar.add(Calendar.DAY_OF_MONTH, -1);
boolean valid = true;

View File

@@ -14,9 +14,7 @@
</on-start>
<view-state id="enterBookingDetails" model="booking">
<transition on="proceed" to="reviewBooking">
<evaluate expression="booking.validate(messageContext)"/>
</transition>
<transition on="proceed" to="reviewBooking" />
<transition on="cancel" to="cancel" bind="false" />
</view-state>

View File

@@ -0,0 +1,137 @@
package org.springframework.webflow.mvc.view;
import java.util.List;
import org.springframework.binding.message.MessageBuilder;
import org.springframework.binding.message.MessageContext;
import org.springframework.validation.Errors;
import org.springframework.validation.FieldError;
import org.springframework.validation.ObjectError;
public class MessageContextErrors implements Errors {
private MessageContext messageContext;
public MessageContextErrors(MessageContext messageContext) {
this.messageContext = messageContext;
}
public void reject(String errorCode) {
messageContext.addMessage(new MessageBuilder().error().code(errorCode).build());
}
public void reject(String errorCode, String defaultMessage) {
messageContext.addMessage(new MessageBuilder().error().code(errorCode).defaultText(defaultMessage).build());
}
public void reject(String errorCode, Object[] errorArgs, String defaultMessage) {
messageContext.addMessage(new MessageBuilder().error().code(errorCode).defaultText(defaultMessage).build());
}
public void rejectValue(String field, String errorCode) {
messageContext.addMessage(new MessageBuilder().error().source(field).code(errorCode).build());
}
public void rejectValue(String field, String errorCode, String defaultMessage) {
messageContext.addMessage(new MessageBuilder().error().source(field).code(errorCode)
.defaultText(defaultMessage).build());
}
public void rejectValue(String field, String errorCode, Object[] errorArgs, String defaultMessage) {
messageContext.addMessage(new MessageBuilder().error().source(field).code(errorCode).args(errorArgs)
.defaultText(defaultMessage).build());
}
public void addAllErrors(Errors errors) {
throw new UnsupportedOperationException("Should not be called by a validator");
}
public List getAllErrors() {
throw new UnsupportedOperationException("Should not be called by a validator");
}
public int getErrorCount() {
throw new UnsupportedOperationException("Should not be called by a validator");
}
public FieldError getFieldError() {
throw new UnsupportedOperationException("Should not be called by a validator");
}
public FieldError getFieldError(String field) {
throw new UnsupportedOperationException("Should not be called by a validator");
}
public int getFieldErrorCount() {
throw new UnsupportedOperationException("Should not be called by a validator");
}
public int getFieldErrorCount(String field) {
throw new UnsupportedOperationException("Should not be called by a validator");
}
public List getFieldErrors() {
throw new UnsupportedOperationException("Should not be called by a validator");
}
public List getFieldErrors(String field) {
throw new UnsupportedOperationException("Should not be called by a validator");
}
public Class getFieldType(String field) {
throw new UnsupportedOperationException("Should not be called by a validator");
}
public Object getFieldValue(String field) {
throw new UnsupportedOperationException("Should not be called by a validator");
}
public ObjectError getGlobalError() {
throw new UnsupportedOperationException("Should not be called by a validator");
}
public int getGlobalErrorCount() {
throw new UnsupportedOperationException("Should not be called by a validator");
}
public List getGlobalErrors() {
throw new UnsupportedOperationException("Should not be called by a validator");
}
public String getNestedPath() {
throw new UnsupportedOperationException("Should not be called by a validator");
}
public String getObjectName() {
throw new UnsupportedOperationException("Should not be called by a validator");
}
public boolean hasErrors() {
throw new UnsupportedOperationException("Should not be called by a validator");
}
public boolean hasFieldErrors() {
throw new UnsupportedOperationException("Should not be called by a validator");
}
public boolean hasFieldErrors(String field) {
throw new UnsupportedOperationException("Should not be called by a validator");
}
public boolean hasGlobalErrors() {
throw new UnsupportedOperationException("Should not be called by a validator");
}
public void popNestedPath() throws IllegalStateException {
throw new UnsupportedOperationException("Should not be called by a validator");
}
public void pushNestedPath(String subPath) {
throw new UnsupportedOperationException("Should not be called by a validator");
}
public void setNestedPath(String nestedPath) {
throw new UnsupportedOperationException("Should not be called by a validator");
}
}

View File

@@ -16,6 +16,7 @@
package org.springframework.webflow.mvc.view;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
@@ -24,6 +25,7 @@ import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.binding.collection.MapAdaptable;
import org.springframework.binding.convert.ConversionException;
import org.springframework.binding.convert.ConversionExecutor;
@@ -41,8 +43,12 @@ 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.message.MessageBuilder;
import org.springframework.binding.message.MessageContext;
import org.springframework.binding.message.MessageResolver;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.validation.BindingResult;
import org.springframework.validation.Errors;
import org.springframework.webflow.core.collection.ParameterMap;
import org.springframework.webflow.definition.TransitionDefinition;
import org.springframework.webflow.definition.TransitionableStateDefinition;
@@ -115,9 +121,14 @@ class MvcView implements View {
}
if (shouldBind(model)) {
mappingResults = bind(model);
if (mappingResults.hasErrorResults() && !onlyPropertyNotFoundErrorsPresent(mappingResults)) {
if (hasMappingErrors(mappingResults)) {
viewErrors = true;
addErrorMessages(mappingResults);
} else {
validate(model);
if (context.getMessageContext().hasErrorMessages()) {
viewErrors = true;
}
}
}
}
@@ -191,6 +202,10 @@ class MvcView implements View {
}
}
private boolean hasMappingErrors(MappingResults results) {
return results.hasErrorResults() && !onlyPropertyNotFoundErrorsPresent(results);
}
private boolean onlyPropertyNotFoundErrorsPresent(MappingResults results) {
return results.getResults(PROPERTY_NOT_FOUND_ERROR).size() == mappingResults.getErrorResults().size();
}
@@ -212,6 +227,33 @@ class MvcView implements View {
.defaultText(errorCode + " on " + field).build();
}
private void validate(Object model) {
String validateMethodName = "validate" + StringUtils.capitalize(context.getCurrentState().getId());
Method validateMethod = ReflectionUtils.findMethod(model.getClass(), validateMethodName,
new Class[] { MessageContext.class });
if (validateMethod != null) {
ReflectionUtils.invokeMethod(validateMethod, model, new Object[] { context.getMessageContext() });
}
BeanFactory beanFactory = context.getActiveFlow().getBeanFactory();
String validatorName = getModelExpression().getExpressionString() + "Validator";
if (beanFactory.containsBean(validatorName)) {
Object validator = beanFactory.getBean(validatorName);
validateMethod = ReflectionUtils.findMethod(validator.getClass(), validateMethodName, new Class[] {
model.getClass(), MessageContext.class });
if (validateMethod != null) {
ReflectionUtils.invokeMethod(validateMethod, validator, new Object[] { model,
context.getMessageContext() });
} else {
validateMethod = ReflectionUtils.findMethod(validator.getClass(), validateMethodName, new Class[] {
model.getClass(), Errors.class });
if (validateMethod != null) {
ReflectionUtils.invokeMethod(validateMethod, validator, new Object[] { model,
new MessageContextErrors(context.getMessageContext()) });
}
}
}
}
private void determineEventId(RequestContext context) {
eventId = findParameter("_eventId", context.getRequestParameters());
}