BindException.java
Upload User: jiancairen
Upload Date: 2007-08-27
Package Size: 26458k
Code Size: 11k
Category:

Java Develop

Development Platform:

Java

  1. /*
  2.  * Copyright 2002-2004 the original author or authors.
  3.  *
  4.  * Licensed under the Apache License, Version 2.0 (the "License");
  5.  * you may not use this file except in compliance with the License.
  6.  * You may obtain a copy of the License at
  7.  *
  8.  *      http://www.apache.org/licenses/LICENSE-2.0
  9.  *
  10.  * Unless required by applicable law or agreed to in writing, software
  11.  * distributed under the License is distributed on an "AS IS" BASIS,
  12.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13.  * See the License for the specific language governing permissions and
  14.  * limitations under the License.
  15.  */
  16. package org.springframework.validation;
  17. import java.beans.PropertyEditor;
  18. import java.util.Collections;
  19. import java.util.EmptyStackException;
  20. import java.util.HashMap;
  21. import java.util.Iterator;
  22. import java.util.LinkedList;
  23. import java.util.List;
  24. import java.util.Map;
  25. import java.util.Stack;
  26. import org.springframework.beans.BeanWrapper;
  27. import org.springframework.beans.BeanWrapperImpl;
  28. /**
  29.  * Default implementation of the Errors interface, supporting
  30.  * registration and evaluation of binding errors.
  31.  * Slightly unusual, as it <i>is</i> an exception.
  32.  *
  33.  * <p>This is mainly a framework-internal class. Normally, application
  34.  * code will work with the Errors interface, or a DataBinder that in
  35.  * turn exposes a BindException via <code>getErrors()</code>.
  36.  *
  37.  * <p>Supports exporting a model, suitable for example for web MVC.
  38.  * Thus, it is sometimes used as parameter type instead of the Errors interface
  39.  * itself - if extracting the model makes sense in the respective context.
  40.  *
  41.  * @author Rod Johnson
  42.  * @author Juergen Hoeller
  43.  * @see #getModel
  44.  * @see DataBinder#getErrors
  45.  */
  46. public class BindException extends Exception implements Errors {
  47. /**
  48.  * Prefix for the name of the Errors instance in a model,
  49.  * followed by the object name.
  50.  */
  51. public static final String ERROR_KEY_PREFIX = BindException.class.getName() + ".";
  52. private final List errors = new LinkedList();
  53. private final BeanWrapper beanWrapper;
  54. private final String objectName;
  55. private MessageCodesResolver messageCodesResolver = new DefaultMessageCodesResolver();
  56. private String nestedPath = "";
  57. private final Stack nestedPathStack = new Stack();
  58. /**
  59.  * Create a new BindException instance.
  60.  * @param target target object to bind onto
  61.  * @param name name of the target object
  62.  * @see DefaultMessageCodesResolver
  63.  */
  64. public BindException(Object target, String name) {
  65. this.beanWrapper = new BeanWrapperImpl(target);
  66. this.objectName = name;
  67. this.nestedPath = "";
  68. }
  69. /**
  70.  * Return the BeanWrapper that this instance uses.
  71.  */
  72. protected BeanWrapper getBeanWrapper() {
  73. return beanWrapper;
  74. }
  75. /**
  76.  * Return the wrapped target object.
  77.  */
  78. public Object getTarget() {
  79. return this.beanWrapper.getWrappedInstance();
  80. }
  81. public String getObjectName() {
  82. return objectName;
  83. }
  84. /**
  85.  * Set the strategy to use for resolving errors into message codes.
  86.  * Default is DefaultMessageCodesResolver.
  87.  * @see DefaultMessageCodesResolver
  88.  */
  89. public void setMessageCodesResolver(MessageCodesResolver messageCodesResolver) {
  90. this.messageCodesResolver = messageCodesResolver;
  91. }
  92. /**
  93.  * Return the strategy to use for resolving errors into message codes.
  94.  */
  95. public MessageCodesResolver getMessageCodesResolver() {
  96. return messageCodesResolver;
  97. }
  98. public void setNestedPath(String nestedPath) {
  99. doSetNestedPath(nestedPath);
  100. this.nestedPathStack.clear();
  101. }
  102. public String getNestedPath() {
  103. return nestedPath;
  104. }
  105. public void pushNestedPath(String subPath) {
  106. this.nestedPathStack.push(this.nestedPath);
  107. doSetNestedPath(this.nestedPath + subPath);
  108. }
  109. public void popNestedPath() throws IllegalArgumentException {
  110. try {
  111. String formerNestedPath = (String) this.nestedPathStack.pop();
  112. doSetNestedPath(formerNestedPath);
  113. }
  114. catch (EmptyStackException ex) {
  115. throw new IllegalStateException("Cannot pop nested path: no nested path on stack");
  116. }
  117. }
  118. /**
  119.  * Actually set the nested path.
  120.  * Delegated to by setNestedPath and pushNestedPath.
  121.  */
  122. protected void doSetNestedPath(String nestedPath) {
  123. if (nestedPath == null) {
  124. nestedPath = "";
  125. }
  126. if (nestedPath.length() > 0 && !nestedPath.endsWith(NESTED_PATH_SEPARATOR)) {
  127. nestedPath += NESTED_PATH_SEPARATOR;
  128. }
  129. this.nestedPath = nestedPath;
  130. }
  131. /**
  132.  * Transform the given field into its full path,
  133.  * regarding the nested path of this instance.
  134.  */
  135. protected String fixedField(String field) {
  136. return this.nestedPath + field;
  137. }
  138. public void reject(String errorCode, String defaultMessage) {
  139. reject(errorCode, null, defaultMessage);
  140. }
  141. public void reject(String errorCode, Object[] errorArgs, String defaultMessage) {
  142. addError(new ObjectError(this.objectName, resolveMessageCodes(errorCode), errorArgs, defaultMessage));
  143. }
  144. public void rejectValue(String field, String errorCode, String defaultMessage) {
  145. rejectValue(field, errorCode, null, defaultMessage);
  146. }
  147. public void rejectValue(String field, String errorCode, Object[] errorArgs, String defaultMessage) {
  148. String fixedField = fixedField(field);
  149. Object newVal = getBeanWrapper().getPropertyValue(fixedField);
  150. FieldError fe = new FieldError(
  151. this.objectName, fixedField, newVal, false,
  152. resolveMessageCodes(errorCode, field), errorArgs, defaultMessage);
  153. addError(fe);
  154. }
  155. protected String[] resolveMessageCodes(String errorCode) {
  156. return this.messageCodesResolver.resolveMessageCodes(errorCode, this.objectName);
  157. }
  158. protected String[] resolveMessageCodes(String errorCode, String field) {
  159. String fixedField = fixedField(field);
  160. Class fieldType = this.beanWrapper.getPropertyType(fixedField);
  161. return this.messageCodesResolver.resolveMessageCodes(errorCode, this.objectName, fixedField, fieldType);
  162. }
  163. /**
  164.  * Add a FieldError to the errors list.
  165.  * Intended to be used by subclasses like DataBinder.
  166.  */
  167. protected void addError(ObjectError error) {
  168. this.errors.add(error);
  169. }
  170. public boolean hasErrors() {
  171. return !this.errors.isEmpty();
  172. }
  173. public int getErrorCount() {
  174. return this.errors.size();
  175. }
  176. public List getAllErrors() {
  177. return Collections.unmodifiableList(this.errors);
  178. }
  179. public boolean hasGlobalErrors() {
  180. return (getGlobalErrorCount() > 0);
  181. }
  182. public int getGlobalErrorCount() {
  183. return getGlobalErrors().size();
  184. }
  185. public List getGlobalErrors() {
  186. List result = new LinkedList();
  187. for (Iterator it = this.errors.iterator(); it.hasNext();) {
  188. Object error = it.next();
  189. if (!(error instanceof FieldError)) {
  190. result.add(error);
  191. }
  192. }
  193. return Collections.unmodifiableList(result);
  194. }
  195. public ObjectError getGlobalError() {
  196. for (Iterator it = this.errors.iterator(); it.hasNext();) {
  197. ObjectError objectError = (ObjectError) it.next();
  198. if (!(objectError instanceof FieldError)) {
  199. return objectError;
  200. }
  201. }
  202. return null;
  203. }
  204. public boolean hasFieldErrors(String field) {
  205. return (getFieldErrorCount(field) > 0);
  206. }
  207. public int getFieldErrorCount(String field) {
  208. return getFieldErrors(field).size();
  209. }
  210. public List getFieldErrors(String field) {
  211. List result = new LinkedList();
  212. String fixedField = fixedField(field);
  213. for (Iterator it = this.errors.iterator(); it.hasNext();) {
  214. Object error = it.next();
  215. if (error instanceof FieldError && isMatchingFieldError(fixedField, ((FieldError) error))) {
  216. result.add(error);
  217. }
  218. }
  219. return Collections.unmodifiableList(result);
  220. }
  221. public FieldError getFieldError(String field) {
  222. String fixedField = fixedField(field);
  223. for (Iterator it = this.errors.iterator(); it.hasNext();) {
  224. Object error = it.next();
  225. if (error instanceof FieldError) {
  226. FieldError fe = (FieldError) error;
  227. if (isMatchingFieldError(fixedField, fe)) {
  228. return fe;
  229. }
  230. }
  231. }
  232. return null;
  233. }
  234. /**
  235.  * Check whether the given FieldError matches the given field.
  236.  * @param field the field that we are looking up FieldErrors for
  237.  * @param fieldError the candidate FieldError
  238.  * @return whether the FieldError matches the given field
  239.  */
  240. protected boolean isMatchingFieldError(String field, FieldError fieldError) {
  241. return (field.equals(fieldError.getField()) ||
  242. (field.endsWith("*") && fieldError.getField().startsWith(field.substring(0, field.length() - 1))));
  243. }
  244. public Object getFieldValue(String field) {
  245. FieldError fe = getFieldError(field);
  246. String fixedField = fixedField(field);
  247. // use rejected value in case of error, current bean property value else
  248. Object value = (fe != null) ? fe.getRejectedValue() : getBeanWrapper().getPropertyValue(fixedField);
  249. // apply custom editor, but not on binding failures like type mismatches
  250. if (fe == null || !fe.isBindingFailure()) {
  251. PropertyEditor customEditor = getCustomEditor(fixedField);
  252. if (customEditor != null) {
  253. customEditor.setValue(value);
  254. return customEditor.getAsText();
  255. }
  256. }
  257. return value;
  258. }
  259. /**
  260.  * Retrieve the custom PropertyEditor for the given field, if any.
  261.  * @param field the field name
  262.  * @return the custom PropertyEditor, or null
  263.  */
  264. public PropertyEditor getCustomEditor(String field) {
  265. String fixedField = fixedField(field);
  266. Class type = getBeanWrapper().getPropertyType(fixedField);
  267. return getBeanWrapper().findCustomEditor(type, fixedField);
  268. }
  269. /**
  270.  * Return a model Map for the obtained state, exposing an Errors
  271.  * instance as '{@link #ERROR_KEY_PREFIX ERROR_KEY_PREFIX} + objectName'
  272.  * and the object itself.
  273.  * <p>Note that the Map is constructed every time you're calling this method.
  274.  * Adding things to the map and then re-calling this method will not work.
  275.  * <p>The attributes in the model Map returned by this method are usually
  276.  * included in the ModelAndView for a form view that uses Spring's bind tag,
  277.  * which needs access to the Errors instance. Spring's SimpleFormController
  278.  * will do this for you when rendering its form or success view. When
  279.  * building the ModelAndView yourself, you need to include the attributes
  280.  * from the model Map returned by this method yourself.
  281.  * @see #getObjectName
  282.  * @see #ERROR_KEY_PREFIX
  283.  * @see org.springframework.web.servlet.ModelAndView
  284.  * @see org.springframework.web.servlet.tags.BindTag
  285.  * @see org.springframework.web.servlet.mvc.SimpleFormController
  286.  */
  287. public final Map getModel() {
  288. Map model = new HashMap();
  289. // errors instance, even if no errors
  290. model.put(ERROR_KEY_PREFIX + this.objectName, this);
  291. // mapping from name to target object
  292. model.put(this.objectName, this.beanWrapper.getWrappedInstance());
  293. return model;
  294. }
  295. /**
  296.  * Returns diagnostic information about the errors held in this object.
  297.  */
  298. public String getMessage() {
  299. StringBuffer sb = new StringBuffer("BindException: ");
  300. sb.append(getErrorCount()).append(" errors");
  301. Iterator it = this.errors.iterator();
  302. while (it.hasNext()) {
  303. sb.append("; ").append(it.next());
  304. }
  305. return sb.toString();
  306. }
  307. }