BindStatus.java
上传用户:jiancairen
上传日期:2007-08-27
资源大小:26458k
文件大小:9k
源码类别:

Java编程

开发平台:

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.web.servlet.support;
  17. import java.beans.PropertyEditor;
  18. import java.util.Arrays;
  19. import java.util.List;
  20. import org.apache.commons.logging.Log;
  21. import org.apache.commons.logging.LogFactory;
  22. import org.springframework.beans.BeanWrapperImpl;
  23. import org.springframework.context.NoSuchMessageException;
  24. import org.springframework.util.StringUtils;
  25. import org.springframework.validation.BindException;
  26. import org.springframework.validation.Errors;
  27. import org.springframework.validation.ObjectError;
  28. import org.springframework.web.util.HtmlUtils;
  29. /**
  30.  * Simple adapter to expose the bind status of a field or object.
  31.  * Set as a variable both by the JSP bind tag and Velocity/FreeMarker macros.
  32.  *
  33.  * <p>Obviously, object status representations (i.e. errors at the object level
  34.  * rather than the field level) do not have an expression and a value but only
  35.  * error codes and messages. For simplicity's sake and to be able to use the same
  36.  * tags and macros, the same status class is used for both scenarios.
  37.  *
  38.  * @author Rod Johnson
  39.  * @author Juergen Hoeller
  40.  * @author Darren Davison
  41.  * @see RequestContext#getBindStatus
  42.  * @see org.springframework.web.servlet.tags.BindTag
  43.  */
  44. public class BindStatus {
  45. protected final Log logger = LogFactory.getLog(getClass());
  46. private final RequestContext requestContext;
  47. private final String path;
  48. private final boolean htmlEscape;
  49. private final String expression;
  50. private Object value;
  51. private final String[] errorCodes;
  52. private final String[] errorMessages;
  53. private final Errors errors;
  54. private PropertyEditor editor;
  55. /**
  56.  * Create a new BindStatus instance, representing a field or object status.
  57.  * @param requestContext the current RequestContext
  58.  * @param path the bean and property path for which values and errors
  59.  * will be resolved (e.g. "customer.address.street")
  60.  * @param htmlEscape whether to HTML-escape error messages and string values
  61.  * @throws IllegalStateException if no corresponding Errors object found
  62.  */
  63. public BindStatus(RequestContext requestContext, String path, boolean htmlEscape)
  64. throws IllegalStateException {
  65. this.requestContext = requestContext;
  66. this.path = path;
  67. this.htmlEscape = htmlEscape;
  68. // determine name of the object and property
  69. String beanName = null;
  70. int dotPos = path.indexOf('.');
  71. if (dotPos == -1) {
  72. // property not set, only the object itself
  73. beanName = path;
  74. this.expression = null;
  75. }
  76. else {
  77. beanName = path.substring(0, dotPos);
  78. this.expression = path.substring(dotPos + 1);
  79. }
  80. this.errors = requestContext.getErrors(beanName, false);
  81. if (this.errors != null) {
  82. // Usual case: An Errors instance is available as request attribute.
  83. // Can determine error codes and messages for the given expression.
  84. // Can use a custom PropertyEditor, as registered by a form controller.
  85. List objectErrors = null;
  86. if (this.expression != null) {
  87. if ("*".equals(this.expression)) {
  88. objectErrors = this.errors.getAllErrors();
  89. }
  90. else if (this.expression.endsWith("*")) {
  91. objectErrors = this.errors.getFieldErrors(this.expression);
  92. }
  93. else {
  94. objectErrors = this.errors.getFieldErrors(this.expression);
  95. this.value = this.errors.getFieldValue(this.expression);
  96. if (this.errors instanceof BindException) {
  97. this.editor = ((BindException) this.errors).getCustomEditor(this.expression);
  98. }
  99. else {
  100. if (logger.isDebugEnabled()) {
  101. logger.debug("Cannot not expose custom property editor because Errors instance [" +
  102. this.errors + "] is not of type BindException");
  103. }
  104. }
  105. if (htmlEscape && this.value instanceof String) {
  106. this.value = HtmlUtils.htmlEscape((String) this.value);
  107. }
  108. }
  109. }
  110. else {
  111. objectErrors = this.errors.getGlobalErrors();
  112. }
  113. this.errorCodes = getErrorCodes(objectErrors);
  114. this.errorMessages = getErrorMessages(objectErrors);
  115. }
  116. else {
  117. // No Errors instance available as request attribute:
  118. // Probably forwarded directly to a form view.
  119. // Let's do the best we can: extract a plain value if appropriate.
  120. Object target = requestContext.getRequest().getAttribute(beanName);
  121. if (target == null) {
  122. throw new IllegalStateException("Neither Errors instance nor plain target object for bean name " +
  123. beanName + " available as request attribute");
  124. }
  125. if (this.expression != null && !"*".equals(this.expression) && !this.expression.endsWith("*")) {
  126. BeanWrapperImpl bw = new BeanWrapperImpl(target);
  127. this.value = bw.getPropertyValue(this.expression);
  128. }
  129. this.errorCodes = new String[0];
  130. this.errorMessages = new String[0];
  131. }
  132. }
  133. /**
  134.  * Extract the error codes from the given ObjectError list.
  135.  */
  136. private String[] getErrorCodes(List objectErrors) {
  137. String[] codes = new String[objectErrors.size()];
  138. for (int i = 0; i < objectErrors.size(); i++) {
  139. ObjectError error = (ObjectError) objectErrors.get(i);
  140. codes[i] = error.getCode();
  141. }
  142. return codes;
  143. }
  144. /**
  145.  * Extract the error messages from the given ObjectError list.
  146.  */
  147. private String[] getErrorMessages(List objectErrors) throws NoSuchMessageException {
  148. String[] messages = new String[objectErrors.size()];
  149. for (int i = 0; i < objectErrors.size(); i++) {
  150. ObjectError error = (ObjectError) objectErrors.get(i);
  151. messages[i] = this.requestContext.getMessage(error, this.htmlEscape);
  152. }
  153. return messages;
  154. }
  155. /**
  156.  * Return the bean and property path for which values and errors
  157.  * will be resolved (e.g. "customer.address.street").
  158.  */
  159. public String getPath() {
  160. return path;
  161. }
  162. /**
  163.  * Return a bind expression that can be used in HTML forms as input name
  164.  * for the respective field, or null if not field-specific.
  165.  * <p>Returns a bind path appropriate for resubmission, e.g. "address.street".
  166.  * Note that the complete bind path as required by the bind tag is
  167.  * "customer.address.street", if bound to a "customer" bean.
  168.  */
  169. public String getExpression() {
  170. return expression;
  171. }
  172. /**
  173.  * Return the current value of the field, i.e. either the property value
  174.  * or a rejected update, or null if not field-specific.
  175.  */
  176. public Object getValue() {
  177. return value;
  178. }
  179. /**
  180.  * Return a suitable display value for the field, i.e. empty string
  181.  * instead of a null value, or null if not field-specific.
  182.  */
  183. public String getDisplayValue() {
  184. return (this.value != null) ? this.value.toString() : "";
  185. }
  186. /**
  187.  * Return if this status represents a field or object error.
  188.  */
  189. public boolean isError() {
  190. return (this.errorCodes != null && this.errorCodes.length > 0);
  191. }
  192. /**
  193.  * Return the error codes for the field or object, if any.
  194.  * Returns an empty array instead of null if none.
  195.  */
  196. public String[] getErrorCodes() {
  197. return errorCodes;
  198. }
  199. /**
  200.  * Return the first error codes for the field or object, if any.
  201.  */
  202. public String getErrorCode() {
  203. return (this.errorCodes.length > 0 ? this.errorCodes[0] : "");
  204. }
  205. /**
  206.  * Return the resolved error messages for the field or object,
  207.  * if any. Returns an empty array instead of null if none.
  208.  */
  209. public String[] getErrorMessages() {
  210. return errorMessages;
  211. }
  212. /**
  213.  * Return the first error message for the field or object, if any.
  214.  */
  215. public String getErrorMessage() {
  216. return (this.errorMessages.length > 0 ? this.errorMessages[0] : "");
  217. }
  218. /**
  219.  * Return an error message string, concatenating all messages
  220.  * separated by the given delimiter.
  221.  * @param delimiter separator string, e.g. ", " or "<br>"
  222.  * @return the error message string
  223.  */
  224. public String getErrorMessagesAsString(String delimiter) {
  225. return StringUtils.arrayToDelimitedString(this.errorMessages, delimiter);
  226. }
  227. /**
  228.  * Return the Errors instance that this bind status is currently bound to.
  229.  * @return the current Errors instance, or null if none
  230.  */
  231. public Errors getErrors() {
  232. return errors;
  233. }
  234. /**
  235.  * Return the PropertyEditor for the property that this bind status
  236.  * is currently bound to.
  237.  * @return the current PropertyEditor, or null if none
  238.  */
  239. public PropertyEditor getEditor() {
  240. return editor;
  241. }
  242. public String toString() {
  243. StringBuffer sb = new StringBuffer("BindStatus: ");
  244. sb.append("expression=[").append(this.expression).append("]; ");
  245. sb.append("value=[").append(this.value).append("]");
  246. if (isError()) {
  247. sb.append("; errorCodes='" + Arrays.asList(this.errorCodes) + "'; ");
  248. sb.append("errorMessages='" + Arrays.asList(this.errorMessages));
  249. }
  250. return sb.toString();
  251. }
  252. }