📄 formaction.java
字号:
return binder.getErrors().hasErrors() ? error() : success();
}
/**
* Resets the form by clearing out the form object in the specified scope and
* reloading it by calling loadFormObject.
* @param context the request context
* @return "success" if the reset action completed successfully, "error" otherwise
* @throws Exception if an exception occured
*/
public Event resetForm(RequestContext context) throws Exception {
try {
Object formObject = loadFormObject(context);
setFormObject(context, formObject);
setFormErrors(context, createFormErrors(context, formObject));
return success();
}
catch (FormObjectRetrievalFailureException e) {
// handle retrieval exceptions only, other exceptions propagate
return error(e);
}
}
// internal helpers
/**
* Create a new binder instance for the given form object and request
* context. Can be overridden to plug in custom DataBinder subclasses.
* <p>
* Default implementation creates a standard WebDataBinder, and invokes
* initBinder. Note that initBinder will not be invoked if you override this
* method!
* @param context the action execution context, for accessing and setting
* data in "flow scope" or "request scope"
* @param formObject the form object to bind onto
* @return the new binder instance
* @see #initBinder(RequestContext, DataBinder)
*/
protected DataBinder createBinder(RequestContext context, Object formObject) {
DataBinder binder = new WebDataBinder(formObject, getFormObjectName());
if (this.messageCodesResolver != null) {
binder.setMessageCodesResolver(this.messageCodesResolver);
}
initBinder(context, binder);
return binder;
}
/**
* Bind parameters of the last event in given context to the form object
* using given binder.
* @param context the action execution context, for accessing and setting
* data in "flow scope" or "request scope"
* @param binder the data binder to use
*/
protected void doBind(RequestContext context, DataBinder binder) throws Exception {
if (logger.isDebugEnabled()) {
logger.debug(
"Binding allowed parameters in event: " + context.getLastEvent()
+ " to form object with name: '" + binder.getObjectName()
+ "', prebind-toString: " + binder.getTarget());
if (binder.getAllowedFields() != null && binder.getAllowedFields().length > 0) {
logger.debug("(Allowed event parameters are: " + binder.getAllowedFields() + ")");
}
else {
logger.debug("(Any event parameter is allowed)");
}
}
binder.bind(new MutablePropertyValues(context.getLastEvent().getParameters()));
if (logger.isDebugEnabled()) {
logger.debug("Binding completed for form object with name: '" + binder.getObjectName() + "', postbind-toString: " + binder.getTarget());
logger.debug("There are [" + binder.getErrors().getErrorCount() + "] errors, details: " + binder.getErrors().getAllErrors());
}
}
/**
* Validate given form object using a registered validator. If a "validatorMethod"
* action property is specified for the currently executing action,
* the identified validator method will be invoked. When no such property is found,
* the defualt <code>validate()</code> method is invoked.
* @param context the action execution context, for accessing and setting
* data in "flow scope" or "request scope"
* @param binder the data binder to use
*/
protected void doValidate(RequestContext context, DataBinder binder) throws Exception {
String validatorMethod = (String)context.getProperties().getAttribute(VALIDATOR_METHOD_PROPERTY);
if (StringUtils.hasText(validatorMethod)) {
invokeValidatorMethod(validatorMethod, binder.getTarget(), binder.getErrors());
}
else {
Assert.notNull(validator, "The validator must not be null but it is: programmer error");
if (logger.isDebugEnabled()) {
logger.debug("Invoking validator: " + validator);
}
getValidator().validate(binder.getTarget(), binder.getErrors());
}
if (logger.isDebugEnabled()) {
logger.debug("Validation completed for form object with name: '" + binder.getObjectName() + "'");
logger.debug("There are [" + binder.getErrors().getErrorCount() + "] errors, details: " + binder.getErrors().getAllErrors());
}
}
/**
* Invoke specified validator method on the validator registered with this
* action.
* @param validatorMethod the name of the validator method to invoke
* @param formObject the form object
* @param errors possible binding errors
*/
private void invokeValidatorMethod(String validatorMethod, Object formObject, Errors errors) throws Exception {
if (logger.isDebugEnabled()) {
logger.debug("Invoking piecemeal validator method: '" + validatorMethod + "' on form object: " + formObject);
}
getValidateMethodDispatcher().dispatch(validatorMethod, new Object[] { formObject, errors });
}
/**
* Convenience method that returns the form object for this form action.
* If not found in the configured scope, a new form object will be created
* or loaded by a call to {@link #loadFormObject(RequestContext)}.
* @param context the flow request context
* @return the form object
*/
protected Object getFormObject(RequestContext context) throws Exception {
Object formObject = getFormObjectAccessor(context).getFormObject(getFormObjectName(), getFormObjectClass(), getFormObjectScope());
if (formObject == null) {
formObject = loadFormObject(context);
}
return formObject;
}
/**
* Put given form object in the configured scope of given context.
*/
private void setFormObject(RequestContext context, Object formObject) {
getFormObjectAccessor(context).setFormObject(formObject, getFormObjectName(), getFormObjectScope());
}
/**
* Convenience method that returns the form object errors for this form action.
* If not found in the configured scope, a new form object errors will be created.
* @param context the flow request context
* @return the form errors
*/
protected Errors getFormErrors(RequestContext context) throws Exception {
return ensureFormErrorsExposed(context, getFormObject(context));
}
/**
* Expose an empty errors collection in the model of the currently executing flow if neccessary.
* @param context the flow execution request context
* @param errors the errors
*/
private Errors ensureFormErrorsExposed(RequestContext context, Object formObject) {
Errors errors = getFormObjectAccessor(context).getFormErrors(getFormObjectName(), getFormErrorsScope());
if (errors instanceof BindException) {
// make sure the existing form errors are consistent with the form object
BindException be = (BindException)errors;
if (be.getTarget() != formObject) {
if (logger.isInfoEnabled()) {
logger.info(
"Inconsistency detected: the Errors instance in '" + getFormErrorsScope() + "' does NOT wrap the current form object: "
+ formObject + " of class: " + formObject.getClass() + "; instead this Errors instance unexpectedly wraps the target object: "
+ be.getTarget() + " of class: " + be.getTarget().getClass() +
". [Taking corrective action: overwriting the existing Errors instance with an empty one for the current form object]");
}
// fall through below
errors = null;
}
}
if (errors == null) {
errors = createFormErrors(context, formObject);
setFormErrors(context, errors);
}
return errors;
}
/**
* Return an empty errors collection with property editors registered.
* @param context the flow execution request context
* @param formObject the object
* @return the new errors instance
*/
private Errors createFormErrors(RequestContext context, Object formObject) {
return createBinder(context, formObject).getErrors();
}
/**
* Put given errors instance in the configured scope of given context.
*/
private void setFormErrors(RequestContext context, Errors errors) {
getFormObjectAccessor(context).setFormErrors(errors, getFormErrorsScope());
}
// subclassing hook methods
/**
* Returns a dispatcher to invoke validation methods. Subclasses could
* override this to return a custom dispatcher.
*/
protected DispatchMethodInvoker getValidateMethodDispatcher() {
return validateMethodDispatcher;
}
/**
* Factory method that returns a new form object accessor for accessing form objects
* in the provided request context.
* @param context the flow request context
* @return the accessor
*/
protected FormObjectAccessor getFormObjectAccessor(RequestContext context) {
return new FormObjectAccessor(context);
}
/**
* Load the backing form object that should be updated from incoming event
* parameters and validated. By default, will attempt to instantiate a new
* form object instance transiently in memory if not already present in the
* configured scope by calling {@link #createFormObject(RequestContext)}.
* <p>
* Subclasses should override if they need to load the form object from a
* specific location or resource such as a database or filesystem.
* @param context the action execution context, for accessing and setting
* data in "flow scope" or "request scope"
* @return the form object
* @throws FormObjectRetrievalFailureException the form object could not be
* loaded
*/
protected Object loadFormObject(RequestContext context) throws FormObjectRetrievalFailureException, Exception {
return createFormObject(context);
}
/**
* Create a new form object by instantiating the configured form object class.
* @param context the action execution context, for accessing and setting
* data in "flow scope" or "request scope"
* @return the form object
* @throws Exception the form object could not be created
*/
protected Object createFormObject(RequestContext context) throws Exception {
if (logger.isDebugEnabled()) {
logger.debug("Creating new form object '" + getFormObjectName() + "'");
}
if (this.formObjectClass == null) {
throw new IllegalStateException("Cannot create form object without formObjectClass being set -- "
+ "either set formObjectClass or override this method");
}
if (logger.isDebugEnabled()) {
logger.debug("Creating new form object of class [" + this.formObjectClass.getName() + "]");
}
return this.formObjectClass.newInstance();
}
/**
* Initialize the given binder instance, for example with custom editors.
* Called by createBinder().
* <p>
* This method allows you to register custom editors for certain fields of
* your form object. For instance, you will be able to transform Date
* objects into a String pattern and back, in order to allow your JavaBeans
* to have Date properties and still be able to set and display them in an
* HTML interface.
* <p>
* Default implementation will simply call registerCustomEditors on any
* propertyEditorRegistrar object that has been set for the action.
* <p>
* The request context may be used to feed reference data to any property
* editors, although it may be better (in the interest of not bloating the
* session, to have the editors get this from somewhere else).
* @param context the action execution context, for accessing and setting
* data in "flow scope" or "request scope"
* @param binder new binder instance
* @see #createBinder(RequestContext, Object)
*/
protected void initBinder(RequestContext context, DataBinder binder) {
if (propertyEditorRegistrar != null) {
propertyEditorRegistrar.registerCustomEditors(binder);
}
else {
if (logger.isDebugEnabled()) {
logger.debug("No property editor registrar set, no custom editors to register");
}
}
}
/**
* Returns true if event parameters should be bound to the form object during
* the {@link #setupForm(RequestContext)} action. The default implementation just
* calls {@link #isBindOnSetupForm()}.
*/
protected boolean setupBindingEnabled(RequestContext context) {
return isBindOnSetupForm();
}
/**
* Return whether validation should be performed given the state of the flow request
* context.
* <p>
* Default implementation always returns true. Can be overridden
* in subclasses to test validation, for example, if a special
* event parameter is set.
* @param context the request context, for accessing and setting
* data in "flow scope" or "request scope"
* @return whether or not validation is enabled
*/
protected boolean validationEnabled(RequestContext context) {
return true;
}
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -