KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > springframework > web > servlet > mvc > AbstractWizardFormController


1 /*
2  * Copyright 2002-2006 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.mvc;
18
19 import java.util.Enumeration JavaDoc;
20 import java.util.HashMap JavaDoc;
21 import java.util.Map JavaDoc;
22
23 import javax.servlet.ServletException JavaDoc;
24 import javax.servlet.http.HttpServletRequest JavaDoc;
25 import javax.servlet.http.HttpServletResponse JavaDoc;
26
27 import org.springframework.validation.BindException;
28 import org.springframework.validation.Errors;
29 import org.springframework.web.servlet.ModelAndView;
30 import org.springframework.web.util.WebUtils;
31
32 /**
33  * Form controller for typical wizard-style workflows.
34  *
35  * <p>In contrast to classic forms, wizards have more than one form view page.
36  * Therefore, there are various actions instead of one single submit action:
37  * <ul>
38  * <li>finish: trying to leave the wizard successfully, that is, perform its
39  * final action, and thus requiring a valid state;
40  * <li>cancel: leaving the wizard without performing its final action, and
41  * thus without regard to the validity of its current state;
42  * <li>page change: showing another wizard page, e.g. the next or previous
43  * one, with regard to "dirty back" and "dirty forward".
44  * </ul>
45  *
46  * <p>Finish and cancel actions can be triggered by request parameters, named
47  * PARAM_FINISH ("_finish") and PARAM_CANCEL ("_cancel"), ignoring parameter
48  * values to allow for HTML buttons. The target page for page changes can be
49  * specified by PARAM_TARGET, appending the page number to the parameter name
50  * (e.g. "_target1"). The action parameters are recognized when triggered by
51  * image buttons too (via "_finish.x", "_abort.x", or "_target1.x").
52  *
53  * <p>The current page number will be stored in the session. It can also be
54  * specified as request parameter PARAM_PAGE, to properly handle usage of
55  * the back button in a browser: In this case, a submission always contains
56  * the correct page number, even if the user submitted from an old view.
57  *
58  * <p>The page can only be changed if it validates correctly, except if a
59  * "dirty back" or "dirty forward" is allowed. At finish, all pages get
60  * validated again to guarantee a consistent state.
61  *
62  * <p>Note that a validator's default validate method is not executed when using
63  * this class! Rather, the {@link #validatePage} implementation should call
64  * special <code>validateXXX</code> methods that the validator needs to provide,
65  * validating certain pieces of the object. These can be combined to validate
66  * the elements of individual pages.
67  *
68  * <p>Note: Page numbering starts with 0, to be able to pass an array
69  * consisting of the corresponding view names to the "pages" bean property.
70  *
71  * @author Juergen Hoeller
72  * @since 25.04.2003
73  * @see #setPages
74  * @see #validatePage
75  * @see #processFinish
76  * @see #processCancel
77  */

