All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.springframework.validation.BindException Maven / Gradle / Ivy

/*
 * Copyright 2002-2005 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.validation;

import java.beans.PropertyEditor;
import java.util.Collections;
import java.util.EmptyStackException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Stack;

import org.springframework.beans.BeanWrapper;
import org.springframework.beans.BeanWrapperImpl;
import org.springframework.util.Assert;

/**
 * Default implementation of the Errors interface, supporting
 * registration and evaluation of binding errors.
 * Slightly unusual, as it is an exception.
 *
 * 

This is mainly a framework-internal class. Normally, application * code will work with the Errors interface, or a DataBinder that in * turn exposes a BindException via getErrors(). * *

Supports exporting a model, suitable for example for web MVC. * Thus, it is sometimes used as parameter type instead of the Errors interface * itself - if extracting the model makes sense in the particular context. * * @author Rod Johnson * @author Juergen Hoeller * @see #getModel * @see DataBinder#getErrors */ public class BindException extends Exception implements Errors { /** * Prefix for the name of the Errors instance in a model, * followed by the object name. */ public static final String ERROR_KEY_PREFIX = BindException.class.getName() + "."; private final List errors = new LinkedList(); private final Object target; private final String objectName; private transient BeanWrapper beanWrapper; private MessageCodesResolver messageCodesResolver = new DefaultMessageCodesResolver(); private String nestedPath = ""; private final Stack nestedPathStack = new Stack(); /** * Create a new BindException instance. * @param target target object to bind onto * @param objectName the name of the target object * @see DefaultMessageCodesResolver */ public BindException(Object target, String objectName) { Assert.notNull(target, "Target object must not be null"); this.target = target; this.objectName = objectName; } /** * Return the wrapped target object. */ public Object getTarget() { return target; } public String getObjectName() { return objectName; } /** * Return the BeanWrapper that this instance uses. * Creates a new one if none existed before. */ protected BeanWrapper getBeanWrapper() { if (this.beanWrapper == null) { this.beanWrapper = new BeanWrapperImpl(this.target); } return this.beanWrapper; } /** * Set the strategy to use for resolving errors into message codes. * Default is DefaultMessageCodesResolver. * @see DefaultMessageCodesResolver */ public void setMessageCodesResolver(MessageCodesResolver messageCodesResolver) { this.messageCodesResolver = messageCodesResolver; } /** * Return the strategy to use for resolving errors into message codes. */ public MessageCodesResolver getMessageCodesResolver() { return messageCodesResolver; } public void setNestedPath(String nestedPath) { doSetNestedPath(nestedPath); this.nestedPathStack.clear(); } public String getNestedPath() { return nestedPath; } public void pushNestedPath(String subPath) { this.nestedPathStack.push(getNestedPath()); doSetNestedPath(getNestedPath() + subPath); } public void popNestedPath() throws IllegalArgumentException { try { String formerNestedPath = (String) this.nestedPathStack.pop(); doSetNestedPath(formerNestedPath); } catch (EmptyStackException ex) { throw new IllegalStateException("Cannot pop nested path: no nested path on stack"); } } /** * Actually set the nested path. * Delegated to by setNestedPath and pushNestedPath. */ protected void doSetNestedPath(String nestedPath) { if (nestedPath == null) { nestedPath = ""; } if (nestedPath.length() > 0 && !nestedPath.endsWith(NESTED_PATH_SEPARATOR)) { nestedPath += NESTED_PATH_SEPARATOR; } this.nestedPath = nestedPath; } /** * Transform the given field into its full path, * regarding the nested path of this instance. */ protected String fixedField(String field) { return getNestedPath() + field; } public void reject(String errorCode) { reject(errorCode, null, null); } public void reject(String errorCode, String defaultMessage) { reject(errorCode, null, defaultMessage); } public void reject(String errorCode, Object[] errorArgs, String defaultMessage) { addError(new ObjectError(getObjectName(), resolveMessageCodes(errorCode), errorArgs, defaultMessage)); } public void rejectValue(String field, String errorCode) { rejectValue(field, errorCode, null, null); } public void rejectValue(String field, String errorCode, String defaultMessage) { rejectValue(field, errorCode, null, defaultMessage); } public void rejectValue(String field, String errorCode, Object[] errorArgs, String defaultMessage) { String fixedField = fixedField(field); Object newVal = getBeanWrapper().getPropertyValue(fixedField); FieldError fe = new FieldError( getObjectName(), fixedField, newVal, false, resolveMessageCodes(errorCode, field), errorArgs, defaultMessage); addError(fe); } /** * Resolve the given error code into message codes. * Calls the MessageCodesResolver with appropriate parameters. * @param errorCode the error code to resolve into message codes * @return the resolved message codes * @see #setMessageCodesResolver */ public String[] resolveMessageCodes(String errorCode) { return getMessageCodesResolver().resolveMessageCodes(errorCode, getObjectName()); } /** * Resolve the given error code into message codes for the given field. * Calls the MessageCodesResolver with appropriate parameters. * @param errorCode the error code to resolve into message codes * @param field the field to resolve message codes for * @return the resolved message codes * @see #setMessageCodesResolver */ public String[] resolveMessageCodes(String errorCode, String field) { String fixedField = fixedField(field); Class fieldType = getBeanWrapper().getPropertyType(fixedField); return getMessageCodesResolver().resolveMessageCodes(errorCode, getObjectName(), fixedField, fieldType); } /** * Add an ObjectError or FieldError to the errors list. *

Intended to be used by subclasses like DataBinder, * or by cooperating strategies like a BindingErrorProcessor. * @see ObjectError * @see FieldError * @see DataBinder * @see BindingErrorProcessor */ public void addError(ObjectError error) { this.errors.add(error); } public void addAllErrors(Errors errors) { this.errors.addAll(errors.getAllErrors()); } public boolean hasErrors() { return !this.errors.isEmpty(); } public int getErrorCount() { return this.errors.size(); } public List getAllErrors() { return Collections.unmodifiableList(this.errors); } public boolean hasGlobalErrors() { return (getGlobalErrorCount() > 0); } public int getGlobalErrorCount() { return getGlobalErrors().size(); } public List getGlobalErrors() { List result = new LinkedList(); for (Iterator it = this.errors.iterator(); it.hasNext();) { Object error = it.next(); if (!(error instanceof FieldError)) { result.add(error); } } return Collections.unmodifiableList(result); } public ObjectError getGlobalError() { for (Iterator it = this.errors.iterator(); it.hasNext();) { ObjectError objectError = (ObjectError) it.next(); if (!(objectError instanceof FieldError)) { return objectError; } } return null; } public boolean hasFieldErrors(String field) { return (getFieldErrorCount(field) > 0); } public int getFieldErrorCount(String field) { return getFieldErrors(field).size(); } public List getFieldErrors(String field) { List result = new LinkedList(); String fixedField = fixedField(field); for (Iterator it = this.errors.iterator(); it.hasNext();) { Object error = it.next(); if (error instanceof FieldError && isMatchingFieldError(fixedField, ((FieldError) error))) { result.add(error); } } return Collections.unmodifiableList(result); } public FieldError getFieldError(String field) { String fixedField = fixedField(field); for (Iterator it = this.errors.iterator(); it.hasNext();) { Object error = it.next(); if (error instanceof FieldError) { FieldError fe = (FieldError) error; if (isMatchingFieldError(fixedField, fe)) { return fe; } } } return null; } /** * Check whether the given FieldError matches the given field. * @param field the field that we are looking up FieldErrors for * @param fieldError the candidate FieldError * @return whether the FieldError matches the given field */ protected boolean isMatchingFieldError(String field, FieldError fieldError) { return (field.equals(fieldError.getField()) || (field.endsWith("*") && fieldError.getField().startsWith(field.substring(0, field.length() - 1)))); } public Object getFieldValue(String field) { FieldError fe = getFieldError(field); String fixedField = fixedField(field); // Use rejected value in case of error, current bean property value else. Object value = (fe != null) ? fe.getRejectedValue() : getBeanWrapper().getPropertyValue(fixedField); // Apply custom editor, but not on binding failures like type mismatches. if (fe == null || !fe.isBindingFailure()) { PropertyEditor customEditor = getCustomEditor(fixedField); if (customEditor != null) { customEditor.setValue(value); String textValue = customEditor.getAsText(); // If the PropertyEditor returned null, there is no appropriate // text representation for this value: only use it if non-null. if (textValue != null) { value = textValue; } } } return value; } /** * Retrieve the custom PropertyEditor for the given field, if any. * @param field the field name * @return the custom PropertyEditor, or null */ public PropertyEditor getCustomEditor(String field) { String fixedField = fixedField(field); Class type = getBeanWrapper().getPropertyType(fixedField); return getBeanWrapper().findCustomEditor(type, fixedField); } /** * Return a model Map for the obtained state, exposing an Errors * instance as '{@link #ERROR_KEY_PREFIX ERROR_KEY_PREFIX} + objectName' * and the object itself. *

Note that the Map is constructed every time you're calling this method. * Adding things to the map and then re-calling this method will not work. *

The attributes in the model Map returned by this method are usually * included in the ModelAndView for a form view that uses Spring's bind tag, * which needs access to the Errors instance. Spring's SimpleFormController * will do this for you when rendering its form or success view. When * building the ModelAndView yourself, you need to include the attributes * from the model Map returned by this method yourself. * @see #getObjectName * @see #ERROR_KEY_PREFIX * @see org.springframework.web.servlet.ModelAndView * @see org.springframework.web.servlet.tags.BindTag * @see org.springframework.web.servlet.mvc.SimpleFormController */ public Map getModel() { Map model = new HashMap(); // Errors instance, even if no errors. model.put(ERROR_KEY_PREFIX + getObjectName(), this); // Mapping from name to target object. model.put(getObjectName(), getTarget()); return model; } /** * Returns diagnostic information about the errors held in this object. */ public String getMessage() { StringBuffer sb = new StringBuffer("BindException: "); sb.append(getErrorCount()).append(" errors"); Iterator it = this.errors.iterator(); while (it.hasNext()) { sb.append("; ").append(it.next()); } return sb.toString(); } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy