KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > springframework > web > servlet > support > BindStatus


1 /*
2  * Copyright 2002-2007 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
17 package org.springframework.web.servlet.support;
18
19 import java.beans.PropertyEditor JavaDoc;
20 import java.util.Arrays JavaDoc;
21 import java.util.List JavaDoc;
22
23 import org.springframework.beans.BeanWrapperImpl;
24 import org.springframework.context.NoSuchMessageException;
25 import org.springframework.util.StringUtils;
26 import org.springframework.validation.AbstractPropertyBindingResult;
27 import org.springframework.validation.BindException;
28 import org.springframework.validation.Errors;
29 import org.springframework.validation.ObjectError;
30 import org.springframework.web.util.HtmlUtils;
31
32 /**
33  * Simple adapter to expose the bind status of a field or object.
34  * Set as a variable both by the JSP bind tag and Velocity/FreeMarker macros.
35  *
36  * <p>Obviously, object status representations (i.e. errors at the object level
37  * rather than the field level) do not have an expression and a value but only
38  * error codes and messages. For simplicity's sake and to be able to use the same
39  * tags and macros, the same status class is used for both scenarios.
40  *
41  * @author Rod Johnson
42  * @author Juergen Hoeller
43  * @author Darren Davison
44  * @see RequestContext#getBindStatus
45  * @see org.springframework.web.servlet.tags.BindTag
46  * @see org.springframework.web.servlet.view.AbstractTemplateView#setExposeSpringMacroHelpers
47  */

48 public class BindStatus {
49
50     private final RequestContext requestContext;
51
52     private final String JavaDoc path;
53
54     private final boolean htmlEscape;
55
56     private final String JavaDoc expression;
57
58     private final Errors errors;
59
60     private Object JavaDoc value;
61
62     private Class JavaDoc valueType;
63
64     private PropertyEditor JavaDoc editor;
65
66     private List JavaDoc objectErrors;
67
68     private String JavaDoc[] errorCodes;
69
70     private String JavaDoc[] errorMessages;
71
72
73     /**
74      * Create a new BindStatus instance, representing a field or object status.
75      * @param requestContext the current RequestContext
76      * @param path the bean and property path for which values and errors
77      * will be resolved (e.g. "customer.address.street")
78      * @param htmlEscape whether to HTML-escape error messages and string values
79      * @throws IllegalStateException if no corresponding Errors object found
80      */

81     public BindStatus(RequestContext requestContext, String JavaDoc path, boolean htmlEscape)
82             throws IllegalStateException JavaDoc {
83
84         this.requestContext = requestContext;
85         this.path = path;
86         this.htmlEscape = htmlEscape;
87
88         // determine name of the object and property
89
String JavaDoc beanName = null;
90         int dotPos = path.indexOf('.');
91         if (dotPos == -1) {
92             // property not set, only the object itself
93
beanName = path;
94             this.expression = null;
95         }
96         else {
97             beanName = path.substring(0, dotPos);
98             this.expression = path.substring(dotPos + 1);
99         }
100
101         this.errors = requestContext.getErrors(beanName, false);
102
103         if (this.errors != null) {
104             // Usual case: A BindingResult is available as request attribute.
105
// Can determine error codes and messages for the given expression.
106
// Can use a custom PropertyEditor, as registered by a form controller.
107

108             if (this.expression != null) {
109                 if ("*".equals(this.expression)) {
110                     this.objectErrors = this.errors.getAllErrors();
111                 }
112                 else if (this.expression.endsWith("*")) {
113                     this.objectErrors = this.errors.getFieldErrors(this.expression);
114                 }
115                 else {
116                     this.objectErrors = this.errors.getFieldErrors(this.expression);
117                     this.value = this.errors.getFieldValue(this.expression);
118                     this.valueType = this.errors.getFieldType(this.expression);
119                     this.editor = getCustomEditor(this.errors, this.expression);
120                 }
121             }
122
123             else {
124                 this.objectErrors = this.errors.getGlobalErrors();
125             }
126
127             initErrorCodes();
128         }
129
130         else {
131             // No BindingResult available as request attribute:
132
// Probably forwarded directly to a form view.
133
// Let's do the best we can: extract a plain target if appropriate.
134

135             Object JavaDoc target = requestContext.getModelObject(beanName);
136             if (target == null) {
137                 throw new IllegalStateException JavaDoc("Neither BindingResult nor plain target object for bean name '" +
138                         beanName + "' available as request attribute");
139             }
140
141             if (this.expression != null && !"*".equals(this.expression) && !this.expression.endsWith("*")) {
142                 BeanWrapperImpl bw = new BeanWrapperImpl(target);
143                 this.valueType = bw.getPropertyType(this.expression);
144                 this.value = bw.getPropertyValue(this.expression);
145             }
146
147             this.errorCodes = new String JavaDoc[0];
148             this.errorMessages = new String JavaDoc[0];
149         }
150
151         if (htmlEscape && this.value instanceof String JavaDoc) {
152             this.value = HtmlUtils.htmlEscape((String JavaDoc) this.value);
153         }
154     }
155
156     /**
157      * Find a custom editor for the given field, if any.
158      * @see org.springframework.validation.AbstractPropertyBindingResult#getCustomEditor(String)
159      */

160     private PropertyEditor JavaDoc getCustomEditor(Errors errors, String JavaDoc field) {
161         Errors bindingResult = errors;
162         // Unwrap BindException for backwards compatibility.
163
if (errors instanceof BindException) {
164             bindingResult = ((BindException) errors).getBindingResult();
165         }
166         if (bindingResult instanceof AbstractPropertyBindingResult) {
167             return ((AbstractPropertyBindingResult) bindingResult).getCustomEditor(field);
168         }
169         return null;
170     }
171
172     /**
173      * Extract the error codes from the ObjectError list.
174      */

175     private void initErrorCodes() {
176         this.errorCodes = new String JavaDoc[this.objectErrors.size()];
177         for (int i = 0; i < this.objectErrors.size(); i++) {
178             ObjectError error = (ObjectError) this.objectErrors.get(i);
179             this.errorCodes[i] = error.getCode();
180         }
181     }
182
183     /**
184      * Extract the error messages from the ObjectError list.
185      */

186     private void initErrorMessages() throws NoSuchMessageException {
187         if (this.errorMessages == null) {
188             this.errorMessages = new String JavaDoc[this.objectErrors.size()];
189             for (int i = 0; i < this.objectErrors.size(); i++) {
190                 ObjectError error = (ObjectError) this.objectErrors.get(i);
191                 this.errorMessages[i] = this.requestContext.getMessage(error, this.htmlEscape);
192             }
193         }
194     }
195
196
197     /**
198      * Return the bean and property path for which values and errors
199      * will be resolved (e.g. "customer.address.street").
200      */

201     public String JavaDoc getPath() {
202         return this.path;
203     }
204
205     /**
206      * Return a bind expression that can be used in HTML forms as input name
207      * for the respective field, or <code>null</code> if not field-specific.
208      * <p>Returns a bind path appropriate for resubmission, e.g. "address.street".
209      * Note that the complete bind path as required by the bind tag is
210      * "customer.address.street", if bound to a "customer" bean.
211      */

212     public String JavaDoc getExpression() {
213         return this.expression;
214     }
215
216     /**
217      * Return the current value of the field, i.e. either the property value
218      * or a rejected update, or <code>null</code> if not field-specific.
219      * <p>This value will be an HTML-escaped String if the original value
220      * already was a String.
221      */

222     public Object JavaDoc getValue() {
223         return this.value;
224     }
225
226     /**
227      * Gets the '<code>Class</code>' type of the field. Favour this instead of
228      * '<code>getValue().getClass()</code>' since '<code>getValue()</code>' may
229      * return '<code>null</code>'.
230      */

231     public Class JavaDoc getValueType() {
232         return this.valueType;
233     }
234
235     /**
236      * Return a suitable display value for the field, i.e. the stringified
237      * value if not null, and an empty string in case of a null value.
238      * <p>This value will be an HTML-escaped String if the original value
239      * was non-null: the <code>toString</code> result of the original value
240      * will get HTML-escaped.
241      */

242     public String JavaDoc getDisplayValue() {
243         if (this.value instanceof String JavaDoc) {
244             return (String JavaDoc) this.value;
245         }
246         if (this.value != null) {
247             return (this.htmlEscape ? HtmlUtils.htmlEscape(this.value.toString()) : this.value.toString());
248         }
249         return "";
250     }
251
252     /**
253      * Return if this status represents a field or object error.
254      */

255     public boolean isError() {
256         return (this.errorCodes != null && this.errorCodes.length > 0);
257     }
258
259     /**
260      * Return the error codes for the field or object, if any.
261      * Returns an empty array instead of null if none.
262      */

263     public String JavaDoc[] getErrorCodes() {
264         return this.errorCodes;
265     }
266
267     /**
268      * Return the first error codes for the field or object, if any.
269      */

270     public String JavaDoc getErrorCode() {
271         return (this.errorCodes.length > 0 ? this.errorCodes[0] : "");
272     }
273
274     /**
275      * Return the resolved error messages for the field or object,
276      * if any. Returns an empty array instead of null if none.
277      */

278     public String JavaDoc[] getErrorMessages() {
279         initErrorMessages();
280         return this.errorMessages;
281     }
282
283     /**
284      * Return the first error message for the field or object, if any.
285      */

286     public String JavaDoc getErrorMessage() {
287         initErrorMessages();
288         return (this.errorMessages.length > 0 ? this.errorMessages[0] : "");
289     }
290
291     /**
292      * Return an error message string, concatenating all messages
293      * separated by the given delimiter.
294      * @param delimiter separator string, e.g. ", " or "<br>"
295      * @return the error message string
296      */

297     public String JavaDoc getErrorMessagesAsString(String JavaDoc delimiter) {
298         initErrorMessages();
299         return StringUtils.arrayToDelimitedString(this.errorMessages, delimiter);
300     }
301
302     /**
303      * Return the Errors instance (typically a BindingResult) that this
304      * bind status is currently associated with.
305      * @return the current Errors instance, or <code>null</code> if none
306      * @see org.springframework.validation.BindingResult
307      */

308     public Errors getErrors() {
309         return this.errors;
310     }
311
312     /**
313      * Return the PropertyEditor for the property that this bind status
314      * is currently bound to.
315      * @return the current PropertyEditor, or <code>null</code> if none
316      */

317     public PropertyEditor JavaDoc getEditor() {
318         return this.editor;
319     }
320
321
322     public String JavaDoc toString() {
323         StringBuffer JavaDoc sb = new StringBuffer JavaDoc("BindStatus: ");
324         sb.append("expression=[").append(this.expression).append("]; ");
325         sb.append("value=[").append(this.value).append("]");
326         if (isError()) {
327             sb.append("; errorCodes=" + Arrays.asList(this.errorCodes));
328         }
329         return sb.toString();
330     }
331
332 }
333
Popular Tags