From 55b3dc58a1c67a5d7a05aa66d2e6e0ec0a9d4b67 Mon Sep 17 00:00:00 2001 From: Keith Donald Date: Thu, 26 Feb 2009 23:05:21 +0000 Subject: [PATCH] initial add of validation failure concept to provide errors equivalent in validation context --- .../message/DefaultMessageResolver.java | 12 ++-- .../binding/message/MessageBuilder.java | 2 +- .../validation/PropertyValidationFailure.java | 37 ++++++++++ .../binding/validation/ValidationContext.java | 19 ++++- .../binding/validation/ValidationFailure.java | 68 ++++++++++++++++++ .../validation/ValidationFailureBuilder.java | 72 +++++++++++++++++++ ...lidationFailureMessageResolverFactory.java | 18 +++++ .../webflow/mvc/view/AbstractMvcView.java | 2 +- .../validation/DefaultValidationContext.java | 5 ++ 9 files changed, 227 insertions(+), 8 deletions(-) create mode 100644 spring-binding/src/main/java/org/springframework/binding/validation/PropertyValidationFailure.java create mode 100644 spring-binding/src/main/java/org/springframework/binding/validation/ValidationFailure.java create mode 100644 spring-binding/src/main/java/org/springframework/binding/validation/ValidationFailureBuilder.java create mode 100644 spring-binding/src/main/java/org/springframework/binding/validation/ValidationFailureMessageResolverFactory.java diff --git a/spring-binding/src/main/java/org/springframework/binding/message/DefaultMessageResolver.java b/spring-binding/src/main/java/org/springframework/binding/message/DefaultMessageResolver.java index 48180e8b..3e2b0efc 100644 --- a/spring-binding/src/main/java/org/springframework/binding/message/DefaultMessageResolver.java +++ b/spring-binding/src/main/java/org/springframework/binding/message/DefaultMessageResolver.java @@ -22,14 +22,18 @@ import org.springframework.context.MessageSourceResolvable; import org.springframework.core.style.ToStringCreator; class DefaultMessageResolver implements MessageResolver, MessageSourceResolvable { + private Object source; + private String[] codes; + private Severity severity; + private Object[] args; + private String defaultText; - public DefaultMessageResolver(Object source, String[] codes, Severity severity, Object[] args, - String defaultText) { + public DefaultMessageResolver(Object source, String[] codes, Severity severity, Object[] args, String defaultText) { this.source = source; this.codes = codes; this.severity = severity; @@ -56,7 +60,7 @@ class DefaultMessageResolver implements MessageResolver, MessageSourceResolvable } public String toString() { - return new ToStringCreator(this).append("source", source).append("severity", severity).append("codes", - codes).append("args", args).append("defaultText", defaultText).toString(); + return new ToStringCreator(this).append("source", source).append("severity", severity).append("codes", codes) + .append("args", args).append("defaultText", defaultText).toString(); } } \ 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 index 4bf1f01c..34140612 100644 --- a/spring-binding/src/main/java/org/springframework/binding/message/MessageBuilder.java +++ b/spring-binding/src/main/java/org/springframework/binding/message/MessageBuilder.java @@ -181,7 +181,7 @@ public class MessageBuilder { /** * 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. + * 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 */ diff --git a/spring-binding/src/main/java/org/springframework/binding/validation/PropertyValidationFailure.java b/spring-binding/src/main/java/org/springframework/binding/validation/PropertyValidationFailure.java new file mode 100644 index 00000000..9eeff008 --- /dev/null +++ b/spring-binding/src/main/java/org/springframework/binding/validation/PropertyValidationFailure.java @@ -0,0 +1,37 @@ +package org.springframework.binding.validation; + +import java.util.Map; + +import org.springframework.binding.message.Severity; + +/** + * A failure that occurred against a specific property on the object being violated. + * + * @author Keith Donald + */ +public final class PropertyValidationFailure extends ValidationFailure { + + private String propertyName; + + /** + * Creates a new property validation failure. + * @param propertyName the property name + * @param code the failure code + * @param severity the severity + * @param arguments named failure arguments + * @param defaultMessage default message text + */ + public PropertyValidationFailure(String propertyName, String code, Severity severity, Map arguments, + String defaultMessage) { + super(code, severity, arguments, defaultMessage); + this.propertyName = propertyName; + } + + /** + * The name of the property. + */ + public String getPropertyName() { + return propertyName; + } + +} diff --git a/spring-binding/src/main/java/org/springframework/binding/validation/ValidationContext.java b/spring-binding/src/main/java/org/springframework/binding/validation/ValidationContext.java index 444ee38f..49cf5aba 100644 --- a/spring-binding/src/main/java/org/springframework/binding/validation/ValidationContext.java +++ b/spring-binding/src/main/java/org/springframework/binding/validation/ValidationContext.java @@ -21,7 +21,7 @@ import java.security.Principal; import org.springframework.binding.message.MessageContext; /** - * A validator context. Allows for recording validation error messages, as well as validating data entered by the user. + * A context for a validator to use to access user data and report validation failures. * * @author Keith Donald * @author Scott Andrews @@ -29,7 +29,9 @@ import org.springframework.binding.message.MessageContext; public interface ValidationContext { /** - * The context for recording validation messages + * A context for adding messages to display to the user directly. The {@link #addFailure(ValidationFailure)} + * operation provides a higher level of abstraction for reporting validation failures. This method provides more + * control over the actual message added. */ public MessageContext getMessageContext(); @@ -49,4 +51,17 @@ public interface ValidationContext { * @return the value the user entered in the field bound to the property */ public Object getUserValue(String property); + + /** + * Add a validation failure to this context. Called by a validator to report failures against the current object + * being validated. Use to report a general failure against the object, or a specific failure against a property on + * the object. The failure code provided is typically mapped to one or message codes that result in a Message being + * added to the {@link MessageContext}. + * @param failure the validation failure + * @see #getMessageContext() + * @see ValidationFailureMessageResolverFactory + * @see PropertyValidationFailure + */ + public void addFailure(ValidationFailure failure); + } diff --git a/spring-binding/src/main/java/org/springframework/binding/validation/ValidationFailure.java b/spring-binding/src/main/java/org/springframework/binding/validation/ValidationFailure.java new file mode 100644 index 00000000..edd1079c --- /dev/null +++ b/spring-binding/src/main/java/org/springframework/binding/validation/ValidationFailure.java @@ -0,0 +1,68 @@ +package org.springframework.binding.validation; + +import java.util.Map; + +import org.springframework.binding.message.Severity; + +/** + * A failure that occurred during validation. Each failure has a code that uniquely identifies the validation constraint + * that was violated; for example "required", or "maxLength". A failure has a severity describing the intensity of the + * violation. A failure may have additional arguments that can be used as named parameters in a UI display message. A + * failure can also have a default message to display if no suitable message can be resolved. + */ +public class ValidationFailure { + + private String code; + + private Severity severity; + + private Map arguments; + + private String defaultMessage; + + /** + * Creates a new validation failure + * @param code the failure code + * @param severity the severity + * @param arguments named failure arguments + * @param defaultMessage the default message text + */ + public ValidationFailure(String code, Severity severity, Map arguments, String defaultMessage) { + this.code = code; + this.severity = severity; + this.arguments = arguments; + this.defaultMessage = defaultMessage; + } + + /** + * The failure code, uniquely identifying the validation constraint that failed. + */ + public String getCode() { + return code; + } + + /** + * The severity of the failure, which measures the impact of the constraint violation. + */ + public Severity getSeverity() { + return severity; + } + + /** + * An map of arguments that can be used as named parameters in the message associated with this validation failure. + * Each constraint that can fail defines a set of arguments that are specific to it. For example, a range constriant + * might define arguments of "min" and "max" of Integer values. In the message bundle, you then might see + * "range=The ${label} field value must be between ${min} and ${max}". + */ + public Map getArguments() { + return arguments; + } + + /** + * The default message to display if no suitable display message can be resolved for this failure. + */ + public String getDefaultMessage() { + return defaultMessage; + } + +} diff --git a/spring-binding/src/main/java/org/springframework/binding/validation/ValidationFailureBuilder.java b/spring-binding/src/main/java/org/springframework/binding/validation/ValidationFailureBuilder.java new file mode 100644 index 00000000..38b5ee86 --- /dev/null +++ b/spring-binding/src/main/java/org/springframework/binding/validation/ValidationFailureBuilder.java @@ -0,0 +1,72 @@ +/* + * Copyright 2004-2008 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.binding.validation; + +import java.util.Map; +import java.util.TreeMap; + +import org.springframework.binding.message.Severity; +import org.springframework.util.StringUtils; + +public class ValidationFailureBuilder { + + private String propertyName; + + private String constraint; + + private Severity severity; + + private Map args = new TreeMap(); + + private String defaultText; + + public ValidationFailureBuilder forProperty(String propertyName) { + this.propertyName = propertyName; + return this; + } + + public ValidationFailureBuilder constraint(String constraint) { + this.constraint = constraint; + return this; + } + + public ValidationFailureBuilder warning() { + severity = Severity.WARNING; + return this; + } + + public ValidationFailureBuilder error() { + severity = Severity.ERROR; + return this; + } + + public ValidationFailureBuilder arg(String name, Object value) { + args.put(name, value); + return this; + } + + public ValidationFailure build() { + if (severity == null) { + severity = Severity.ERROR; + } + if (StringUtils.hasText(propertyName)) { + return new PropertyValidationFailure(propertyName, constraint, severity, args, defaultText); + } else { + return new ValidationFailure(constraint, severity, args, defaultText); + } + } + +} diff --git a/spring-binding/src/main/java/org/springframework/binding/validation/ValidationFailureMessageResolverFactory.java b/spring-binding/src/main/java/org/springframework/binding/validation/ValidationFailureMessageResolverFactory.java new file mode 100644 index 00000000..a7b392f9 --- /dev/null +++ b/spring-binding/src/main/java/org/springframework/binding/validation/ValidationFailureMessageResolverFactory.java @@ -0,0 +1,18 @@ +package org.springframework.binding.validation; + +import org.springframework.binding.message.MessageResolver; + +/** + * Translates a validation failure into a resolvable message. + * + * @author Keith Donald + */ +public interface ValidationFailureMessageResolverFactory { + + /** + * Creates a new message resolver for the validation failure. + * @param failure the validation failure + * @return the message resolver for the failure + */ + MessageResolver createMessageResolver(ValidationFailure failure); +} diff --git a/spring-webflow/src/main/java/org/springframework/webflow/mvc/view/AbstractMvcView.java b/spring-webflow/src/main/java/org/springframework/webflow/mvc/view/AbstractMvcView.java index 911a3bcf..9096b836 100644 --- a/spring-webflow/src/main/java/org/springframework/webflow/mvc/view/AbstractMvcView.java +++ b/spring-webflow/src/main/java/org/springframework/webflow/mvc/view/AbstractMvcView.java @@ -184,7 +184,7 @@ public abstract class AbstractMvcView implements View { Object model = getModelObject(); if (model == null) { if (logger.isDebugEnabled()) { - logger.debug("No model to work with; done processing user event"); + logger.debug("No model to bind to; done processing user event"); } return; } diff --git a/spring-webflow/src/main/java/org/springframework/webflow/validation/DefaultValidationContext.java b/spring-webflow/src/main/java/org/springframework/webflow/validation/DefaultValidationContext.java index d4494793..58675d52 100644 --- a/spring-webflow/src/main/java/org/springframework/webflow/validation/DefaultValidationContext.java +++ b/spring-webflow/src/main/java/org/springframework/webflow/validation/DefaultValidationContext.java @@ -8,6 +8,7 @@ import org.springframework.binding.mapping.MappingResults; import org.springframework.binding.mapping.MappingResultsCriteria; import org.springframework.binding.message.MessageContext; import org.springframework.binding.validation.ValidationContext; +import org.springframework.binding.validation.ValidationFailure; import org.springframework.webflow.execution.RequestContext; public class DefaultValidationContext implements ValidationContext { @@ -47,6 +48,10 @@ public class DefaultValidationContext implements ValidationContext { return null; } + public void addFailure(ValidationFailure failure) { + // TODO + } + private static class PropertyMappingResult implements MappingResultsCriteria { private String property;