KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > springframework > validation > AbstractBindingResult


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.validation;
18
19 import java.io.Serializable JavaDoc;
20 import java.util.Collections JavaDoc;
21 import java.util.EmptyStackException JavaDoc;
22 import java.util.HashMap JavaDoc;
23 import java.util.HashSet JavaDoc;
24 import java.util.Iterator JavaDoc;
25 import java.util.LinkedList JavaDoc;
26 import java.util.List JavaDoc;
27 import java.util.Map JavaDoc;
28 import java.util.Set JavaDoc;
29 import java.util.Stack JavaDoc;
30
31 import org.springframework.beans.PropertyEditorRegistry;
32 import org.springframework.util.StringUtils;
33
34 /**
35  * Abstract implementation of the {@link BindingResult} interface and
36  * its super-interface {@link Errors}. Encapsulates common management of
37  * {@link ObjectError ObjectErrors} and {@link FieldError FieldErrors}.
38  *
39  * @author Juergen Hoeller
40  * @author Rob Harrop
41  * @since 2.0
42  * @see Errors
43  */

44 public abstract class AbstractBindingResult implements BindingResult, Serializable JavaDoc {
45
46     private final List JavaDoc errors = new LinkedList JavaDoc();
47
48     private final String JavaDoc objectName;
49
50     private MessageCodesResolver messageCodesResolver = new DefaultMessageCodesResolver();
51
52     private String JavaDoc nestedPath = "";
53
54     private final Stack JavaDoc nestedPathStack = new Stack JavaDoc();
55
56     private Set JavaDoc suppressedFields = new HashSet JavaDoc();
57
58
59     /**
60      * Create a new AbstractBindingResult instance.
61      * @param objectName the name of the target object
62      * @see DefaultMessageCodesResolver
63      */

64     protected AbstractBindingResult(String JavaDoc objectName) {
65         this.objectName = objectName;
66     }
67
68     /**
69      * Set the strategy to use for resolving errors into message codes.
70      * Default is DefaultMessageCodesResolver.
71      * @see DefaultMessageCodesResolver
72      */

73     public void setMessageCodesResolver(MessageCodesResolver messageCodesResolver) {
74         this.messageCodesResolver = messageCodesResolver;
75     }
76
77     /**
78      * Return the strategy to use for resolving errors into message codes.
79      */

80     public MessageCodesResolver getMessageCodesResolver() {
81         return this.messageCodesResolver;
82     }
83
84
85     //---------------------------------------------------------------------
86
// Implementation of Errors interface
87
//---------------------------------------------------------------------
88

89     public String JavaDoc getObjectName() {
90         return this.objectName;
91     }
92
93     public void setNestedPath(String JavaDoc nestedPath) {
94         doSetNestedPath(nestedPath);
95         this.nestedPathStack.clear();
96     }
97
98     public String JavaDoc getNestedPath() {
99         return this.nestedPath;
100     }
101
102     public void pushNestedPath(String JavaDoc subPath) {
103         this.nestedPathStack.push(getNestedPath());
104         doSetNestedPath(getNestedPath() + subPath);
105     }
106
107     public void popNestedPath() throws IllegalArgumentException JavaDoc {
108         try {
109             String JavaDoc formerNestedPath = (String JavaDoc) this.nestedPathStack.pop();
110             doSetNestedPath(formerNestedPath);
111         }
112         catch (EmptyStackException JavaDoc ex) {
113             throw new IllegalStateException JavaDoc("Cannot pop nested path: no nested path on stack");
114         }
115     }
116
117     /**
118      * Actually set the nested path.
119      * Delegated to by setNestedPath and pushNestedPath.
120      */

121     protected void doSetNestedPath(String JavaDoc nestedPath) {
122         if (nestedPath == null) {
123             nestedPath = "";
124         }
125         nestedPath = canonicalFieldName(nestedPath);
126         if (nestedPath.length() > 0 && !nestedPath.endsWith(Errors.NESTED_PATH_SEPARATOR)) {
127             nestedPath += Errors.NESTED_PATH_SEPARATOR;
128         }
129         this.nestedPath = nestedPath;
130     }
131
132     /**
133      * Transform the given field into its full path,
134      * regarding the nested path of this instance.
135      */

136     protected String JavaDoc fixedField(String JavaDoc field) {
137         if (StringUtils.hasLength(field)) {
138             return getNestedPath() + canonicalFieldName(field);
139         }
140         else {
141             String JavaDoc path = getNestedPath();
142             return (path.endsWith(Errors.NESTED_PATH_SEPARATOR) ?
143                     path.substring(0, path.length() - NESTED_PATH_SEPARATOR.length()) : path);
144         }
145     }
146
147
148     public void reject(String JavaDoc errorCode) {
149         reject(errorCode, null, null);
150     }
151
152     public void reject(String JavaDoc errorCode, String JavaDoc defaultMessage) {
153         reject(errorCode, null, defaultMessage);
154     }
155
156     public void reject(String JavaDoc errorCode, Object JavaDoc[] errorArgs, String JavaDoc defaultMessage) {
157         addError(new ObjectError(getObjectName(), resolveMessageCodes(errorCode), errorArgs, defaultMessage));
158     }
159
160     public void rejectValue(String JavaDoc field, String JavaDoc errorCode) {
161         rejectValue(field, errorCode, null, null);
162     }
163
164     public void rejectValue(String JavaDoc field, String JavaDoc errorCode, String JavaDoc defaultMessage) {
165         rejectValue(field, errorCode, null, defaultMessage);
166     }
167
168     public void rejectValue(String JavaDoc field, String JavaDoc errorCode, Object JavaDoc[] errorArgs, String JavaDoc defaultMessage) {
169         if ("".equals(getNestedPath()) && !StringUtils.hasLength(field)) {
170             // We're at the top of the nested object hierarchy,
171
// so the present level is not a field but rather the top object.
172
// The best we can do is register a global error here...
173
reject(errorCode, errorArgs, defaultMessage);
174             return;
175         }
176         String JavaDoc fixedField = fixedField(field);
177         Object JavaDoc newVal = getActualFieldValue(fixedField);
178         FieldError fe = new FieldError(
179                 getObjectName(), fixedField, newVal, false,
180                 resolveMessageCodes(errorCode, field), errorArgs, defaultMessage);
181         addError(fe);
182     }
183
184     /**
185      * Resolve the given error code into message codes.
186      * Calls the MessageCodesResolver with appropriate parameters.
187      * @param errorCode the error code to resolve into message codes
188      * @return the resolved message codes
189      * @see #setMessageCodesResolver
190      */

191     public String JavaDoc[] resolveMessageCodes(String JavaDoc errorCode) {
192         return getMessageCodesResolver().resolveMessageCodes(errorCode, getObjectName());
193     }
194
195     public String JavaDoc[] resolveMessageCodes(String JavaDoc errorCode, String JavaDoc field) {
196         String JavaDoc fixedField = fixedField(field);
197         Class JavaDoc fieldType = getFieldType(fixedField);
198         return getMessageCodesResolver().resolveMessageCodes(errorCode, getObjectName(), fixedField, fieldType);
199     }
200
201     public void addError(ObjectError error) {
202         this.errors.add(error);
203     }
204
205     public void addAllErrors(Errors errors) {
206         if (!errors.getObjectName().equals(getObjectName())) {
207             throw new IllegalArgumentException JavaDoc("Errors object needs to have same object name");
208         }
209         this.errors.addAll(errors.getAllErrors());
210     }
211
212
213     public boolean hasErrors() {
214         return !this.errors.isEmpty();
215     }
216
217     public int getErrorCount() {
218         return this.errors.size();
219     }
220
221     public List JavaDoc getAllErrors() {
222         return Collections.unmodifiableList(this.errors);
223     }
224
225     public boolean hasGlobalErrors() {
226         return (getGlobalErrorCount() > 0);
227     }
228
229     public int getGlobalErrorCount() {
230         return getGlobalErrors().size();
231     }
232
233     public List JavaDoc getGlobalErrors() {
234         List JavaDoc result = new LinkedList JavaDoc();
235         for (Iterator JavaDoc it = this.errors.iterator(); it.hasNext();) {
236             Object JavaDoc error = it.next();
237             if (!(error instanceof FieldError)) {
238                 result.add(error);
239             }
240         }
241         return Collections.unmodifiableList(result);
242     }
243
244     public ObjectError getGlobalError() {
245         for (Iterator JavaDoc it = this.errors.iterator(); it.hasNext();) {
246             ObjectError objectError = (ObjectError) it.next();
247             if (!(objectError instanceof FieldError)) {
248                 return objectError;
249             }
250         }
251         return null;
252     }
253
254     public boolean hasFieldErrors() {
255         return (getFieldErrorCount() > 0);
256     }
257
258     public int getFieldErrorCount() {
259         return getFieldErrors().size();
260     }
261
262     public List JavaDoc getFieldErrors() {
263         List JavaDoc result = new LinkedList JavaDoc();
264         for (Iterator JavaDoc it = this.errors.iterator(); it.hasNext();) {
265             Object JavaDoc error = it.next();
266             if (error instanceof FieldError) {
267                 result.add(error);
268             }
269         }
270         return Collections.unmodifiableList(result);
271     }
272
273     public FieldError getFieldError() {
274         for (Iterator JavaDoc it = this.errors.iterator(); it.hasNext();) {
275             Object JavaDoc error = it.next();
276             if (error instanceof FieldError) {
277                 return (FieldError) error;
278             }
279         }
280         return null;
281     }
282
283     public boolean hasFieldErrors(String JavaDoc field) {
284         return (getFieldErrorCount(field) > 0);
285     }
286
287     public int getFieldErrorCount(String JavaDoc field) {
288         return getFieldErrors(field).size();
289     }
290
291     public List JavaDoc getFieldErrors(String JavaDoc field) {
292         List JavaDoc result = new LinkedList JavaDoc();
293         String JavaDoc fixedField = fixedField(field);
294         for (Iterator JavaDoc it = this.errors.iterator(); it.hasNext();) {
295             Object JavaDoc error = it.next();
296             if (error instanceof FieldError && isMatchingFieldError(fixedField, ((FieldError) error))) {
297                 result.add(error);
298             }
299         }
300         return Collections.unmodifiableList(result);
301     }
302
303     public FieldError getFieldError(String JavaDoc field) {
304         String JavaDoc fixedField = fixedField(field);
305         for (Iterator JavaDoc it = this.errors.iterator(); it.hasNext();) {
306             Object JavaDoc error = it.next();
307             if (error instanceof FieldError) {
308                 FieldError fe = (FieldError) error;
309                 if (isMatchingFieldError(fixedField, fe)) {
310                     return fe;
311                 }
312             }
313         }
314         return null;
315     }
316
317     /**
318      * Check whether the given FieldError matches the given field.
319      * @param field the field that we are looking up FieldErrors for
320      * @param fieldError the candidate FieldError
321      * @return whether the FieldError matches the given field
322      */

323     protected boolean isMatchingFieldError(String JavaDoc field, FieldError fieldError) {
324         return (field.equals(fieldError.getField()) ||
325                 (field.endsWith("*") && fieldError.getField().startsWith(field.substring(0, field.length() - 1))));
326     }
327
328
329     public Object JavaDoc getFieldValue(String JavaDoc field) {
330         FieldError fe = getFieldError(field);
331         // Use rejected value in case of error, current bean property value else.
332
Object JavaDoc value = null;
333         if (fe != null) {
334             value = fe.getRejectedValue();
335         }
336         else {
337             value = getActualFieldValue(fixedField(field));
338         }
339         // Apply formatting, but not on binding failures like type mismatches.
340
if (fe == null || !fe.isBindingFailure()) {
341             value = formatFieldValue(field, value);
342         }
343         return value;
344     }
345
346     /**
347      * This default implementation determines the type based on the actual
348      * field value, if any. Subclasses should override this to determine
349      * the type from a descriptor, even for <code>null</code> values.
350      * @see #getActualFieldValue
351      */

352     public Class JavaDoc getFieldType(String JavaDoc field) {
353         Object JavaDoc value = getActualFieldValue(field);
354         if (value != null) {
355             return value.getClass();
356         }
357         return null;
358     }
359
360
361     //---------------------------------------------------------------------
362
// Implementation of BindingResult interface
363
//---------------------------------------------------------------------
364

365     /**
366      * Return a model Map for the obtained state, exposing an Errors
367      * instance as '{@link #MODEL_KEY_PREFIX MODEL_KEY_PREFIX} + objectName'
368      * and the object itself.
369      * <p>Note that the Map is constructed every time you're calling this method.
370      * Adding things to the map and then re-calling this method will not work.
371      * <p>The attributes in the model Map returned by this method are usually
372      * included in the ModelAndView for a form view that uses Spring's bind tag,
373      * which needs access to the Errors instance. Spring's SimpleFormController
374      * will do this for you when rendering its form or success view. When
375      * building the ModelAndView yourself, you need to include the attributes
376      * from the model Map returned by this method yourself.
377      * @see #getObjectName
378      * @see #MODEL_KEY_PREFIX
379      * @see org.springframework.web.servlet.ModelAndView
380      * @see org.springframework.web.servlet.tags.BindTag
381      * @see org.springframework.web.servlet.mvc.SimpleFormController
382      */

383     public Map JavaDoc getModel() {
384         Map JavaDoc model = new HashMap JavaDoc();
385         // Errors instance, even if no errors.
386
model.put(BindingResult.MODEL_KEY_PREFIX + getObjectName(), this);
387         // Mapping from name to target object.
388
model.put(getObjectName(), getTarget());
389         return model;
390     }
391
392     /**
393      * This implementation throws an UnsupportedOperationException.
394      */

395     public PropertyEditorRegistry getPropertyEditorRegistry() {
396         throw new UnsupportedOperationException JavaDoc(
397                 "[" + getClass().getName() + "] does not support a PropertyEditorRegistry");
398     }
399
400     /**
401      * Mark the specified disallowed field as suppressed.
402      * <p>The data binder invokes this for each field value that was
403      * detected to target a disallowed field.
404      * @see DataBinder#setAllowedFields
405      */

406     public void recordSuppressedField(String JavaDoc fieldName) {
407         this.suppressedFields.add(fieldName);
408     }
409
410     /**
411      * Return the list of fields that were suppressed during the bind process.
412      * <p>Can be used to determine whether any field values were targetting
413      * disallowed fields.
414      * @see DataBinder#setAllowedFields
415      */

416     public String JavaDoc[] getSuppressedFields() {
417         return StringUtils.toStringArray(this.suppressedFields);
418     }
419
420
421     public String JavaDoc toString() {
422         StringBuffer JavaDoc sb = new StringBuffer JavaDoc(getClass().getName());
423         sb.append(": ").append(getErrorCount()).append(" errors");
424         Iterator JavaDoc it = getAllErrors().iterator();
425         while (it.hasNext()) {
426             sb.append('\n').append(it.next());
427         }
428         return sb.toString();
429     }
430
431     public boolean equals(Object JavaDoc other) {
432         if (this == other) {
433             return true;
434         }
435         if (!(other instanceof BindingResult)) {
436             return false;
437         }
438         BindingResult otherResult = (BindingResult) other;
439         return (getObjectName().equals(otherResult.getObjectName()) &&
440                 getTarget().equals(otherResult.getTarget()) &&
441                 getAllErrors().equals(otherResult.getAllErrors()));
442     }
443
444     public int hashCode() {
445         return getObjectName().hashCode() * 29 + getTarget().hashCode();
446     }
447
448
449     //---------------------------------------------------------------------
450
// Template methods to be implemented/overridden by subclasses
451
//---------------------------------------------------------------------
452

453     /**
454      * Return the wrapped target object.
455      */

456     public abstract Object JavaDoc getTarget();
457
458     /**
459      * Determine the canonical field name for the given field.
460      * <p>The default implementation simply returns the field name as-is.
461      * @param field the original field name
462      * @return the canonical field name
463      */

464     protected String JavaDoc canonicalFieldName(String JavaDoc field) {
465         return field;
466     }
467
468     /**
469      * Extract the actual field value for the given field.
470      * @param field the field to check
471      * @return the current value of the field
472      */

473     protected abstract Object JavaDoc getActualFieldValue(String JavaDoc field);
474
475     /**
476      * Format the given value for the specified field.
477      * <p>The default implementation simply returns the field value as-is.
478      * @param field the field to check
479      * @param value the value of the field (either a rejected value
480      * other than from a binding error, or an actual field value)
481      * @return the formatted value
482      */

483     protected Object JavaDoc formatFieldValue(String JavaDoc field, Object JavaDoc value) {
484         return value;
485     }
486
487 }
488
Popular Tags