org.apache.wicket.markup.html.form.Form.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.wicket.markup.html.form.Form.java

Source

/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.apache.wicket.markup.html.form;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;

import org.apache.commons.fileupload.FileUploadBase;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.wicket.Component;
import org.apache.wicket.IGenericComponent;
import org.apache.wicket.IRequestListener;
import org.apache.wicket.Page;
import org.apache.wicket.WicketRuntimeException;
import org.apache.wicket.ajax.AjaxRequestTarget;
import org.apache.wicket.behavior.Behavior;
import org.apache.wicket.core.util.string.CssUtils;
import org.apache.wicket.event.IEvent;
import org.apache.wicket.markup.ComponentTag;
import org.apache.wicket.markup.MarkupStream;
import org.apache.wicket.markup.html.WebMarkupContainer;
import org.apache.wicket.markup.html.form.upload.FileUploadField;
import org.apache.wicket.markup.html.form.validation.FormValidatorAdapter;
import org.apache.wicket.markup.html.form.validation.IFormValidator;
import org.apache.wicket.model.IModel;
import org.apache.wicket.model.Model;
import org.apache.wicket.protocol.http.servlet.MultipartServletWebRequest;
import org.apache.wicket.protocol.http.servlet.ServletWebRequest;
import org.apache.wicket.request.IRequestParameters;
import org.apache.wicket.request.Response;
import org.apache.wicket.request.mapper.parameter.PageParameters;
import org.apache.wicket.util.encoding.UrlDecoder;
import org.apache.wicket.util.lang.Args;
import org.apache.wicket.util.lang.Bytes;
import org.apache.wicket.util.lang.Generics;
import org.apache.wicket.util.string.AppendingStringBuffer;
import org.apache.wicket.util.string.PrependingStringBuffer;
import org.apache.wicket.util.string.Strings;
import org.apache.wicket.util.string.interpolator.MapVariableInterpolator;
import org.apache.wicket.util.value.LongValue;
import org.apache.wicket.util.visit.ClassVisitFilter;
import org.apache.wicket.util.visit.IVisit;
import org.apache.wicket.util.visit.IVisitor;
import org.apache.wicket.util.visit.Visits;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Container for {@link FormComponent}s (such as {@link CheckBox}es, {@link ListChoice}s or
 * {@link TextField}s). Subclass this class to receive submit notifications through
 * {@link #onSubmit()} or nest multiple {@link IFormSubmittingComponent}s if you want to vary submit
 * behavior. In the former case it is not necessary to use any of Wicket's classes (such as
 * {@link Button} or {@link SubmitLink}), just putting e.g. <input type="submit" value="go"/>
 * suffices.
 * <p>
 * As a {@link IRequestListener} the form gets notified of listener requests in
 * {@link #onRequest()}. By default, the processing of this submit works like this:
 * <ul>
 * <li>All nested {@link FormComponent}s are notified of new input via
 * {@link FormComponent#inputChanged()}</li>
 * <li>The form submitter is looked up, e.g. a {@link Button} is contained in the component
 * hierarchy of this form and was clicked by the user:
 * <ul>
 * <li>If an {@link IFormSubmitter} was found which
 * {@link IFormSubmitter#getDefaultFormProcessing()} returns {@code false} (default is {@code true}
 * ), it's {@link IFormSubmitter#onSubmit()} method will be called right away, thus all further
 * processing is skipped. This has the same effect as nesting a normal link in the form. <br>
 * If needed the form submitter can continue processing however, by calling {@link #validate()} to
 * execute form validation, {@link #hasError()} to find out whether validate() resulted in
 * validation errors, and {@link #updateFormComponentModels()} to update the models of nested form
 * components.</li>
 * <li>Otherwise this form is further processed via {@link #process(IFormSubmitter)}, resulting in
 * all nested components being validated via {@link FormComponent#validate()}. <br>
 * <ul>
 * <li>If form validation failed, all nested form components will be marked invalid, and
 * {@link #onError()} is called to allow clients to provide custom error handling code.</li>
 * <li>Otherwise the nested components will be asked to update their models via
 * {@link FormComponent#updateModel()}. After that submit notification is delegated to the
 * {@link IFormSubmitter#onSubmit()} (if just found) before calling {@link #onSubmit()} on this
 * form. Subclasses may override {@link #delegateSubmit(IFormSubmitter)} if they want a different
 * behavior.</li>
 * </ul>
 * </li>
 * </ul>
 * </li>
 * </ul>
 * 
 * A Form can be configured for handling uploads with multipart requests (e.g. files) by calling
 * {@link #setMultiPart(boolean)} (although Wicket will try to automatically detect this for you).
 * Use this with {@link FileUploadField} components. You can attach multiple {@link FileUploadField}
 * components for multiple file uploads.
 * <p>
 * In case of an upload error two resource keys are available to specify error messages:
 * {@code uploadTooLarge} and {@code uploadFailed}, i.e. for a form with id {@code myform} in
 * {@code MyPage.properties}:
 * 
 * <pre>
 * myform.uploadTooLarge=You have uploaded a file that is over the allowed limit of 2Mb
 * </pre>
 * 
 * Forms can be nested. You can put a form in another form. Since HTML doesn't allow nested
 * &lt;form&gt; tags, the inner forms will be rendered using the &lt;div&gt; tag. You have to submit
 * the inner forms using explicit components (like {@link Button} or {@link SubmitLink}), you can't
 * rely on implicit submit behavior (by using just &lt;input type="submit"&gt; that is not attached
 * to a component).
 * <p>
 * When a nested form is submitted, the user entered values in outer (parent) forms are preserved
 * and only the fields in the submitted form are validated. </b>
 * 
 * @author Jonathan Locke
 * @author Juergen Donnerstag
 * @author Eelco Hillenius
 * @author Cameron Braid
 * @author Johan Compagner
 * @author Igor Vaynberg (ivaynberg)
 * @author David Leangen
 * 
 * @param <T>
 *            The model object type
 */
public class Form<T> extends WebMarkupContainer implements IRequestListener, IGenericComponent<T, Form<T>> {
    public static final String ENCTYPE_MULTIPART_FORM_DATA = "multipart/form-data";

    /**
     * Visitor used for validation
     * 
     * @author Igor Vaynberg (ivaynberg)
     */
    public abstract static class ValidationVisitor implements IVisitor<FormComponent<?>, Void> {
        @Override
        public void component(final FormComponent<?> formComponent, final IVisit<Void> visit) {

            Form<?> form = formComponent.getForm();
            if (!form.isVisibleInHierarchy() || !form.isEnabledInHierarchy()) {
                // do not validate formComponent or any of formComponent's children
                visit.dontGoDeeper();
                return;
            }

            if (formComponent.isVisibleInHierarchy() && formComponent.isEnabledInHierarchy()) {
                validate(formComponent);
            }
            if (formComponent.processChildren() == false) {
                visit.dontGoDeeper();
            }
        }

        /**
         * Callback that should be used to validate form component
         * 
         * @param formComponent
         */
        public abstract void validate(FormComponent<?> formComponent);
    }

    /**
     * Visitor used to update component models
     * 
     * @author Igor Vaynberg (ivaynberg)
     */
    private static class FormModelUpdateVisitor implements IVisitor<Component, Void> {
        private final Form<?> formFilter;

        /**
         * Constructor
         * 
         * @param formFilter
         */
        public FormModelUpdateVisitor(Form<?> formFilter) {
            this.formFilter = formFilter;
        }

        /** {@inheritDoc} */
        @Override
        public void component(final Component component, final IVisit<Void> visit) {
            if (component instanceof IFormModelUpdateListener) {
                final Form<?> form = Form.findForm(component);
                if (form != null) {
                    if (this.formFilter == null || this.formFilter == form) {
                        if (form.isEnabledInHierarchy()) {
                            if (component.isVisibleInHierarchy() && component.isEnabledInHierarchy()) {
                                ((IFormModelUpdateListener) component).updateModel();
                            }
                        }
                    }
                }
            }
        }
    }

    /**
     * Constant for specifying how a form is submitted, in this case using get.
     */
    public static final String METHOD_GET = "get";

    /**
     * Constant for specifying how a form is submitted, in this case using post.
     */
    public static final String METHOD_POST = "post";

    /** Flag that indicates this form has been submitted during this request */
    private static final short FLAG_SUBMITTED = FLAG_RESERVED1;

    /** Log. */
    private static final Logger log = LoggerFactory.getLogger(Form.class);

    private static final long serialVersionUID = 1L;

    private static final String UPLOAD_FAILED_RESOURCE_KEY = "uploadFailed";

    private static final String UPLOAD_TOO_LARGE_RESOURCE_KEY = "uploadTooLarge";
    private static final String UPLOAD_SINGLE_FILE_TOO_LARGE_RESOURCE_KEY = "uploadSingleFileTooLarge";

    /**
     * Any default IFormSubmittingComponent. If set, a hidden submit component will be rendered
     * right after the form tag, so that when users press enter in a textfield, this submit
     * component's action will be selected. If no default IFormSubmittingComponent is set, nothing
     * additional is rendered.
     * <p>
     * WARNING: note that this is a best effort only. Unfortunately having a 'default'
     * IFormSubmittingComponent in a form is ill defined in the standards, and of course IE has it's
     * own way of doing things.
     * </p>
     */
    private IFormSubmittingComponent defaultSubmittingComponent;

    /**
     * Maximum size of an upload in bytes. If null, the setting
     * {@link org.apache.wicket.settings.ApplicationSettings#getDefaultMaximumUploadSize()} is used.
     */
    private Bytes maxSize = null;

    /**
     * Maximum size of file of upload in bytes (if there are more than one) in request.
     */
    private Bytes fileMaxSize;

    /** True if the form has enctype of multipart/form-data */
    private short multiPart = 0;

    /**
     * A user has explicitly called {@link #setMultiPart(boolean)} with value {@code true} forcing
     * it to be true
     */
    private static final short MULTIPART_HARD = 0x01;

    /**
     * The form has discovered a multipart component before rendering and is marking itself as
     * multipart until next render
     */
    private static final short MULTIPART_HINT_YES = 0x02;

    /**
     * The form has discovered no multipart component before rendering and is marking itself as
     * not multipart until next render
     */
    private static final short MULTIPART_HINT_NO = 0x04;

    /**
     * Constructs a form with no validation.
     * 
     * @param id
     *            See Component
     */
    public Form(final String id) {
        this(id, null);
    }

    /**
     * @param id
     *            See Component
     * @param model
     *            See Component
     * @see org.apache.wicket.Component#Component(String, IModel)
     */
    public Form(final String id, final IModel<T> model) {
        super(id, model);
        setOutputMarkupId(true);
    }

    /**
     * Adds a form validator to the form.
     * 
     * @param validator
     *            validator
     * @throws IllegalArgumentException
     *             if validator is null
     * @see IFormValidator
     */
    public void add(final IFormValidator validator) {
        Args.notNull(validator, "validator");

        if (validator instanceof Behavior) {
            add((Behavior) validator);
        } else {
            add(new FormValidatorAdapter(validator));
        }
    }

    /**
     * Removes a form validator from the form.
     * 
     * @param validator
     *            validator
     * @throws IllegalArgumentException
     *             if validator is null
     * @see IFormValidator
     */
    public void remove(final IFormValidator validator) {
        Args.notNull(validator, "validator");

        Behavior match = null;
        for (Behavior behavior : getBehaviors()) {
            if (behavior.equals(validator)) {
                match = behavior;
                break;
            } else if (behavior instanceof FormValidatorAdapter) {
                if (((FormValidatorAdapter) behavior).getValidator().equals(validator)) {
                    match = behavior;
                    break;
                }
            }
        }

        if (match != null) {
            remove(match);
        } else {

            throw new IllegalStateException("Tried to remove form validator that was not previously added. "
                    + "Make sure your validator's equals() implementation is sufficient");
        }
    }

    /**
     * Clears the input from the form's nested children of type {@link FormComponent}. This method
     * is typically called when a form needs to be reset.
     */
    public final void clearInput() {
        // Visit all the (visible) form components and clear the input on each.
        visitFormComponentsPostOrder(new IVisitor<FormComponent<?>, Void>() {
            @Override
            public void component(final FormComponent<?> formComponent, IVisit<Void> visit) {
                if (formComponent.isVisibleInHierarchy()) {
                    // Clear input from form component
                    formComponent.clearInput();
                }
            }
        });
    }

    /**
     * Registers an error feedback message for this component
     * 
     * @param error
     *            error message
     * @param args
     *            argument replacement map for ${key} variables
     */
    public final void error(String error, Map<String, Object> args) {
        error(new MapVariableInterpolator(error, args).toString());
    }

    /**
     * THIS METHOD IS NOT PART OF THE WICKET PUBLIC API. DO NOT USE IT!
     * <p>
     * Gets the IFormSubmittingComponent which submitted this form.
     * 
     * @return The component which submitted this form, or null if the processing was not triggered
     *         by a registered IFormSubmittingComponent
     */
    public final IFormSubmitter findSubmittingButton() {
        IFormSubmittingComponent submittingComponent = getPage().visitChildren(IFormSubmittingComponent.class,
                new IVisitor<Component, IFormSubmittingComponent>() {
                    @Override
                    public void component(final Component component, final IVisit<IFormSubmittingComponent> visit) {
                        // Get submitting component
                        final IFormSubmittingComponent submittingComponent = (IFormSubmittingComponent) component;
                        final Form<?> form = submittingComponent.getForm();

                        // Check for component-name or component-name.x request string
                        if ((form != null) && (form.getRootForm() == Form.this)) {
                            String name = submittingComponent.getInputName();
                            IRequestParameters parameters = getRequest().getRequestParameters();
                            if ((!parameters.getParameterValue(name).isNull())
                                    || !parameters.getParameterValue(name + ".x").isNull()) {
                                visit.stop(submittingComponent);
                            }
                        }
                    }
                });

        return submittingComponent;
    }

    /**
     * Gets the default IFormSubmittingComponent. If set (not null), a hidden submit component will
     * be rendered right after the form tag, so that when users press enter in a textfield, this
     * submit component's action will be selected. If no default component is set (it is null),
     * nothing additional is rendered.
     * <p>
     * WARNING: note that this is a best effort only. Unfortunately having a 'default' button in a
     * form is ill defined in the standards, and of course IE has it's own way of doing things.
     * </p>
     * There can be only one default submit component per form hierarchy. So if you want to get the
     * default component on a nested form, it will actually delegate the call to root form. </b>
     * 
     * @return The submit component to set as the default IFormSubmittingComponent, or null when you
     *         want to 'unset' any previously set default IFormSubmittingComponent
     */
    public final IFormSubmittingComponent getDefaultButton() {
        if (isRootForm()) {
            return defaultSubmittingComponent;
        } else {
            return getRootForm().getDefaultButton();
        }
    }

    /**
     * Gets all {@link IFormValidator}s added to this form
     * 
     * @return unmodifiable collection of {@link IFormValidator}s
     */
    public final Collection<IFormValidator> getFormValidators() {
        List<IFormValidator> validators = new ArrayList<>();

        for (Behavior behavior : getBehaviors()) {
            if (behavior instanceof IFormValidator) {
                validators.add((IFormValidator) behavior);
            }
        }

        return Collections.unmodifiableCollection(validators);
    }

    /**
     * @deprecated use {@link #getJsForListenerUrl(CharSequence)} instead.
     */
    public final CharSequence getJsForInterfaceUrl(CharSequence url) {
        return getJsForListenerUrl(url);
    }

    /**
     * Generate a piece of JavaScript that submits the form to the given URL of an {@link IRequestListener}.
     * 
     * Warning: This code should only be called in the rendering phase for form components inside
     * the form because it uses the css/javascript id of the form which can be stored in the markup.
     * 
     * @param url
     *            The listener url to be submitted to
     * @return the javascript code that submits the form.
     */
    public final CharSequence getJsForListenerUrl(CharSequence url) {
        Form<?> root = getRootForm();

        AppendingStringBuffer buffer = new AppendingStringBuffer();

        String action = url.toString();
        if (root.encodeUrlInHiddenFields()) {
            buffer.append(String.format("document.getElementById('%s').innerHTML = '", root.getHiddenFieldsId()));

            // parameter must be sent as hidden field, as it would be ignored in the action URL
            int i = action.indexOf('?');
            if (i != -1) {
                writeParamsAsHiddenFields(Strings.split(action.substring(i + 1), '&'), buffer);

                action = action.substring(0, i);
            }

            buffer.append("';");
        }
        buffer.append(String.format("var f = document.getElementById('%s');", root.getMarkupId()));
        buffer.append(String.format("f.action='%s';", action));
        buffer.append("f.submit();");
        return buffer;
    }

    /**
     * Generate a piece of JavaScript that submits the form with the given {@link IFormSubmittingComponent}.
     * 
     * @param submitter
     *            the submitter
     * @return the javascript code that submits the form.
     * 
     * @see #findSubmittingButton()
     */
    public final CharSequence getJsForSubmitter(IFormSubmittingComponent submitter) {
        Form<?> root = getRootForm();

        String param = submitter.getInputName() + "=x";

        AppendingStringBuffer buffer = new AppendingStringBuffer();
        buffer.append(String.format("var f = document.getElementById('%s');", root.getMarkupId()));
        if (root.encodeUrlInHiddenFields()) {
            buffer.append(String.format("document.getElementById('%s').innerHTML += '", root.getHiddenFieldsId()));

            writeParamsAsHiddenFields(new String[] { param }, buffer);

            buffer.append("';");
        } else {
            String action = root.getActionUrl().toString();
            buffer.append("f.action += '" + (action.indexOf('?') > -1 ? '&' : '?') + param + "';");
        }
        buffer.append("f.submit();");
        return buffer;
    }

    /**
     * Gets the maximum size for uploads. If null, the setting
     * {@link org.apache.wicket.settings.ApplicationSettings#getDefaultMaximumUploadSize()} is used.
     * 
     * 
     * @return the maximum size
     */
    public final Bytes getMaxSize() {
        /*
         * NOTE: This method should remain final otherwise it will be impossible to set a default
         * max size smaller then the one specified in applications settings because the inner form
         * will return the default unless it is specifically set in the traversal. With this method
         * remaining final we can tell when the value is explicitly set by the user.
         * 
         * If the value needs to be dynamic it can be set in oncofigure() instead of overriding this
         * method.
         */

        final Bytes[] maxSize = { this.maxSize };
        if (maxSize[0] == null) {
            visitChildren(Form.class, new IVisitor<Form<?>, Bytes>() {
                @Override
                public void component(Form<?> component, IVisit<Bytes> visit) {
                    maxSize[0] = LongValue.maxNullSafe(maxSize[0], component.maxSize);
                }
            });
        }
        if (maxSize[0] == null) {
            return getApplication().getApplicationSettings().getDefaultMaximumUploadSize();
        }
        return maxSize[0];
    }

    /**
     * Gets maximum size for each file of an upload.
     * 
     * @return
     */
    public Bytes getFileMaxSize() {
        return fileMaxSize;
    }

    /**
     * Returns the root form or this, if this is the root form.
     * 
     * @return root form or this form
     */
    public Form<?> getRootForm() {
        Form<?> form;
        Form<?> parent = this;
        do {
            form = parent;
            parent = form.findParent(Form.class);
        } while (parent != null);

        return form;
    }

    /**
     * Returns the prefix used when building validator keys. This allows a form to use a separate
     * "set" of keys. For example if prefix "short" is returned, validator key short.Required will
     * be tried instead of Required key.
     * <p>
     * This can be useful when different designs are used for a form. In a form where error messages
     * are displayed next to their respective form components as opposed to at the top of the form,
     * the ${label} attribute is of little use and only causes redundant information to appear in
     * the message. Forms like these can return the "short" (or any other string) validator prefix
     * and declare key: short.Required=required to override the longer message which is usually
     * declared like this: Required=${label} is a required field
     * <p>
     * Returned prefix will be used for all form components. The prefix can also be overridden on
     * form component level by overriding {@link FormComponent#getValidatorKeyPrefix()}
     * 
     * @return prefix prepended to validator keys
     */
    public String getValidatorKeyPrefix() {
        return null;
    }

    /**
     * Gets whether the current form has any error registered.
     * 
     * @return True if this form has at least one error.
     */
    public final boolean hasError() {
        // if this form itself has an error message
        if (hasErrorMessage()) {
            return true;
        }

        // the form doesn't have any errors, now check any nested form
        // components
        return anyFormComponentError();
    }

    /**
     * Returns whether the form is a root form, which means that there's no other form in it's
     * parent hierarchy.
     * 
     * @return true if form is a root form, false otherwise
     */
    public boolean isRootForm() {
        return findParent(Form.class) == null;
    }

    /**
     * Checks if this form has been submitted during the current request
     * 
     * @return true if the form has been submitted during this request, false otherwise
     */
    public final boolean isSubmitted() {
        return getFlag(FLAG_SUBMITTED);
    }

    /**
     * THIS METHOD IS NOT PART OF THE WICKET API. DO NOT ATTEMPT TO OVERRIDE OR CALL IT.
     * 
     * Handles form submissions.
     * 
     * @see #onFormSubmitted(IFormSubmitter)
     */
    @Override
    public final void onRequest() {
        onFormSubmitted(null);
    }

    /**
     * Called when a form has been submitted using a method differing from return value of
     * {@link #getMethod()}. For example, someone can copy and paste the action url and invoke the
     * form using a {@code GET} instead of the desired {@code POST}. This method allows the user to
     * react to this situation.
     * 
     * @return response that can either abort or continue the processing of the form
     */
    protected MethodMismatchResponse onMethodMismatch() {
        return MethodMismatchResponse.CONTINUE;
    }

    /**
     * THIS METHOD IS NOT PART OF THE WICKET API. DO NOT ATTEMPT TO OVERRIDE OR CALL IT.
     * 
     * Handles form submissions.
     * 
     * @param submitter
     *            listener that will receive form processing events, if {@code null} the form will
     *            attempt to locate one
     * 
     * @see Form#validate()
     */
    public final void onFormSubmitted(IFormSubmitter submitter) {
        // check methods match
        if (getRequest().getContainerRequest() instanceof HttpServletRequest) {
            String desiredMethod = getMethod();
            String actualMethod = ((HttpServletRequest) getRequest().getContainerRequest()).getMethod();
            if (!actualMethod.equalsIgnoreCase(desiredMethod)) {
                MethodMismatchResponse response = onMethodMismatch();
                switch (response) {
                case ABORT:
                    return;
                case CONTINUE:
                    break;
                default:
                    throw new IllegalStateException(
                            "Invalid " + MethodMismatchResponse.class.getName() + " value: " + response);
                }
            }
        }

        markFormsSubmitted(submitter);

        if (handleMultiPart()) {
            // Tells FormComponents that a new user input has come
            inputChanged();

            // First, see if the processing was triggered by a IFormSubmittingComponent
            if (submitter == null) {
                submitter = findSubmittingButton();

                if (submitter instanceof IFormSubmittingComponent) {
                    IFormSubmittingComponent submittingComponent = (IFormSubmittingComponent) submitter;
                    Component component = (Component) submitter;

                    if (!component.isVisibleInHierarchy()) {
                        throw new WicketRuntimeException("Submit Button " + submittingComponent.getInputName()
                                + " (path=" + component.getPageRelativePath() + ") is not visible");
                    }

                    if (!component.isEnabledInHierarchy()) {
                        throw new WicketRuntimeException("Submit Button " + submittingComponent.getInputName()
                                + " (path=" + component.getPageRelativePath() + ") is not enabled");
                    }
                }
            }

            // When processing was triggered by a Wicket IFormSubmittingComponent and that
            // component indicates it wants to be called immediately
            // (without processing), call the IFormSubmittingComponent.onSubmit* methods right
            // away.
            if (submitter != null && !submitter.getDefaultFormProcessing()) {
                submitter.onSubmit();
                submitter.onAfterSubmit();
            } else {
                // the submit request might be for one of the nested forms, so let's
                // find the right one:
                final Form<?> formToProcess = findFormToProcess(submitter);

                // process the form for this request
                formToProcess.process(submitter);
            }
        }
        // If multi part did fail check if an error is registered and call
        // onError
        else if (hasError()) {
            callOnError(submitter);
        }

        // update auto labels if we are inside an ajax request
        getRequestCycle().find(AjaxRequestTarget.class).ifPresent(target -> {
            visitChildren(FormComponent.class, new IVisitor<FormComponent<?>, Void>() {
                @Override
                public void component(FormComponent<?> component, IVisit<Void> visit) {
                    component.updateAutoLabels(target);
                }
            });
        });
    }

    /**
     * This method finds the correct form that should be processed based on the submitting component
     * (if there is one) and correctly handles nested forms by also looking at
     * {@link #wantSubmitOnNestedFormSubmit()} throughout the form hierarchy. The form that needs to
     * be processed is:
     * <ul>
     * <li>if there is no submitting component (i.e. a "default submit"): this form.</li>
     * <li>if only one form exists (this): this form.</li>
     * <li>if nested forms exist:
     * <ul>
     * <li>if the submitting component points at the root form: the root form</li>
     * <li>if the submitting component points at a nested form:
     * <ul>
     * <li>starting at that nested form, the outermost form that returns true for
     * {@link #wantSubmitOnNestedFormSubmit()}</li>
     * <li>if no outer form returns true for that, the nested form is returned.</li>
     * </ul>
     * </li>
     * </ul>
     * </li>
     * </ul>
     * 
     * @param submitter
     *            The submitting component, if any. May be null.
     * @return The form that needs to be processed.
     */
    private Form<?> findFormToProcess(IFormSubmitter submitter) {
        if (submitter == null) {
            // no submitting component => default form submit => so *this* is the
            // form to process
            return this;
        } else {
            // some button submitted this request, this is the form it belongs to:
            final Form<?> targetedForm = submitter.getForm();
            if (targetedForm == null) {
                throw new IllegalStateException("submitting component must not return 'null' on getForm()");
            }

            final Form<?> rootForm = getRootForm();
            if (targetedForm == rootForm) {
                // the submitting component points at the root form => so let's just go with
                // root, everything else will be submitted with it anyway.
                return rootForm;
            } else {
                // a different form was targeted. let's find the outermost form that wants to be
                // submitted.
                Form<?> formThatWantsToBeSubmitted = targetedForm;
                Form<?> current = targetedForm.findParent(Form.class);
                while (current != null) {
                    if (current.wantSubmitOnNestedFormSubmit()) {
                        formThatWantsToBeSubmitted = current;
                    }
                    current = current.findParent(Form.class);
                }
                return formThatWantsToBeSubmitted;
            }
        }
    }

    /**
     * Whether this form wants to be submitted too if a nested form is submitted. By default, this
     * is false, so when a nested form is submitted, this form will <em>not</em> be submitted. If
     * this method is overridden to return true, this form <em>will</em> be submitted.
     * 
     * @return Whether this form wants to be submitted too if a nested form is submitted.
     */
    // TODO wicket-7 migration guide: changed from public to protected
    protected boolean wantSubmitOnNestedFormSubmit() {
        return false;
    }

    /**
     * Whether this *nested* form wants to be submitted when parent form is submitted. By default,
     * this is true, so when a parent form is submitted, the nested form is also submitted. If this
     * method is overridden to return false, it will not be validated, processed nor submitted.
     * 
     * @return {@code true} by default
     */
    protected boolean wantSubmitOnParentFormSubmit() {
        return true;
    }

    /**
     * Process the form. Though you can override this method to provide your own algorithm, it is
     * not recommended to do so.
     * 
     * <p>
     * See the class documentation for further details on the form processing
     * </p>
     * 
     * @param submittingComponent
     *            component responsible for submitting the form, or <code>null</code> if none (eg
     *            the form has been submitted via the enter key or javascript calling form.submit())
     * 
     * @see #delegateSubmit(IFormSubmitter) for an easy way to process submitting component in the
     *      default manner
     */
    public void process(IFormSubmitter submittingComponent) {
        if (!isEnabledInHierarchy() || !isVisibleInHierarchy()) {
            // since process() can be called outside of the default form workflow, an additional
            // check is needed

            // FIXME throw listener exception
            return;
        }

        // run validation
        validate();

        // If a validation error occurred
        if (hasError()) {
            // mark all children as invalid
            markFormComponentsInvalid();

            // let subclass handle error
            callOnError(submittingComponent);
        } else {
            // mark all children as valid
            markFormComponentsValid();

            // before updating, call the interception method for clients
            beforeUpdateFormComponentModels();

            // Update model using form data
            updateFormComponentModels();

            // validate model objects after input values have been bound
            internalOnValidateModelObjects();
            if (hasError()) {
                callOnError(submittingComponent);
                return;
            }

            // Form has no error
            delegateSubmit(submittingComponent);
        }
    }

    /**
     * Calls onError on this {@link Form} and any enabled and visible nested form, if the respective
     * {@link Form} actually has errors.
     * 
     * @param submitter
     */
    protected void callOnError(IFormSubmitter submitter) {
        final Form<?> processingForm = findFormToProcess(submitter);

        if (submitter != null) {
            submitter.onError();
        }

        // invoke Form#onSubmit(..) going from innermost to outermost
        Visits.visitPostOrder(processingForm, new IVisitor<Form<?>, Void>() {
            @Override
            public void component(Form<?> form, IVisit<Void> visit) {
                if (!form.isEnabledInHierarchy() || !form.isVisibleInHierarchy()) {
                    visit.dontGoDeeper();
                    return;
                }
                if (form.hasError()) {
                    form.onError();
                }
            }
        }, new ClassVisitFilter(Form.class));
    }

    /**
     * Sets FLAG_SUBMITTED to true on this form and every enabled nested form.
     * @param submitter 
     */
    private void markFormsSubmitted(IFormSubmitter submitter) {
        setFlag(FLAG_SUBMITTED, true);
        Form<?> formToProcess = findFormToProcess(submitter);

        visitChildren(Form.class, new IVisitor<Component, Void>() {
            @Override
            public void component(final Component component, final IVisit<Void> visit) {
                Form<?> form = (Form<?>) component;
                if ((form.wantSubmitOnParentFormSubmit() || form == formToProcess) && form.isEnabledInHierarchy()
                        && form.isVisibleInHierarchy()) {
                    form.setFlag(FLAG_SUBMITTED, true);
                    return;
                }
                visit.dontGoDeeper();
            }
        });
    }

    /**
     * Sets the default IFormSubmittingComponent. If set (not null), a hidden submit component will
     * be rendered right after the form tag, so that when users press enter in a textfield, this
     * submit component's action will be selected. If no default component is set (so unset by
     * calling this method with null), nothing additional is rendered.
     * <p>
     * WARNING: note that this is a best effort only. Unfortunately having a 'default' button in a
     * form is ill defined in the standards, and of course IE has it's own way of doing things.
     * </p>
     * There can be only one default button per form hierarchy. So if you set default button on a
     * nested form, it will actually delegate the call to root form. </b>
     * 
     * @param submittingComponent
     *            The component to set as the default submitting component, or null when you want to
     *            'unset' any previously set default component
     */
    public final void setDefaultButton(IFormSubmittingComponent submittingComponent) {
        if (isRootForm()) {
            defaultSubmittingComponent = submittingComponent;
        } else {
            getRootForm().setDefaultButton(submittingComponent);
        }
    }

    /**
     * Sets the maximum size for uploads. If null, the setting
     * {@link org.apache.wicket.settings.ApplicationSettings#getDefaultMaximumUploadSize()} is used.
     * 
     * @param maxSize
     *            The maximum size
     */
    public void setMaxSize(final Bytes maxSize) {
        this.maxSize = maxSize;
    }

    /**
     * Sets maximum size of each file in upload request.
     * 
     * @param fileMaxSize
     */
    public void setFileMaxSize(Bytes fileMaxSize) {
        this.fileMaxSize = fileMaxSize;
    }

    /**
     * Set to true to use enctype='multipart/form-data', and to process file uploads by default
     * multiPart = false
     * 
     * @param multiPart
     *            whether this form should behave as a multipart form
     */
    public void setMultiPart(boolean multiPart) {
        if (multiPart) {
            this.multiPart |= MULTIPART_HARD;
        } else {
            this.multiPart &= ~MULTIPART_HARD;
        }
    }

    /**
     * @see org.apache.wicket.Component#setVersioned(boolean)
     */
    @Override
    public final Component setVersioned(final boolean isVersioned) {
        super.setVersioned(isVersioned);

        // Search for FormComponents like TextField etc.
        visitFormComponents(new IVisitor<FormComponent<?>, Void>() {
            @Override
            public void component(final FormComponent<?> formComponent, IVisit<Void> visit) {
                formComponent.setVersioned(isVersioned);
            }
        });
        return this;
    }

    /**
     * Convenient and typesafe way to visit all the form components on a form.
     * 
     * @param <R>
     *            return object type
     * @param visitor
     *            The visitor interface to call
     * @return user provided in callback
     */
    public final <R> R visitFormComponents(final IVisitor<FormComponent<?>, R> visitor) {
        return visitChildren(FormComponent.class, visitor);
    }

    /**
     * Convenient and typesafe way to visit all the form components on a form postorder (deepest
     * first)
     * 
     * @param <R>
     *            Return object type
     * @param visitor
     *            The visitor interface to call
     * @return whatever you provided
     */
    public final <R> R visitFormComponentsPostOrder(final IVisitor<? extends FormComponent<?>, R> visitor) {
        return FormComponent.visitFormComponentsPostOrder(this, visitor);
    }

    /**
     * Find out whether there is any registered error for a form component.
     * 
     * @return whether there is any registered error for a form component
     */
    private boolean anyFormComponentError() {
        // Check ALL children for error messages irrespective of FormComponents or not
        Boolean error = visitChildren(Component.class, new IVisitor<Component, Boolean>() {
            @Override
            public void component(final Component component, final IVisit<Boolean> visit) {
                if (component.hasErrorMessage() && component.isVisibleInHierarchy()
                        && component.isEnabledInHierarchy()) {
                    visit.stop(true);
                }
            }
        });

        return (error != null) && error;
    }

    /**
     * Visits the form's children FormComponents and inform them that a new user input is available
     * in the Request
     */
    private void inputChanged() {
        visitFormComponentsPostOrder(new IVisitor<FormComponent<?>, Void>() {
            @Override
            public void component(final FormComponent<?> formComponent, IVisit<Void> visit) {
                formComponent.inputChanged();
            }
        });
    }

    /**
     * If a default IFormSubmittingComponent was set on this form, this method will be called to
     * render an extra field with an invisible style so that pressing enter in one of the textfields
     * will do a form submit using this component. This method is overridable as what we do is best
     * effort only, and may not what you want in specific situations. So if you have specific
     * usability concerns, or want to follow another strategy, you may override this method.
     */
    protected void appendDefaultButtonField() {
        AppendingStringBuffer buffer = new AppendingStringBuffer();

        String cssClass = getString(CssUtils.key(Form.class, "hidden-fields"));

        // div that is not visible (but not display:none either)
        buffer.append(String.format(
                "<div style=\"width:0px;height:0px;position:absolute;left:-100px;top:-100px;overflow:hidden\" class=\"%s\">",
                cssClass));

        // add an empty textfield (otherwise IE doesn't work)
        buffer.append("<input type=\"text\" tabindex=\"-1\" autocomplete=\"off\"/>");

        // add the submitting component
        final Component submittingComponent = (Component) defaultSubmittingComponent;
        buffer.append("<input type=\"submit\" tabindex=\"-1\" name=\"");
        buffer.append(defaultSubmittingComponent.getInputName());
        buffer.append("\" onclick=\" var b=document.getElementById('");
        buffer.append(submittingComponent.getMarkupId());
        buffer.append(
                "'); if (b!=null&amp;&amp;b.onclick!=null&amp;&amp;typeof(b.onclick) != 'undefined') {  var r = Wicket.bind(b.onclick, b)(); if (r != false) b.click(); } else { b.click(); };  return false;\" ");
        buffer.append(" />");

        // close div
        buffer.append("</div>");

        getResponse().write(buffer);
    }

    /**
     * Template method to allow clients to do any processing (like recording the current model so
     * that, in case onSubmit does further validation, the model can be rolled back) before the
     * actual updating of form component models is done.
     */
    protected void beforeUpdateFormComponentModels() {
    }

    /**
     * Called (by the default implementation of 'process') when all fields validated, the form was
     * updated and it's data was allowed to be persisted. It is meant for delegating further
     * processing to clients.
     * <p>
     * This implementation first finds out whether the form processing was triggered by a nested
     * IFormSubmittingComponent of this form. If that is the case, that component's
     * onSubmitBefore/AfterForm methods are called appropriately..
     * </p>
     * <p>
     * Regardless of whether a submitting component was found, the form's onSubmit method is called
     * next.
     * </p>
     * 
     * @param submittingComponent
     *            the component that triggered this form processing, or null if the processing was
     *            triggered by something else (like a non-Wicket submit button or a javascript
     *            execution)
     */
    protected void delegateSubmit(IFormSubmitter submittingComponent) {
        final Form<?> processingForm = findFormToProcess(submittingComponent);

        // collect all forms innermost to outermost before any hierarchy is changed
        final List<Form<?>> forms = Generics.newArrayList(3);
        Visits.visitPostOrder(processingForm, new IVisitor<Form<?>, Void>() {
            @Override
            public void component(Form<?> form, IVisit<Void> visit) {
                if (form.isSubmitted()) {
                    forms.add(form);
                }
            }
        }, new ClassVisitFilter(Form.class));

        // process submitting component (if specified)
        if (submittingComponent != null) {
            // invoke submit on component
            submittingComponent.onSubmit();
        }

        // invoke Form#onSubmit(..)
        for (Form<?> form : forms) {
            form.onSubmit();
        }

        if (submittingComponent != null) {
            submittingComponent.onAfterSubmit();
        }
    }

    /**
     * Returns the id which will be used for the hidden div containing all parameter fields.
     * 
     * @return the id of the hidden div
     */
    private final String getHiddenFieldsId() {
        return getInputNamePrefix() + getMarkupId() + "_hf_0";
    }

    /**
     * Gets the HTTP submit method that will appear in form markup. If no method is specified in the
     * template, "post" is the default. Note that the markup-declared HTTP method may not correspond
     * to the one actually used to submit the form; in an Ajax submit, for example, JavaScript event
     * handlers may submit the form with a "get" even when the form method is declared as "post."
     * Therefore this method should not be considered a guarantee of the HTTP method used, but a
     * value for the markup only. Override if you have a requirement to alter this behavior.
     * 
     * @return the submit method specified in markup.
     */
    protected String getMethod() {
        String method = getMarkupAttributes().getString("method");
        return (method != null) ? method : METHOD_POST;
    }

    /**
     * 
     * @see org.apache.wicket.Component#getStatelessHint()
     */
    @Override
    protected boolean getStatelessHint() {
        return false;
    }

    /**
     * @return True if is multipart
     */
    public boolean isMultiPart() {
        if (multiPart == 0) {
            Boolean anyEmbeddedMultipart = visitChildren(Component.class, new IVisitor<Component, Boolean>() {
                @Override
                public void component(final Component component, final IVisit<Boolean> visit) {
                    boolean isMultiPart = false;
                    if (component instanceof Form<?>) {
                        Form<?> form = (Form<?>) component;
                        if (form.isVisibleInHierarchy() && form.isEnabledInHierarchy()) {
                            isMultiPart = (form.multiPart & MULTIPART_HARD) != 0;
                        }
                    } else if (component instanceof FormComponent<?>) {
                        FormComponent<?> fc = (FormComponent<?>) component;
                        if (fc.isVisibleInHierarchy() && fc.isEnabledInHierarchy()) {
                            isMultiPart = fc.isMultiPart();
                        }
                    }

                    if (isMultiPart) {
                        visit.stop(true);
                    }
                }

            });

            if (Boolean.TRUE.equals(anyEmbeddedMultipart)) {
                multiPart |= MULTIPART_HINT_YES;
            } else {
                multiPart |= MULTIPART_HINT_NO;
            }
        }

        return (multiPart & (MULTIPART_HARD | MULTIPART_HINT_YES)) != 0;
    }

    /**
     * Handles multi-part processing of the submitted data. <h3>
     * WARNING</h3> If this method is overridden it can break {@link FileUploadField}s on this form
     * 
     * @return false if form is multipart and upload failed
     */
    protected boolean handleMultiPart() {
        if (isMultiPart()) {
            // Change the request to a multipart web request so parameters are
            // parsed out correctly
            try {
                ServletWebRequest request = (ServletWebRequest) getRequest();
                final MultipartServletWebRequest multipartWebRequest = request.newMultipartWebRequest(getMaxSize(),
                        getPage().getId());
                multipartWebRequest.setFileMaxSize(getFileMaxSize());
                multipartWebRequest.parseFileParts();

                // TODO: Can't this be detected from header?
                getRequestCycle().setRequest(multipartWebRequest);
            } catch (final FileUploadException fux) {
                // Create model with exception and maximum size values
                final Map<String, Object> model = new HashMap<>();
                model.put("exception", fux);
                model.put("maxSize", getMaxSize());
                model.put("fileMaxSize", getFileMaxSize());

                onFileUploadException(fux, model);

                // don't process the form if there is a FileUploadException
                return false;
            }
        }
        return true;
    }

    /**
     * The default message may look like ".. may not exceed 10240 Bytes..". Which is ok, but
     * sometimes you may want something like "10KB". By subclassing this method you may replace
     * maxSize in the model or add you own property and use that in your error message.
     * <p>
     * Don't forget to call super.onFileUploadException(e, model) at the end of your method.
     * 
     * @param e
     * @param model
     */
    protected void onFileUploadException(final FileUploadException e, final Map<String, Object> model) {
        if (e instanceof FileUploadBase.SizeLimitExceededException) {
            String msg = getString(UPLOAD_TOO_LARGE_RESOURCE_KEY, Model.ofMap(model));
            error(msg);
        } else if (e instanceof FileUploadBase.FileSizeLimitExceededException) {
            String msg = getString(UPLOAD_SINGLE_FILE_TOO_LARGE_RESOURCE_KEY, Model.ofMap(model));
            error(msg);
        } else {
            String msg = getString(UPLOAD_FAILED_RESOURCE_KEY, Model.ofMap(model));
            error(msg);

            log.warn(msg, e);
        }
    }

    /**
     * @see org.apache.wicket.Component#internalOnModelChanged()
     */
    @Override
    protected void internalOnModelChanged() {
        // Visit all the form components and validate each
        visitFormComponentsPostOrder(new IVisitor<FormComponent<?>, Void>() {
            @Override
            public void component(final FormComponent<?> formComponent, IVisit<Void> visit) {
                // If form component is using form model
                if (formComponent.sameInnermostModel(Form.this)) {
                    formComponent.modelChanged();
                }
            }
        });
    }

    /**
     * Mark each form component on this form invalid.
     */
    protected final void markFormComponentsInvalid() {
        // call invalidate methods of all nested form components
        visitFormComponentsPostOrder(new IVisitor<FormComponent<?>, Void>() {
            @Override
            public void component(final FormComponent<?> formComponent, IVisit<Void> visit) {
                if (formComponent.isVisibleInHierarchy()) {
                    formComponent.invalid();
                }
            }
        });
    }

    /**
     * Mark each form component on this form and on nested forms valid.
     */
    protected final void markFormComponentsValid() {
        internalMarkFormComponentsValid();
        markNestedFormComponentsValid();
    }

    /**
     * Mark each form component on nested form valid.
     */
    private void markNestedFormComponentsValid() {
        visitChildren(Form.class, new IVisitor<Form<?>, Void>() {
            @Override
            public void component(final Form<?> form, final IVisit<Void> visit) {
                if (form.isSubmitted()) {
                    form.internalMarkFormComponentsValid();
                } else {
                    visit.dontGoDeeper();
                }
            }
        });
    }

    /**
     * Mark each form component on this form valid.
     */
    private void internalMarkFormComponentsValid() {
        // call valid methods of all nested form components
        visitFormComponentsPostOrder(new IVisitor<FormComponent<?>, Void>() {
            @Override
            public void component(final FormComponent<?> formComponent, IVisit<Void> visit) {
                if (formComponent.getForm() == Form.this && formComponent.isVisibleInHierarchy()) {
                    formComponent.valid();
                }
            }
        });
    }

    /**
     * @see org.apache.wicket.Component#onComponentTag(ComponentTag)
     */
    @Override
    protected void onComponentTag(final ComponentTag tag) {
        super.onComponentTag(tag);

        if (isRootForm()) {
            checkComponentTag(tag, "form");

            String method = getMethod().toLowerCase(Locale.ROOT);
            tag.put("method", method);
            String url = getActionUrl().toString();
            if (encodeUrlInHiddenFields()) {
                int i = url.indexOf('?');
                String action = (i > -1) ? url.substring(0, i) : "";
                tag.put("action", action);
                // alternatively, we could just put an empty string here, so
                // that mounted paths stay in good order. I decided against this
                // as I'm not sure whether that could have side effects with
                // other encoders
            } else {
                tag.put("action", url);
            }

            if (isMultiPart()) {
                if (METHOD_GET.equalsIgnoreCase(method)) {
                    log.warn("Form with id '{}' is multipart. It should use method 'POST'!", getId());
                    tag.put("method", METHOD_POST.toLowerCase(Locale.ROOT));
                }

                tag.put("enctype", ENCTYPE_MULTIPART_FORM_DATA);
                //
                // require the application-encoding for multipart/form-data to be sure to
                // get multipart-uploaded characters with the proper encoding on the following
                // request.
                //
                // for details see: http://stackoverflow.com/questions/546365
                //
                tag.put("accept-charset", getApplication().getRequestCycleSettings().getResponseRequestEncoding());
            } else {
                // sanity check
                String enctype = (String) tag.getAttributes().get("enctype");
                if (ENCTYPE_MULTIPART_FORM_DATA.equalsIgnoreCase(enctype)) {
                    // though not set explicitly in Java, this is a multipart
                    // form
                    setMultiPart(true);
                }
            }
        } else {
            // WICKET-6658 form is not allowed, anything else can stay as is
            if ("form".equalsIgnoreCase(tag.getName())) {
                tag.setName("div");
            }
            tag.remove("method");
            tag.remove("action");
            tag.remove("enctype");
        }
    }

    /**
     * Generates the action url for the form
     * 
     * @return action url
     */
    protected CharSequence getActionUrl() {
        return urlForListener(new PageParameters());
    }

    /**
     * @see org.apache.wicket.Component#renderPlaceholderTag(org.apache.wicket.markup.ComponentTag,
     *      org.apache.wicket.request.Response)
     */
    @Override
    protected void renderPlaceholderTag(ComponentTag tag, Response response) {
        if (isRootForm()) {
            super.renderPlaceholderTag(tag, response);
        } else {
            // rewrite inner form tag as div
            response.write("<div style=\"display:none\"");
            if (getOutputMarkupId()) {
                response.write(" id=\"");
                response.write(getMarkupId());
                response.write("\"");
            }
            response.write("></div>");
        }
    }

    /**
     * Should URL query parameters be encoded in hidden fields.
     *
     * @return true if form's method is 'get'
     */
    protected boolean encodeUrlInHiddenFields() {
        return METHOD_GET.equalsIgnoreCase(getMethod());
    }

    /**
     * Append an additional hidden input tag to support anchor tags that can submit a form.
     * 
     * @param markupStream
     *            The markup stream
     * @param openTag
     *            The open tag for the body
     */
    @Override
    public void onComponentTagBody(final MarkupStream markupStream, final ComponentTag openTag) {
        if (isRootForm()) {
            // get the hidden field id
            writeHiddenFields();
        }

        // do the rest of the processing
        super.onComponentTagBody(markupStream, openTag);
    }

    /**
     * Writes the markup for the hidden input fields and default button field if applicable to the
     * current response.
     */
    public final void writeHiddenFields() {
        // if it's a get, did put the parameters in the action attribute,
        // and have to write the url parameters as hidden fields
        if (encodeUrlInHiddenFields()) {
            String cssClass = getString(CssUtils.key(Form.class, "hidden-fields"));

            getResponse().write(String.format(
                    "<div id=\"%s\" style=\"width:0px;height:0px;position:absolute;left:-100px;top:-100px;overflow:hidden\" class=\"%s\">",
                    getHiddenFieldsId(), cssClass));

            AppendingStringBuffer buffer = new AppendingStringBuffer();

            String url = getActionUrl().toString();
            int i = url.indexOf('?');
            String queryString = (i > -1) ? url.substring(i + 1) : url;
            String[] params = Strings.split(queryString, '&');

            writeParamsAsHiddenFields(params, buffer);

            getResponse().write(buffer);

            getResponse().write("</div>");
        }

        // if a default submitting component was set, handle the rendering of that
        if (defaultSubmittingComponent instanceof Component) {
            final Component submittingComponent = (Component) defaultSubmittingComponent;
            if (submittingComponent.isVisibleInHierarchy() && submittingComponent.isEnabledInHierarchy()) {
                appendDefaultButtonField();
            }
        }
    }

    /**
     * 
     * @param params
     * @param buffer
     */
    protected void writeParamsAsHiddenFields(String[] params, AppendingStringBuffer buffer) {
        for (String param : params) {
            String[] pair = Strings.split(param, '=');

            buffer.append("<input type=\"hidden\" name=\"").append(recode(pair[0])).append("\" value=\"")
                    .append(pair.length > 1 ? recode(pair[1]) : "").append("\" />");
        }
    }

    /**
     * Take URL-encoded query string value, decode it and return HTML-escaped version
     * 
     * @param s
     *            value to decode
     * @return URL decoded and HTML escaped value
     */
    private String recode(String s) {
        String un = UrlDecoder.QUERY_INSTANCE.decode(s, getRequest().getCharset());
        return Strings.escapeMarkup(un).toString();
    }

    /**
     * @see org.apache.wicket.Component#onDetach()
     */
    @Override
    protected void onDetach() {
        setFlag(FLAG_SUBMITTED, false);

        super.onDetach();
    }

    /**
     * Method to override if you want to do something special when an error occurs (other than
     * simply displaying validation errors).
     */
    protected void onError() {
    }

    @Override
    public void onEvent(IEvent<?> event) {
        if (event.getPayload() instanceof AjaxRequestTarget) {
            // WICKET-6171 clear multipart hint, it might change during Ajax requests without this form being rendered
            this.multiPart &= MULTIPART_HARD;
        }
    }

    @Override
    protected void onBeforeRender() {
        // clear multipart hint, it will be reevaluated by #isMultiPart()
        this.multiPart &= MULTIPART_HARD;

        super.onBeforeRender();
    }

    /**
     * Implemented by subclasses to deal with form submits.
     */
    protected void onSubmit() {
    }

    /**
     * Update the model of all components on this form and nested forms using the fields that were
     * sent with the current request. This method only updates models when the Form.validate() is
     * called first that takes care of the conversion for the FormComponents.
     * 
     * Normally this method will not be called when a validation error occurs in one of the form
     * components.
     * 
     * @see org.apache.wicket.markup.html.form.FormComponent#updateModel()
     */
    protected final void updateFormComponentModels() {
        internalUpdateFormComponentModels();
        updateNestedFormComponentModels();
    }

    /**
     * Update the model of all components on nested forms.
     * 
     * @see #updateFormComponentModels()
     */
    private void updateNestedFormComponentModels() {
        visitChildren(Form.class, new IVisitor<Form<?>, Void>() {
            @Override
            public void component(final Form<?> form, final IVisit<Void> visit) {
                if (form.isSubmitted()) {
                    form.internalUpdateFormComponentModels();
                } else {
                    visit.dontGoDeeper();
                }
            }
        });
    }

    /**
     * Update the model of all components on this form.
     * 
     * @see #updateFormComponentModels()
     */
    private void internalUpdateFormComponentModels() {
        FormComponent.visitComponentsPostOrder(this, new FormModelUpdateVisitor(this));
    }

    /**
     * Validates the form by checking required fields, converting raw input and running validators
     * for every form component, and last running global form validators. This method is typically
     * called before updating any models.
     * <p>
     * NOTE: in most cases, custom validations on the form can be achieved using an IFormValidator
     * that can be added using addValidator().
     * </p>
     */
    protected final void validate() {
        // since this method can be called directly by users, this additional check is needed
        if (isEnabledInHierarchy() && isVisibleInHierarchy()) {
            validateNestedForms();
            validateComponents();
            validateFormValidators();
            onValidate();
        }
    }

    /**
     * Callback during the validation stage of the form
     */
    protected void onValidate() {

    }

    /**
     * Calls {@linkplain #onValidateModelObjects()} on this form and all nested forms that are
     * visible and enabled
     */
    private void internalOnValidateModelObjects() {
        onValidateModelObjects();
        visitChildren(Form.class, new IVisitor<Form<?>, Void>() {
            @Override
            public void component(Form<?> form, IVisit<Void> visit) {
                if (form.isSubmitted()) {
                    form.onValidateModelObjects();
                } else {
                    visit.dontGoDeeper();
                }
            }
        });
    }

    /**
     * Called after form components have updated their models. This is a late-stage validation that
     * allows outside frameworks to validate any beans that the form is updating.
     * 
     * This validation method is not preferred because at this point any errors will not unroll any
     * changes to the model object, so the model object is in a modified state potentially
     * containing illegal values. However, with external frameworks there may not be an alternate
     * way to validate the model object. A good example of this is a JSR303 Bean Validator
     * validating the model object to check any class-level constraints, in order to check such
     * constraints the model object must contain the values set by the user.
     */
    protected void onValidateModelObjects() {
    }

    /**
     * Triggers type conversion on form components
     */
    protected final void validateComponents() {
        visitFormComponentsPostOrder(new ValidationVisitor() {
            @Override
            public void validate(final FormComponent<?> formComponent) {
                final Form<?> form = formComponent.getForm();
                if (form == Form.this && form.isEnabledInHierarchy() && form.isVisibleInHierarchy()) {
                    formComponent.validate();
                }
            }
        });
    }

    /**
     * Checks if the specified form component visible and is attached to a page
     * 
     * @param fc
     *            form component
     * 
     * @return true if the form component and all its parents are visible and there component is in
     *         page's hierarchy
     */
    private boolean isFormComponentVisibleInPage(FormComponent<?> fc) {
        if (fc == null) {
            throw new IllegalArgumentException("Argument `fc` cannot be null");
        }
        return fc.isVisibleInHierarchy();
    }

    /**
     * Validates form with the given form validator
     * 
     * @param validator
     */
    protected final void validateFormValidator(final IFormValidator validator) {
        Args.notNull(validator, "validator");

        final FormComponent<?>[] dependents = validator.getDependentFormComponents();

        boolean validate = true;

        if (dependents != null) {
            for (final FormComponent<?> dependent : dependents) {
                // check if the dependent component is valid
                if (!dependent.isValid()) {
                    validate = false;
                    break;
                }
                // check if the dependent component is visible and is attached to
                // the page
                else if (!isFormComponentVisibleInPage(dependent)) {
                    if (log.isWarnEnabled()) {
                        log.warn("IFormValidator in form `" + getPageRelativePath()
                                + "` depends on a component that has been removed from the page or is no longer visible. "
                                + "Offending component id `" + dependent.getId() + "`.");
                    }
                    validate = false;
                    break;
                }
            }
        }

        if (validate) {
            validator.validate(this);
        }
    }

    /**
     * Triggers any added {@link IFormValidator}s.
     */
    protected final void validateFormValidators() {
        for (Behavior behavior : getBehaviors()) {
            if (behavior instanceof IFormValidator) {
                validateFormValidator((IFormValidator) behavior);
            }
        }
    }

    /**
     * Validates {@link FormComponent}s as well as {@link IFormValidator}s in nested {@link Form}s.
     * 
     * @see #validate()
     */
    private void validateNestedForms() {
        Visits.visitPostOrder(this, new IVisitor<Form<?>, Void>() {
            @Override
            public void component(final Form<?> form, final IVisit<Void> visit) {
                if (form == Form.this) {
                    // skip self, only process children
                    visit.stop();
                    return;
                }

                if (form.isSubmitted()) {
                    form.validateComponents();
                    form.validateFormValidators();
                    form.onValidate();
                }
            }
        }, new ClassVisitFilter(Form.class));
    }

    /**
     * Allows to customize input names of form components inside this form.
     * 
     * @return String that well be used as prefix to form component input names
     */
    protected String getInputNamePrefix() {
        return "";
    }

    /**
     * @param component
     * @return The parent form for component
     */
    public static Form<?> findForm(Component component) {
        return component.findParent(Form.class);
    }

    /**
     * Utility method to assemble an id to distinct form components from different nesting levels.
     * Useful to generate input names attributes.
     * 
     * @param component
     * @return form relative identification string
     */
    public static String getRootFormRelativeId(Component component) {
        String id = component.getId();
        final PrependingStringBuffer inputName = new PrependingStringBuffer(id.length());
        Component c = component;
        while (true) {
            inputName.prepend(id);
            c = c.getParent();
            if (c == null || (c instanceof Form<?> && ((Form<?>) c).isRootForm()) || c instanceof Page) {
                break;
            }
            inputName.prepend(Component.PATH_SEPARATOR);
            id = c.getId();
        }

        /*
         * Certain input names causes problems with JavaScript. If the input name would cause a
         * problem, we create a replacement unique name by prefixing the name with a path that would
         * otherwise never be used (blank id in path).
         * 
         * Input names must start with [A-Za-z] according to HTML 4.01 spec. HTML 5 allows almost
         * anything.
         */
        if (JavaScriptReservedNames.isNameReserved(inputName.toString())) {
            inputName.prepend(Component.PATH_SEPARATOR);
            inputName.prepend(Component.PATH_SEPARATOR);
            inputName.prepend("p");
        }
        return inputName.toString();
    }

    /**
     * Response when a submission method mismatch is detected
     * 
     * @see Form#getMethod()
     * 
     * @author igor
     */
    public static enum MethodMismatchResponse {
        /**
         * Continue processing.
         */
        CONTINUE,

        /**
         * Abort processing.
         */
        ABORT
    }
}