78 public abstract class AbstractWizardFormController extends AbstractFormController {
79
80     /**
81      * Parameter triggering the finish action.
82      * Can be called from any wizard page!
83      */

84     public static final String JavaDoc PARAM_FINISH = "_finish";
85
86     /**
87      * Parameter triggering the cancel action.
88      * Can be called from any wizard page!
89      */

90     public static final String JavaDoc PARAM_CANCEL = "_cancel";
91
92     /**
93      * Parameter specifying the target page,
94      * appending the page number to the name.
95      */

96     public static final String JavaDoc PARAM_TARGET = "_target";
97
98     /**
99      * Parameter specifying the current page as value. Not necessary on
100      * form pages, but allows to properly handle usage of the back button.
101      * @see #setPageAttribute
102      */

103     public static final String JavaDoc PARAM_PAGE = "_page";
104
105
106     private String JavaDoc[] pages;
107
108     private String JavaDoc pageAttribute;
109
110     private boolean allowDirtyBack = true;
111
112     private boolean allowDirtyForward = false;
113
114
115     /**
116      * Create a new AbstractWizardFormController.
117      * <p>"sessionForm" is automatically turned on, "validateOnBinding"
118      * turned off, and "cacheSeconds" set to 0 by the base class
119      * (-> no caching for all form controllers).
120      */

121     public AbstractWizardFormController() {
122         // AbstractFormController sets default cache seconds to 0.
123
super();
124
125         // Always needs session to keep data from all pages.
126
setSessionForm(true);
127
128         // Never validate everything on binding ->
129
// wizards validate individual pages.
130
setValidateOnBinding(false);
131     }
132
133     /**
134      * Set the wizard pages, i.e. the view names for the pages.
135      * The array index is interpreted as page number.
136      * @param pages view names for the pages
137      */

138     public final void setPages(String JavaDoc[] pages) {
139         if (pages == null || pages.length == 0) {
140             throw new IllegalArgumentException JavaDoc("No wizard pages defined");
141         }
142         this.pages = pages;
143     }
144
145     /**
146      * Return the wizard pages, i.e. the view names for the pages.
147      * The array index corresponds to the page number.
148      * <p>Note that a concrete wizard form controller might override
149      * {@link #getViewName(HttpServletRequest, Object, int)} to
150      * determine the view name for each page dynamically.
151      * @see #getViewName(javax.servlet.http.HttpServletRequest, Object, int)
152      */

153     public final String JavaDoc[] getPages() {
154         return pages;
155     }
156
157     /**
158      * Return the number of wizard pages.
159      * Useful to check whether the last page has been reached.
160      * <p>Note that a concrete wizard form controller might override
161      * {@link #getPageCount(HttpServletRequest, Object)} to determine
162      * the page count dynamically. The default implementation of that extended
163      * <code>getPageCount</code> variant returns the static page count as
164      * determined by this <code>getPageCount()</code> method.
165      * @see #getPageCount(javax.servlet.http.HttpServletRequest, Object)
166      */

167     protected final int getPageCount() {
168         return this.pages.length;
169     }
170
171     /**
172      * Set the name of the page attribute in the model, containing
173      * an Integer with the current page number.
174      * <p>This will be necessary for single views rendering multiple view pages.
175      * It also allows for specifying the optional "_page" parameter.
176      * @param pageAttribute name of the page attribute
177      * @see #PARAM_PAGE
178      */

179     public final void setPageAttribute(String JavaDoc pageAttribute) {
180         this.pageAttribute = pageAttribute;
181     }
182
183     /**
184      * Return the name of the page attribute in the model.
185      */

186     public final String JavaDoc getPageAttribute() {
187         return pageAttribute;
188     }
189
190     /**
191      * Set if "dirty back" is allowed, that is, if moving to a former wizard
192      * page is allowed in case of validation errors for the current page.
193      * @param allowDirtyBack if "dirty back" is allowed
194      */

195     public final void setAllowDirtyBack(boolean allowDirtyBack) {
196         this.allowDirtyBack = allowDirtyBack;
197     }
198
199     /**
200      * Return whether "dirty back" is allowed.
201      */

202     public final boolean isAllowDirtyBack() {
203         return allowDirtyBack;
204     }
205
206     /**
207      * Set if "dirty forward" is allowed, that is, if moving to a later wizard
208      * page is allowed in case of validation errors for the current page.
209      * @param allowDirtyForward if "dirty forward" is allowed
210      */

211     public final void setAllowDirtyForward(boolean allowDirtyForward) {
212         this.allowDirtyForward = allowDirtyForward;
213     }
214
215     /**
216      * Return whether "dirty forward" is allowed.
217      */

218     public final boolean isAllowDirtyForward() {
219         return allowDirtyForward;
220     }
221
222
223     /**
224      * Calls page-specific onBindAndValidate method.
225      */

226     protected final void onBindAndValidate(HttpServletRequest JavaDoc request, Object JavaDoc command, BindException errors)
227         throws Exception JavaDoc {
228
229         onBindAndValidate(request, command, errors, getCurrentPage(request));
230     }
231
232     /**
233      * Callback for custom post-processing in terms of binding and validation.
234      * Called on each submit, after standard binding but before page-specific
235      * validation of this wizard form controller.
236      * <p>Note: AbstractWizardFormController does not perform standand
237      * validation on binding but rather applies page-specific validation
238      * on processing the form submission.
239      * @param request current HTTP request
240      * @param command bound command
241      * @param errors Errors instance for additional custom validation
242      * @param page current wizard page
243      * @throws Exception in case of invalid state or arguments
244      * @see #bindAndValidate
245      * @see #processFormSubmission
246      * @see org.springframework.validation.Errors
247      */

248     protected void onBindAndValidate(HttpServletRequest JavaDoc request, Object JavaDoc command, BindException errors, int page)
249         throws Exception JavaDoc {
250     }
251
252     /**
253      * Consider an explicit finish or cancel request as a form submission too.
254      * @see #isFinishRequest(javax.servlet.http.HttpServletRequest)
255      * @see #isCancelRequest(javax.servlet.http.HttpServletRequest)
256      */

257     protected boolean isFormSubmission(HttpServletRequest JavaDoc request) {
258         return super.isFormSubmission(request) || isFinishRequest(request) || isCancelRequest(request);
259     }
260
261     /**
262      * Calls page-specific referenceData method.
263      */

264     protected final Map JavaDoc referenceData(HttpServletRequest JavaDoc request, Object JavaDoc command, Errors errors)
265         throws Exception JavaDoc {
266
267         return referenceData(request, command, errors, getCurrentPage(request));
268     }
269
270     /**
271      * Create a reference data map for the given request, consisting of
272      * bean name/bean instance pairs as expected by ModelAndView.
273      * <p>The default implementation delegates to referenceData(HttpServletRequest, int).
274      * Subclasses can override this to set reference data used in the view.
275      * @param request current HTTP request
276      * @param command form object with request parameters bound onto it
277      * @param errors validation errors holder
278      * @param page current wizard page
279      * @return a Map with reference data entries, or <code>null</code> if none
280      * @throws Exception in case of invalid state or arguments
281      * @see #referenceData(HttpServletRequest, int)
282      * @see ModelAndView
283      */

284     protected Map JavaDoc referenceData(HttpServletRequest JavaDoc request, Object JavaDoc command, Errors errors, int page)
285         throws Exception JavaDoc {
286
287         return referenceData(request, page);
288     }
289
290     /**
291      * Create a reference data map for the given request, consisting of
292      * bean name/bean instance pairs as expected by ModelAndView.
293      * <p>The default implementation returns <code>null</code>.
294      * Subclasses can override this to set reference data used in the view.
295      * @param request current HTTP request
296      * @param page current wizard page
297      * @return a Map with reference data entries, or <code>null</code> if none
298      * @throws Exception in case of invalid state or arguments
299      * @see ModelAndView
300      */

301     protected Map JavaDoc referenceData(HttpServletRequest JavaDoc request, int page) throws Exception JavaDoc {
302         return null;
303     }
304
305
306     /**
307      * Show first page as form view.
308      */

309     protected final ModelAndView showForm(
310             HttpServletRequest JavaDoc request, HttpServletResponse JavaDoc response, BindException errors)
311         throws Exception JavaDoc {
312
313         return showPage(request, errors, getInitialPage(request, errors.getTarget()));
314     }
315
316     /**
317      * Prepare the form model and view, including reference and error data,
318      * for the given page. Can be used in {@link #processFinish} implementations,
319      * to show the corresponding page in case of validation errors.
320      * @param request current HTTP request
321      * @param errors validation errors holder
322      * @param page number of page to show
323      * @return the prepared form view
324      * @throws Exception in case of invalid state or arguments
325      */

326     protected final ModelAndView showPage(HttpServletRequest JavaDoc request, BindException errors, int page)
327         throws Exception JavaDoc {
328
329         if (page >= 0 && page < getPageCount(request, errors.getTarget())) {
330             if (logger.isDebugEnabled()) {
331                 logger.debug("Showing wizard page " + page + " for form bean '" + getCommandName() + "'");
332             }
333
334             // Set page session attribute, expose overriding request attribute.
335
Integer JavaDoc pageInteger = new Integer JavaDoc(page);
336             String JavaDoc pageAttrName = getPageSessionAttributeName(request);
337             if (isSessionForm()) {
338                 if (logger.isDebugEnabled()) {
339                     logger.debug("Setting page session attribute [" + pageAttrName + "] to: " + pageInteger);
340                 }
341                 request.getSession().setAttribute(pageAttrName, pageInteger);
342             }
343             request.setAttribute(pageAttrName, pageInteger);
344
345             // Set page request attribute for evaluation by views.
346
Map JavaDoc controlModel = new HashMap JavaDoc();
347             if (this.pageAttribute != null) {
348                 controlModel.put(this.pageAttribute, new Integer JavaDoc(page));
349             }
350             String JavaDoc viewName = getViewName(request, errors.getTarget(), page);
351             return showForm(request, errors, viewName, controlModel);
352         }
353
354         else {
355             throw new ServletException JavaDoc("Invalid wizard page number: " + page);
356         }
357     }
358
359     /**
360      * Return the page count for this wizard form controller.
361      * The default implementation delegates to {@link #getPageCount()}.
362      * <p>Can be overridden to dynamically adapt the page count.
363      * @param request current HTTP request
364      * @param command the command object as returned by formBackingObject
365      * @return the current page count
366      * @see #getPageCount
367      */

368     protected int getPageCount(HttpServletRequest JavaDoc request, Object JavaDoc command) {
369         return getPageCount();
370     }
371
372     /**
373      * Return the name of the view for the specified page of this wizard form controller.
374      * <p>The default implementation takes the view name from the {@link #getPages()} array.
375      * <p>Can be overridden to dynamically switch the page view or to return view names
376      * for dynamically defined pages.
377      * @param request current HTTP request
378      * @param command the command object as returned by formBackingObject
379      * @return the current page count
380      * @see #getPageCount
381      */

382     protected String JavaDoc getViewName(HttpServletRequest JavaDoc request, Object JavaDoc command, int page) {
383         return getPages()[page];
384     }
385
386     /**
387      * Return the initial page of the wizard, that is, the page shown at wizard startup.
388      * <p>The default implementation delegates to {@link #getInitialPage(HttpServletRequest)}.
389      * @param request current HTTP request
390      * @param command the command object as returned by formBackingObject
391      * @return the initial page number
392      * @see #getInitialPage(HttpServletRequest)
393      * @see #formBackingObject
394      */

395     protected int getInitialPage(HttpServletRequest JavaDoc request, Object JavaDoc command) {
396         return getInitialPage(request);
397     }
398
399     /**
400      * Return the initial page of the wizard, that is, the page shown at wizard startup.
401      * <p>The default implementation returns 0 for first page.
402      * @param request current HTTP request
403      * @return the initial page number
404      */

405     protected int getInitialPage(HttpServletRequest JavaDoc request) {
406         return 0;
407     }
408
409     /**
410      * Return the name of the HttpSession attribute that holds the page object
411      * for this wizard form controller.
412      * <p>The default implementation delegates to the {@link #getPageSessionAttributeName()}
413      * variant without arguments.
414      * @param request current HTTP request
415      * @return the name of the form session attribute, or <code>null</code> if not in session form mode
416      * @see #getPageSessionAttributeName
417      * @see #getFormSessionAttributeName(javax.servlet.http.HttpServletRequest)
418      * @see javax.servlet.http.HttpSession#getAttribute
419      */

420     protected String JavaDoc getPageSessionAttributeName(HttpServletRequest JavaDoc request) {
421         return getPageSessionAttributeName();
422     }
423
424     /**
425      * Return the name of the HttpSession attribute that holds the page object
426      * for this wizard form controller.
427      * <p>Default is an internal name, of no relevance to applications, as the form
428      * session attribute is not usually accessed directly. Can be overridden to use
429      * an application-specific attribute name, which allows other code to access
430      * the session attribute directly.
431      * @return the name of the page session attribute
432      * @see #getFormSessionAttributeName
433      * @see javax.servlet.http.HttpSession#getAttribute
434      */

435     protected String JavaDoc getPageSessionAttributeName() {
436         return getClass().getName() + ".PAGE." + getCommandName();
437     }
438
439     /**
440      * Handle an invalid submit request, e.g. when in session form mode but no form object
441      * was found in the session (like in case of an invalid resubmit by the browser).
442      * <p>The default implementation for wizard form controllers simply shows the initial page
443      * of a new wizard form. If you want to show some "invalid submit" message, you need
444      * to override this method.
445      * @param request current HTTP request
446      * @param response current HTTP response
447      * @return a prepared view, or <code>null</code> if handled directly
448      * @throws Exception in case of errors
449      * @see #showNewForm
450      * @see #setBindOnNewForm
451      */

452     protected ModelAndView handleInvalidSubmit(HttpServletRequest JavaDoc request, HttpServletResponse JavaDoc response)
453             throws Exception JavaDoc {
454
455         return showNewForm(request, response);
456     }
457
458
459     /**
460      * Apply wizard workflow: finish, cancel, page change.
461      */

462     protected final ModelAndView processFormSubmission(
463             HttpServletRequest JavaDoc request, HttpServletResponse JavaDoc response, Object JavaDoc command, BindException errors)
464             throws Exception JavaDoc {
465
466         int currentPage = getCurrentPage(request);
467         // Remove page session attribute, provide copy as request attribute.
468
String JavaDoc pageAttrName = getPageSessionAttributeName(request);
469         if (isSessionForm()) {
470             if (logger.isDebugEnabled()) {
471                 logger.debug("Removing page session attribute [" + pageAttrName + "]");
472             }
473             request.getSession().removeAttribute(pageAttrName);
474         }
475         request.setAttribute(pageAttrName, new Integer JavaDoc(currentPage));
476
477         // cancel?
478
if (isCancelRequest(request)) {
479             if (logger.isDebugEnabled()) {
480                 logger.debug("Cancelling wizard for form bean '" + getCommandName() + "'");
481             }
482             return processCancel(request, response, command, errors);
483         }
484
485         // finish?
486
if (isFinishRequest(request)) {
487             if (logger.isDebugEnabled()) {
488                 logger.debug("Finishing wizard for form bean '" + getCommandName() + "'");
489             }
490             return validatePagesAndFinish(request, response, command, errors, currentPage);
491         }
492
493         // Normal submit: validate current page and show specified target page.
494
if (!suppressValidation(request, command)) {
495             if (logger.isDebugEnabled()) {
496                 logger.debug("Validating wizard page " + currentPage + " for form bean '" + getCommandName() + "'");
497             }
498             validatePage(command, errors, currentPage, false);
499         }
500
501         // Give subclasses a change to perform custom post-procession
502
// of the current page and its command object.
503
postProcessPage(request, command, errors, currentPage);
504
505         int targetPage = getTargetPage(request, command, errors, currentPage);
506         if (logger.isDebugEnabled()) {
507             logger.debug("Target page " + targetPage + " requested");
508         }
509         if (targetPage != currentPage) {
510             if (!errors.hasErrors() || (this.allowDirtyBack && targetPage < currentPage) ||
511                     (this.allowDirtyForward && targetPage > currentPage)) {
512                 // Allowed to go to target page.
513
return showPage(request, errors, targetPage);
514             }
515         }
516
517         // Show current page again.
518
return showPage(request, errors, currentPage);
519     }
520
521     /**
522      * Return the current page number. Used by processFormSubmission.
523      * <p>The default implementation checks the page session attribute.
524      * Subclasses can override this for customized page determination.
525      * @see #processFormSubmission
526      * @see #getPageSessionAttributeName
527      */

528     protected int getCurrentPage(HttpServletRequest JavaDoc request) {
529         // Check for overriding attribute in request.
530
String JavaDoc pageAttrName = getPageSessionAttributeName(request);
531         Integer JavaDoc pageAttr = (Integer JavaDoc) request.getAttribute(pageAttrName);
532         if (pageAttr != null) {
533             return pageAttr.intValue();
534         }
535         // Check for explicit request parameter.
536
String JavaDoc pageParam = request.getParameter(PARAM_PAGE);
537         if (pageParam != null) {
538             return Integer.parseInt(pageParam);
539         }
540         // Check for original attribute in session.
541
if (isSessionForm()) {
542             pageAttr = (Integer JavaDoc) request.getSession().getAttribute(pageAttrName);
543             if (pageAttr != null) {
544                 return pageAttr.intValue();
545             }
546         }
547         throw new IllegalStateException JavaDoc(
548                 "Page attribute [" + pageAttrName + "] neither found in session nor in request");
549     }
550
551     /**
552      * Determine whether the incoming request is a request to finish the
553      * processing of the current form.
554      * <p>By default, this method returns <code>true</code> if a parameter
555      * matching the "_finish" key is present in the request, otherwise it
556      * returns <code>false</code>. Subclasses may override this method
557      * to provide custom logic to detect a finish request.
558      * <p>The parameter is recognized both when sent as a plain parameter
559      * ("_finish") or when triggered by an image button ("_finish.x").
560      * @param request current HTTP request
561      * @see #PARAM_FINISH
562      */

563     protected boolean isFinishRequest(HttpServletRequest JavaDoc request) {
564         return WebUtils.hasSubmitParameter(request, PARAM_FINISH);
565     }
566
567     /**
568      * Determine whether the incoming request is a request to cancel the
569      * processing of the current form.
570      * <p>By default, this method returns <code>true</code> if a parameter
571      * matching the "_cancel" key is present in the request, otherwise it
572      * returns <code>false</code>. Subclasses may override this method
573      * to provide custom logic to detect a cancel request.
574      * <p>The parameter is recognized both when sent as a plain parameter
575      * ("_cancel") or when triggered by an image button ("_cancel.x").
576      * @param request current HTTP request
577      * @see #PARAM_CANCEL
578      */

579     protected boolean isCancelRequest(HttpServletRequest JavaDoc request) {
580         return WebUtils.hasSubmitParameter(request, PARAM_CANCEL);
581     }
582
583     /**
584      * Return the target page specified in the request.
585      * <p>The default implementation delegates to {@link #getTargetPage(HttpServletRequest, int)}.
586      * Subclasses can override this for customized target page determination.
587      * @param request current HTTP request
588      * @param command form object with request parameters bound onto it
589      * @param errors validation errors holder
590      * @param currentPage the current page, to be returned as fallback
591      * if no target page specified
592      * @return the page specified in the request, or current page if not found
593      * @see #getTargetPage(HttpServletRequest, int)
594      */

595     protected int getTargetPage(HttpServletRequest JavaDoc request, Object JavaDoc command, Errors errors, int currentPage) {
596         return getTargetPage(request, currentPage);
597     }
598
599     /**
600      * Return the target page specified in the request.
601      * <p>The default implementation examines "_target" parameter (e.g. "_target1").
602      * Subclasses can override this for customized target page determination.
603      * @param request current HTTP request
604      * @param currentPage the current page, to be returned as fallback
605      * if no target page specified
606      * @return the page specified in the request, or current page if not found
607      * @see #PARAM_TARGET
608      */

609     protected int getTargetPage(HttpServletRequest JavaDoc request, int currentPage) {
610         Enumeration JavaDoc paramNames = request.getParameterNames();
611         while (paramNames.hasMoreElements()) {
612             String JavaDoc paramName = (String JavaDoc) paramNames.nextElement();
613             if (paramName.startsWith(PARAM_TARGET)) {
614                 for (int i = 0; i < WebUtils.SUBMIT_IMAGE_SUFFIXES.length; i++) {
615                     String JavaDoc suffix = WebUtils.SUBMIT_IMAGE_SUFFIXES[i];
616                     if (paramName.endsWith(suffix)) {
617                         paramName = paramName.substring(0, paramName.length() - suffix.length());
618                     }
619                 }
620                 return Integer.parseInt(paramName.substring(PARAM_TARGET.length()));
621             }
622         }
623         return currentPage;
624     }
625
626     /**
627      * Validate all pages and process finish.
628      * If there are page validation errors, show the corresponding view page.
629      */

630     private ModelAndView validatePagesAndFinish(
631             HttpServletRequest JavaDoc request, HttpServletResponse JavaDoc response, Object JavaDoc command, BindException errors,
632             int currentPage) throws Exception JavaDoc {
633
634         // In case of binding errors -> show current page.
635
if (errors.hasErrors()) {
636             return showPage(request, errors, currentPage);
637         }
638
639         if (!suppressValidation(request, command)) {
640             // In case of remaining errors on a page -> show the page.
641
for (int page = 0; page < getPageCount(request, command); page++) {
642                 validatePage(command, errors, page, true);
643                 if (errors.hasErrors()) {
644                     return showPage(request, errors, page);
645                 }
646             }
647         }
648
649         // No remaining errors -> proceed with finish.
650
return processFinish(request, response, command, errors);
651     }
652
653
654     /**
655      * Template method for custom validation logic for individual pages.
656      * The default implementation calls {@link #validatePage(Object, Errors, int)}.
657      * <p>Implementations will typically call fine-granular <code>validateXXX</code>
658      * methods of this instance's Validator, combining them to validation of the
659      * corresponding pages. The Validator's default <code>validate</code> method
660      * will not be called by a wizard form controller!
661      * @param command form object with the current wizard state
662      * @param errors validation errors holder
663      * @param page number of page to validate
664      * @param finish whether this method is called during final revalidation on finish
665      * (else, it is called for validating the current page)
666      * @see #validatePage(Object, Errors, int)
667      * @see org.springframework.validation.Validator#validate
668      */

669     protected void validatePage(Object JavaDoc command, Errors errors, int page, boolean finish) {
670         validatePage(command, errors, page);
671     }
672
673     /**
674      * Template method for custom validation logic for individual pages.
675      * The default implementation is empty.
676      * <p>Implementations will typically call fine-granular validateXXX methods of this
677      * instance's validator, combining them to validation of the corresponding pages.
678      * The validator's default <code>validate</code> method will not be called by a
679      * wizard form controller!
680      * @param command form object with the current wizard state
681      * @param errors validation errors holder
682      * @param page number of page to validate
683      * @see org.springframework.validation.Validator#validate
684      */

685     protected void validatePage(Object JavaDoc command, Errors errors, int page) {
686     }
687
688     /**
689      * Post-process the given page after binding and validation, potentially
690      * updating its command object. The passed-in request might contain special
691      * parameters sent by the page.
692      * <p>Only invoked when displaying another page or the same page again,
693      * not when finishing or cancelling.
694      * @param request current HTTP request
695      * @param command form object with request parameters bound onto it
696      * @param errors validation errors holder
697      * @param page number of page to post-process
698      * @throws Exception in case of invalid state or arguments
699      */

700     protected void postProcessPage(HttpServletRequest JavaDoc request, Object JavaDoc command, Errors errors, int page)
701             throws Exception JavaDoc {
702     }
703
704     /**
705      * Template method for processing the final action of this wizard.
706      * <p>Call <code>errors.getModel()</code> to populate the ModelAndView model
707      * with the command and the Errors instance, under the specified command name,
708      * as expected by the "spring:bind" tag.
709      * <p>You can call the {@link #showPage} method to return back to the wizard,
710      * in case of last-minute validation errors having been found that you would
711      * like to present to the user within the original wizard form.
712      * @param request current HTTP request
713      * @param response current HTTP response
714      * @param command form object with the current wizard state
715      * @param errors validation errors holder
716      * @return the finish view
717      * @throws Exception in case of invalid state or arguments
718      * @see org.springframework.validation.Errors
719      * @see org.springframework.validation.BindException#getModel
720      * @see #showPage(javax.servlet.http.HttpServletRequest, org.springframework.validation.BindException, int)
721      */

722     protected abstract ModelAndView processFinish(
723             HttpServletRequest JavaDoc request, HttpServletResponse JavaDoc response, Object JavaDoc command, BindException errors)
724             throws Exception JavaDoc;
725
726     /**
727      * Template method for processing the cancel action of this wizard.
728      * <p>The default implementation throws a ServletException, saying that a cancel
729      * operation is not supported by this controller. Thus, you do not need to
730      * implement this template method if you do not support a cancel operation.
731      * <p>Call <code>errors.getModel()</code> to populate the ModelAndView model
732      * with the command and the Errors instance, under the specified command name,
733      * as expected by the "spring:bind" tag.
734      * @param request current HTTP request
735      * @param response current HTTP response
736      * @param command form object with the current wizard state
737      * @param errors Errors instance containing errors
738      * @return the cancellation view
739      * @throws Exception in case of invalid state or arguments
740      * @see org.springframework.validation.Errors
741      * @see org.springframework.validation.BindException#getModel
742      */

743     protected ModelAndView processCancel(
744             HttpServletRequest JavaDoc request, HttpServletResponse JavaDoc response, Object JavaDoc command, BindException errors)
745             throws Exception JavaDoc {
746
747         throw new ServletException JavaDoc(
748                 "Wizard form controller class [" + getClass().getName() + "] does not support a cancel operation");
749     }
750
751 }
752
Popular Tags