001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.wicket.markup.html.form;
018
019import java.util.ArrayList;
020import java.util.Collection;
021import java.util.Collections;
022import java.util.HashMap;
023import java.util.List;
024import java.util.Locale;
025import java.util.Map;
026
027import javax.servlet.http.HttpServletRequest;
028
029import org.apache.commons.fileupload.FileCountLimitExceededException;
030import org.apache.commons.fileupload.FileUploadBase;
031import org.apache.commons.fileupload.FileUploadException;
032import org.apache.wicket.Component;
033import org.apache.wicket.IGenericComponent;
034import org.apache.wicket.IRequestListener;
035import org.apache.wicket.Page;
036import org.apache.wicket.WicketRuntimeException;
037import org.apache.wicket.ajax.AjaxRequestTarget;
038import org.apache.wicket.behavior.Behavior;
039import org.apache.wicket.core.request.handler.IPartialPageRequestHandler;
040import org.apache.wicket.core.util.string.CssUtils;
041import org.apache.wicket.event.IEvent;
042import org.apache.wicket.markup.ComponentTag;
043import org.apache.wicket.markup.MarkupStream;
044import org.apache.wicket.markup.head.IHeaderResponse;
045import org.apache.wicket.markup.head.OnEventHeaderItem;
046import org.apache.wicket.markup.html.WebMarkupContainer;
047import org.apache.wicket.markup.html.form.upload.FileUploadField;
048import org.apache.wicket.markup.html.form.validation.FormValidatorAdapter;
049import org.apache.wicket.markup.html.form.validation.IFormValidator;
050import org.apache.wicket.model.IModel;
051import org.apache.wicket.model.Model;
052import org.apache.wicket.protocol.http.servlet.MultipartServletWebRequest;
053import org.apache.wicket.protocol.http.servlet.ServletWebRequest;
054import org.apache.wicket.request.IRequestParameters;
055import org.apache.wicket.request.Request;
056import org.apache.wicket.request.Response;
057import org.apache.wicket.request.mapper.parameter.PageParameters;
058import org.apache.wicket.request.parameter.EmptyRequestParameters;
059import org.apache.wicket.util.encoding.UrlDecoder;
060import org.apache.wicket.util.lang.Args;
061import org.apache.wicket.util.lang.Bytes;
062import org.apache.wicket.util.lang.Generics;
063import org.apache.wicket.util.string.AppendingStringBuffer;
064import org.apache.wicket.util.string.PrependingStringBuffer;
065import org.apache.wicket.util.string.Strings;
066import org.apache.wicket.util.string.interpolator.MapVariableInterpolator;
067import org.apache.wicket.util.value.LongValue;
068import org.apache.wicket.util.visit.ClassVisitFilter;
069import org.apache.wicket.util.visit.IVisit;
070import org.apache.wicket.util.visit.IVisitor;
071import org.apache.wicket.util.visit.Visits;
072import org.slf4j.Logger;
073import org.slf4j.LoggerFactory;
074
075
076/**
077 * Container for {@link FormComponent}s (such as {@link CheckBox}es, {@link ListChoice}s or
078 * {@link TextField}s). Subclass this class to receive submit notifications through
079 * {@link #onSubmit()} or nest multiple {@link IFormSubmittingComponent}s if you want to vary submit
080 * behavior. In the former case it is not necessary to use any of Wicket's classes (such as
081 * {@link Button} or {@link SubmitLink}), just putting e.g. <input type="submit" value="go"/>
082 * suffices.
083 * <p>
084 * As a {@link IRequestListener} the form gets notified of listener requests in
085 * {@link #onRequest()}. By default, the processing of this submit works like this:
086 * <ul>
087 * <li>All nested {@link FormComponent}s are notified of new input via
088 * {@link FormComponent#inputChanged()}</li>
089 * <li>The form submitter is looked up, e.g. a {@link Button} is contained in the component
090 * hierarchy of this form and was clicked by the user:
091 * <ul>
092 * <li>If an {@link IFormSubmitter} was found which
093 * {@link IFormSubmitter#getDefaultFormProcessing()} returns {@code false} (default is {@code true}
094 * ), it's {@link IFormSubmitter#onSubmit()} method will be called right away, thus all further
095 * processing is skipped. This has the same effect as nesting a normal link in the form. <br>
096 * If needed the form submitter can continue processing however, by calling {@link #validate()} to
097 * execute form validation, {@link #hasError()} to find out whether validate() resulted in
098 * validation errors, and {@link #updateFormComponentModels()} to update the models of nested form
099 * components.</li>
100 * <li>Otherwise this form is further processed via {@link #process(IFormSubmitter)}, resulting in
101 * all nested components being validated via {@link FormComponent#validate()}. <br>
102 * <ul>
103 * <li>If form validation failed, all nested form components will be marked invalid, and
104 * {@link #onError()} is called to allow clients to provide custom error handling code.</li>
105 * <li>Otherwise the nested components will be asked to update their models via
106 * {@link FormComponent#updateModel()}. After that submit notification is delegated to the
107 * {@link IFormSubmitter#onSubmit()} (if just found) before calling {@link #onSubmit()} on this
108 * form. Subclasses may override {@link #delegateSubmit(IFormSubmitter)} if they want a different
109 * behavior.</li>
110 * </ul>
111 * </li>
112 * </ul>
113 * </li>
114 * </ul>
115 *
116 * A Form can be configured for handling uploads with multipart requests (e.g. files) by calling
117 * {@link #setMultiPart(boolean)} (although Wicket will try to automatically detect this for you).
118 * Use this with {@link FileUploadField} components. You can attach multiple {@link FileUploadField}
119 * components for multiple file uploads.
120 * <p>
121 * In case of an upload error two resource keys are available to specify error messages:
122 * {@code uploadTooLarge} and {@code uploadFailed}, i.e. for a form with id {@code myform} in
123 * {@code MyPage.properties}:
124 *
125 * <pre>
126 * myform.uploadTooLarge=You have uploaded a file that is over the allowed limit of 2Mb
127 * </pre>
128 *
129 * Forms can be nested. You can put a form in another form. Since HTML doesn't allow nested
130 * &lt;form&gt; tags, the inner forms will be rendered using the &lt;div&gt; tag. You have to submit
131 * the inner forms using explicit components (like {@link Button} or {@link SubmitLink}), you can't
132 * rely on implicit submit behavior (by using just &lt;input type="submit"&gt; that is not attached
133 * to a component).
134 * <p>
135 * When a nested form is submitted, the user entered values in outer (parent) forms are preserved
136 * and only the fields in the submitted form are validated. </b>
137 *
138 * @author Jonathan Locke
139 * @author Juergen Donnerstag
140 * @author Eelco Hillenius
141 * @author Cameron Braid
142 * @author Johan Compagner
143 * @author Igor Vaynberg (ivaynberg)
144 * @author David Leangen
145 *
146 * @param <T>
147 *            The model object type
148 */
149public class Form<T> extends WebMarkupContainer
150        implements
151                IRequestListener,
152                IGenericComponent<T, Form<T>>
153{
154        public static final String ENCTYPE_MULTIPART_FORM_DATA = "multipart/form-data";
155
156        public static final String HIDDEN_FIELDS_CSS_CLASS_KEY = CssUtils
157                .key(Form.class, "hidden-fields");
158
159        /**
160         * Visitor used for validation
161         *
162         * @author Igor Vaynberg (ivaynberg)
163         */
164        public abstract static class ValidationVisitor implements IVisitor<FormComponent<?>, Void>
165        {
166                @Override
167                public void component(final FormComponent<?> formComponent, final IVisit<Void> visit)
168                {
169
170                        Form<?> form = formComponent.getForm();
171                        if (!form.isVisibleInHierarchy() || !form.isEnabledInHierarchy())
172                        {
173                                // do not validate formComponent or any of formComponent's children
174                                visit.dontGoDeeper();
175                                return;
176                        }
177
178                        if (formComponent.isVisibleInHierarchy() && formComponent.isEnabledInHierarchy())
179                        {
180                                validate(formComponent);
181                        }
182                        if (formComponent.processChildren() == false)
183                        {
184                                visit.dontGoDeeper();
185                        }
186                }
187
188                /**
189                 * Callback that should be used to validate form component
190                 *
191                 * @param formComponent
192                 */
193                public abstract void validate(FormComponent<?> formComponent);
194        }
195
196
197        /**
198         * Visitor used to update component models
199         *
200         * @author Igor Vaynberg (ivaynberg)
201         */
202        private static class FormModelUpdateVisitor implements IVisitor<Component, Void>
203        {
204                private final Form<?> formFilter;
205
206                /**
207                 * Constructor
208                 *
209                 * @param formFilter
210                 */
211                public FormModelUpdateVisitor(Form<?> formFilter)
212                {
213                        this.formFilter = formFilter;
214                }
215
216                /** {@inheritDoc} */
217                @Override
218                public void component(final Component component, final IVisit<Void> visit)
219                {
220                        if (component instanceof IFormModelUpdateListener)
221                        {
222                                final Form<?> form = Form.findForm(component);
223                                if (form != null)
224                                {
225                                        if (this.formFilter == null || this.formFilter == form)
226                                        {
227                                                if (form.isEnabledInHierarchy())
228                                                {
229                                                        if (component.isVisibleInHierarchy() &&
230                                                                component.isEnabledInHierarchy())
231                                                        {
232                                                                ((IFormModelUpdateListener)component).updateModel();
233                                                        }
234                                                }
235                                        }
236                                }
237                        }
238                }
239        }
240
241        /**
242         * Constant for specifying how a form is submitted, in this case using get.
243         */
244        public static final String METHOD_GET = "get";
245
246        /**
247         * Constant for specifying how a form is submitted, in this case using post.
248         */
249        public static final String METHOD_POST = "post";
250
251        /** Flag that indicates this form has been submitted during this request */
252        private static final short FLAG_SUBMITTED = FLAG_RESERVED1;
253
254        /** Log. */
255        private static final Logger log = LoggerFactory.getLogger(Form.class);
256
257        private static final long serialVersionUID = 1L;
258
259        public static final String UPLOAD_FAILED_RESOURCE_KEY = "uploadFailed";
260
261        public static final String UPLOAD_TOO_LARGE_RESOURCE_KEY = "uploadTooLarge";
262        public static final String UPLOAD_SINGLE_FILE_TOO_LARGE_RESOURCE_KEY = "uploadSingleFileTooLarge";
263        public static final String UPLOAD_TOO_MANY_FILES_RESOURCE_KEY = "uploadTooManyFiles";
264
265        /**
266         * Any default IFormSubmittingComponent. If set, a hidden submit component will be rendered
267         * right after the form tag, so that when users press enter in a textfield, this submit
268         * component's action will be selected. If no default IFormSubmittingComponent is set, nothing
269         * additional is rendered.
270         * <p>
271         * WARNING: note that this is a best effort only. Unfortunately having a 'default'
272         * IFormSubmittingComponent in a form is ill defined in the standards, and of course IE has it's
273         * own way of doing things.
274         * </p>
275         */
276        private IFormSubmittingComponent defaultSubmittingComponent;
277
278        /**
279         * Maximum size of an upload in bytes. If null, the setting
280         * {@link org.apache.wicket.settings.ApplicationSettings#getDefaultMaximumUploadSize()} is used.
281         */
282        private Bytes maxSize = null;
283
284        /**
285         * Maximum size of file of upload in bytes (if there are more than one) in request.
286         */
287        private Bytes fileMaxSize;
288
289        /**
290         * Maximum amount of files in request.
291         * A value of -1 indicates no maximum.
292         */
293        private long fileCountMax = -1L;
294
295        /** True if the form has enctype of multipart/form-data */
296        private short multiPart = 0;
297
298        /**
299         * A user has explicitly called {@link #setMultiPart(boolean)} with value {@code true} forcing
300         * it to be true
301         */
302        private static final short MULTIPART_HARD = 0x01;
303
304        /**
305         * The form has discovered a multipart component before rendering and is marking itself as
306         * multipart until next render
307         */
308        private static final short MULTIPART_HINT_YES = 0x02;
309
310        /**
311         * The form has discovered no multipart component before rendering and is marking itself as
312         * not multipart until next render
313         */
314        private static final short MULTIPART_HINT_NO = 0x04;
315
316        /**
317         * The index of the hidden fields used to pass parameters.
318         */
319        private static final int HIDDEN_FIELDS_PARAMS_IDX = 0;
320
321        /**
322         * The index of the hidden fields used for the default submit button.
323         */
324        private static final int HIDDEN_FIELDS_SUBMIT_IDX = 1;
325
326        /**
327         * Constructs a form with no validation.
328         *
329         * @param id
330         *            See Component
331         */
332        public Form(final String id)
333        {
334                this(id, null);
335        }
336
337        /**
338         * @param id
339         *            See Component
340         * @param model
341         *            See Component
342         * @see org.apache.wicket.Component#Component(String, IModel)
343         */
344        public Form(final String id, final IModel<T> model)
345        {
346                super(id, model);
347                setOutputMarkupId(true);
348        }
349
350        /**
351         * Adds a form validator to the form.
352         *
353         * @param validator
354         *            validator
355         * @throws IllegalArgumentException
356         *             if validator is null
357         * @see IFormValidator
358         */
359        public void add(final IFormValidator validator)
360        {
361                Args.notNull(validator, "validator");
362
363                if (validator instanceof Behavior)
364                {
365                        add((Behavior)validator);
366                }
367                else
368                {
369                        add(new FormValidatorAdapter(validator));
370                }
371        }
372
373        /**
374         * Removes a form validator from the form.
375         *
376         * @param validator
377         *            validator
378         * @throws IllegalArgumentException
379         *             if validator is null
380         * @see IFormValidator
381         */
382        public void remove(final IFormValidator validator)
383        {
384                Args.notNull(validator, "validator");
385
386                Behavior match = null;
387                for (Behavior behavior : getBehaviors())
388                {
389                        if (behavior.equals(validator))
390                        {
391                                match = behavior;
392                                break;
393                        }
394                        else if (behavior instanceof FormValidatorAdapter)
395                        {
396                                if (((FormValidatorAdapter)behavior).getValidator().equals(validator))
397                                {
398                                        match = behavior;
399                                        break;
400                                }
401                        }
402                }
403
404                if (match != null)
405                {
406                        remove(match);
407                }
408                else
409                {
410
411                        throw new IllegalStateException(
412                                "Tried to remove form validator that was not previously added. "
413                                        + "Make sure your validator's equals() implementation is sufficient");
414                }
415        }
416
417        /**
418         * Clears the input from the form's nested children of type {@link FormComponent}. This method
419         * is typically called when a form needs to be reset.
420         */
421        public final void clearInput()
422        {
423                // Visit all the (visible) form components and clear the input on each.
424                visitFormComponentsPostOrder(new IVisitor<FormComponent<?>, Void>()
425                {
426                        @Override
427                        public void component(final FormComponent<?> formComponent, IVisit<Void> visit)
428                        {
429                                if (formComponent.isVisibleInHierarchy())
430                                {
431                                        // Clear input from form component
432                                        formComponent.clearInput();
433                                }
434                        }
435                });
436        }
437
438        /**
439         * Registers an error feedback message for this component
440         *
441         * @param error
442         *            error message
443         * @param args
444         *            argument replacement map for ${key} variables
445         */
446        public final void error(String error, Map<String, Object> args)
447        {
448                error(new MapVariableInterpolator(error, args).toString());
449        }
450
451        /**
452         * THIS METHOD IS NOT PART OF THE WICKET PUBLIC API. DO NOT USE IT!
453         * <p>
454         * Gets the IFormSubmittingComponent which submitted this form.
455         *
456         * @return The component which submitted this form, or null if the processing was not triggered
457         *         by a registered IFormSubmittingComponent
458         */
459        public final IFormSubmittingComponent findSubmitter()
460        {
461                final IRequestParameters parameters = getRequestParameters(this);
462
463                IFormSubmittingComponent submittingComponent = getPage().visitChildren(
464                        IFormSubmittingComponent.class, new IVisitor<Component, IFormSubmittingComponent>()
465                        {
466                                @Override
467                                public void component(final Component component,
468                                        final IVisit<IFormSubmittingComponent> visit)
469                                {
470                                        // Get submitting component
471                                        final IFormSubmittingComponent submittingComponent = (IFormSubmittingComponent)component;
472                                        final Form<?> form = submittingComponent.getForm();
473
474                                        // Check for component-name or component-name.x request string
475                                        if ((form != null) && (form.getRootForm() == Form.this))
476                                        {
477                                                String name = submittingComponent.getInputName();
478                                                if ((!parameters.getParameterValue(name).isNull()) ||
479                                                        !parameters.getParameterValue(name + ".x").isNull())
480                                                {
481                                                        visit.stop(submittingComponent);
482                                                }
483                                        }
484                                }
485                        });
486
487                return submittingComponent;
488        }
489
490        /**
491         * Gets the default IFormSubmittingComponent. If set (not null), a hidden submit component will
492         * be rendered right after the form tag, so that when users press enter in a textfield, this
493         * submit component's action will be selected. If no default component is set (it is null),
494         * nothing additional is rendered.
495         * <p>
496         * WARNING: note that this is a best effort only. Unfortunately having a 'default' button in a
497         * form is ill defined in the standards, and of course IE has it's own way of doing things.
498         * </p>
499         * There can be only one default submit component per form hierarchy. So if you want to get the
500         * default component on a nested form, it will actually delegate the call to root form. </b>
501         *
502         * @return The submit component to set as the default IFormSubmittingComponent, or null when you
503         *         want to 'unset' any previously set default IFormSubmittingComponent
504         */
505        public final IFormSubmittingComponent getDefaultButton()
506        {
507                if (isRootForm())
508                {
509                        return defaultSubmittingComponent;
510                }
511                else
512                {
513                        return getRootForm().getDefaultButton();
514                }
515        }
516
517        /**
518         * Gets all {@link IFormValidator}s added to this form
519         *
520         * @return unmodifiable collection of {@link IFormValidator}s
521         */
522        public final Collection<IFormValidator> getFormValidators()
523        {
524                List<IFormValidator> validators = new ArrayList<>();
525
526                for (Behavior behavior : getBehaviors())
527                {
528                        if (behavior instanceof IFormValidator)
529                        {
530                                validators.add((IFormValidator)behavior);
531                        }
532                }
533
534                return Collections.unmodifiableCollection(validators);
535        }
536
537        /**
538         * Generate a piece of JavaScript that submits the form to the given URL of an {@link IRequestListener}.
539         *
540         * Warning: This code should only be called in the rendering phase for form components inside
541         * the form because it uses the css/javascript id of the form which can be stored in the markup.
542         *
543         * @param url
544         *            The listener url to be submitted to
545         * @return the javascript code that submits the form.
546         */
547        public final CharSequence getJsForListenerUrl(CharSequence url)
548        {
549                Form<?> root = getRootForm();
550
551                AppendingStringBuffer buffer = new AppendingStringBuffer();
552
553                String action = url.toString();
554                if (root.encodeUrlInHiddenFields()) {
555                        buffer.append(String.format("document.getElementById('%s').innerHTML = '",
556                                root.getHiddenFieldsId(HIDDEN_FIELDS_PARAMS_IDX)));
557
558                        // parameter must be sent as hidden field, as it would be ignored in the action URL
559                        int i = action.indexOf('?');
560                        if (i != -1)
561                        {
562                                writeParamsAsHiddenFields(Strings.split(action.substring(i + 1), '&'), buffer);
563
564                                action = action.substring(0, i);
565                        }
566
567                        buffer.append("';");
568                }
569                buffer.append(String.format("var f = document.getElementById('%s');", root.getMarkupId()));
570                buffer.append(String.format("f.action='%s';", action));
571                buffer.append("Wicket.Event.fire(f, 'submit');");
572                return buffer;
573        }
574
575        /**
576         * Generate a piece of JavaScript that submits the form with the given
577         * {@link IFormSubmittingComponent}.
578         *
579         * @param submitter
580         *            the submitter
581         * @param triggerEvent
582         *            When true, the form will be submitted via standard form submission ({@code requestSubmit()})
583         *            including client side validation and firing a javascript submit event, when false via
584         *            the {@code submit()} method.
585         * @return the javascript code that submits the form.
586         *
587         * @see #findSubmitter()
588         */
589        public final CharSequence getJsForSubmitter(IFormSubmittingComponent submitter, boolean triggerEvent)
590        {
591                Form<?> root = getRootForm();
592
593                String param = submitter.getInputName() + "=x";
594
595                AppendingStringBuffer buffer = new AppendingStringBuffer();
596                buffer.append(String.format("var f = document.getElementById('%s');", root.getMarkupId()));
597                buffer.append(String.format("document.getElementById('%s').innerHTML += '",
598                        root.getHiddenFieldsId(HIDDEN_FIELDS_PARAMS_IDX)));
599                writeParamsAsHiddenFields(new String[] {param}, buffer);
600                buffer.append("';");
601
602                if (triggerEvent)
603                {
604                        buffer.append("Wicket.Event.requestSubmit(f);");
605                }
606                else
607                {
608                        buffer.append("f.submit();");
609                }
610                return buffer;
611        }
612
613        /**
614         * Gets the maximum size for uploads. If null, the setting
615         * {@link org.apache.wicket.settings.ApplicationSettings#getDefaultMaximumUploadSize()} is used.
616         *
617         *
618         * @return the maximum size
619         */
620        public final Bytes getMaxSize()
621        {
622                /*
623                 * NOTE: This method should remain final otherwise it will be impossible to set a default
624                 * max size smaller then the one specified in applications settings because the inner form
625                 * will return the default unless it is specifically set in the traversal. With this method
626                 * remaining final we can tell when the value is explicitly set by the user.
627                 *
628                 * If the value needs to be dynamic it can be set in oncofigure() instead of overriding this
629                 * method.
630                 */
631
632                final Bytes[] maxSize = { this.maxSize };
633                if (maxSize[0] == null)
634                {
635                        visitChildren(Form.class, new IVisitor<Form<?>, Bytes>()
636                        {
637                                @Override
638                                public void component(Form<?> component, IVisit<Bytes> visit)
639                                {
640                                        maxSize[0] = LongValue.maxNullSafe(maxSize[0], component.maxSize);
641                                }
642                        });
643                }
644                if (maxSize[0] == null)
645                {
646                        return getApplication().getApplicationSettings().getDefaultMaximumUploadSize();
647                }
648                return maxSize[0];
649        }
650
651        /**
652         * Gets maximum size for each file of an upload.
653         *
654         * @return
655         */
656        public Bytes getFileMaxSize()
657        {
658                return fileMaxSize;
659        }
660
661        /**
662         * Gets maximum count of files in the form
663         *
664         * @return
665         */
666        public long getFileCountMax()
667        {
668                return fileCountMax;
669        }
670
671        /**
672         * Returns the root form or this, if this is the root form.
673         *
674         * @return root form or this form
675         */
676        public Form<?> getRootForm()
677        {
678                Form<?> form;
679                Form<?> parent = this;
680                do
681                {
682                        form = parent;
683                        parent = form.findParent(Form.class);
684                }
685                while (parent != null);
686
687                return form;
688        }
689
690        /**
691         * Returns the prefix used when building validator keys. This allows a form to use a separate
692         * "set" of keys. For example if prefix "short" is returned, validator key short.Required will
693         * be tried instead of Required key.
694         * <p>
695         * This can be useful when different designs are used for a form. In a form where error messages
696         * are displayed next to their respective form components as opposed to at the top of the form,
697         * the ${label} attribute is of little use and only causes redundant information to appear in
698         * the message. Forms like these can return the "short" (or any other string) validator prefix
699         * and declare key: short.Required=required to override the longer message which is usually
700         * declared like this: Required=${label} is a required field
701         * <p>
702         * Returned prefix will be used for all form components. The prefix can also be overridden on
703         * form component level by overriding {@link FormComponent#getValidatorKeyPrefix()}
704         *
705         * @return prefix prepended to validator keys
706         */
707        public String getValidatorKeyPrefix()
708        {
709                return null;
710        }
711
712        /**
713         * Gets whether the current form has any error registered.
714         *
715         * @return True if this form has at least one error.
716         */
717        public final boolean hasError()
718        {
719                // if this form itself has an error message
720                if (hasErrorMessage())
721                {
722                        return true;
723                }
724
725                // the form doesn't have any errors, now check any nested form
726                // components
727                return anyFormComponentError();
728        }
729
730        /**
731         * Returns whether the form is a root form, which means that there's no other form in it's
732         * parent hierarchy.
733         *
734         * @return true if form is a root form, false otherwise
735         */
736        public boolean isRootForm()
737        {
738                return findParent(Form.class) == null;
739        }
740
741        /**
742         * Checks if this form has been submitted during the current request
743         *
744         * @return true if the form has been submitted during this request, false otherwise
745         */
746        public final boolean isSubmitted()
747        {
748                return getFlag(FLAG_SUBMITTED);
749        }
750
751        /**
752         * THIS METHOD IS NOT PART OF THE WICKET API. DO NOT ATTEMPT TO OVERRIDE OR CALL IT.
753         *
754         * Handles form submissions.
755         *
756         * @see #onFormSubmitted(IFormSubmitter)
757         */
758        @Override
759        public final void onRequest()
760        {
761                onFormSubmitted(null);
762        }
763
764        /**
765         * Called when a form has been submitted using a method differing from return value of
766         * {@link #getMethod()}. For example, someone can copy and paste the action url and invoke the
767         * form using a {@code GET} instead of the desired {@code POST}. This method allows the user to
768         * react to this situation.
769         *
770         * @return response that can either abort or continue the processing of the form
771         */
772        protected MethodMismatchResponse onMethodMismatch()
773        {
774                return MethodMismatchResponse.CONTINUE;
775        }
776
777        /**
778         * THIS METHOD IS NOT PART OF THE WICKET API. DO NOT ATTEMPT TO OVERRIDE OR CALL IT.
779         *
780         * Handles form submissions.
781         *
782         * @param submitter
783         *            listener that will receive form processing events, if {@code null} the form will
784         *            attempt to locate one
785         *
786         * @see Form#validate()
787         */
788        public final void onFormSubmitted(IFormSubmitter submitter)
789        {
790                // check methods match
791                if (getRequest().getContainerRequest() instanceof HttpServletRequest)
792                {
793                        String desiredMethod = getMethod();
794                        String actualMethod = ((HttpServletRequest)getRequest().getContainerRequest()).getMethod();
795                        if (!actualMethod.equalsIgnoreCase(desiredMethod))
796                        {
797                                MethodMismatchResponse response = onMethodMismatch();
798                                switch (response)
799                                {
800                                        case ABORT :
801                                                return;
802                                        case CONTINUE :
803                                                break;
804                                        default :
805                                                throw new IllegalStateException("Invalid " +
806                                                                MethodMismatchResponse.class.getName() + " value: " + response);
807                                }
808                        }
809                }
810
811                markFormsSubmitted(submitter);
812
813                if (handleMultiPart())
814                {
815                        // Tells FormComponents that a new user input has come
816                        inputChanged();
817
818                        // First, see if the processing was triggered by a IFormSubmittingComponent
819                        if (submitter == null)
820                        {
821                                submitter = findSubmitter();
822
823                                if (submitter instanceof IFormSubmittingComponent)
824                                {
825                                        IFormSubmittingComponent submittingComponent = (IFormSubmittingComponent)submitter;
826                                        Component component = (Component)submitter;
827
828                                        if (!component.isVisibleInHierarchy())
829                                        {
830                                                throw new WicketRuntimeException("Submit Button " +
831                                                        submittingComponent.getInputName() + " (path=" +
832                                                        component.getPageRelativePath() + ") is not visible");
833                                        }
834
835                                        if (!component.isEnabledInHierarchy())
836                                        {
837                                                throw new WicketRuntimeException("Submit Button " +
838                                                        submittingComponent.getInputName() + " (path=" +
839                                                        component.getPageRelativePath() + ") is not enabled");
840                                        }
841                                }
842                        }
843
844                        // When processing was triggered by a Wicket IFormSubmittingComponent and that
845                        // component indicates it wants to be called immediately
846                        // (without processing), call the IFormSubmittingComponent.onSubmit* methods right
847                        // away.
848                        if (submitter != null && !submitter.getDefaultFormProcessing())
849                        {
850                                submitter.onSubmit();
851                                submitter.onAfterSubmit();
852                        }
853                        else
854                        {
855                                // the submit request might be for one of the nested forms, so let's
856                                // find the right one:
857                                final Form<?> formToProcess = findFormToProcess(submitter);
858
859                                // process the form for this request
860                                formToProcess.process(submitter);
861                        }
862                }
863                // If multi part did fail check if an error is registered and call
864                // onError
865                else if (hasError())
866                {
867                        callOnError(submitter);
868                }
869
870                // update auto labels if we are inside an ajax request
871                getRequestCycle().find(AjaxRequestTarget.class).ifPresent(target -> {
872                        visitChildren(FormComponent.class, new IVisitor<FormComponent<?>, Void>()
873                        {
874                                @Override
875                                public void component(FormComponent<?> component, IVisit<Void> visit)
876                                {
877                                        component.updateAutoLabels(target, false);
878                                }
879                        });
880                });
881        }
882
883        /**
884         * This method finds the correct form that should be processed based on the submitting component
885         * (if there is one) and correctly handles nested forms by also looking at
886         * {@link #wantSubmitOnNestedFormSubmit()} throughout the form hierarchy. The form that needs to
887         * be processed is:
888         * <ul>
889         * <li>if there is no submitting component (i.e. a "default submit"): this form.</li>
890         * <li>if only one form exists (this): this form.</li>
891         * <li>if nested forms exist:
892         * <ul>
893         * <li>if the submitting component points at the root form: the root form</li>
894         * <li>if the submitting component points at a nested form:
895         * <ul>
896         * <li>starting at that nested form, the outermost form that returns true for
897         * {@link #wantSubmitOnNestedFormSubmit()}</li>
898         * <li>if no outer form returns true for that, the nested form is returned.</li>
899         * </ul>
900         * </li>
901         * </ul>
902         * </li>
903         * </ul>
904         *
905         * @param submitter
906         *            The submitting component, if any. May be null.
907         * @return The form that needs to be processed.
908         */
909        private Form<?> findFormToProcess(IFormSubmitter submitter)
910        {
911                if (submitter == null)
912                {
913                        // no submitting component => default form submit => so *this* is the
914                        // form to process
915                        return this;
916                }
917                else
918                {
919                        // some button submitted this request, this is the form it belongs to:
920                        final Form<?> targetedForm = submitter.getForm();
921                        if (targetedForm == null)
922                        {
923                                throw new IllegalStateException(
924                                        "submitting component must not return 'null' on getForm()");
925                        }
926
927                        final Form<?> rootForm = getRootForm();
928                        if (targetedForm == rootForm)
929                        {
930                                // the submitting component points at the root form => so let's just go with
931                                // root, everything else will be submitted with it anyway.
932                                return rootForm;
933                        }
934                        else
935                        {
936                                // a different form was targeted. let's find the outermost form that wants to be
937                                // submitted.
938                                Form<?> formThatWantsToBeSubmitted = targetedForm;
939                                Form<?> current = targetedForm.findParent(Form.class);
940                                while (current != null)
941                                {
942                                        if (current.wantSubmitOnNestedFormSubmit())
943                                        {
944                                                formThatWantsToBeSubmitted = current;
945                                        }
946                                        current = current.findParent(Form.class);
947                                }
948                                return formThatWantsToBeSubmitted;
949                        }
950                }
951        }
952
953        /**
954         * Whether this form wants to be submitted too if a nested form is submitted. By default, this
955         * is false, so when a nested form is submitted, this form will <em>not</em> be submitted. If
956         * this method is overridden to return true, this form <em>will</em> be submitted.
957         *
958         * @return Whether this form wants to be submitted too if a nested form is submitted.
959         */
960        // TODO wicket-7 migration guide: changed from public to protected
961        protected boolean wantSubmitOnNestedFormSubmit()
962        {
963                return false;
964        }
965
966        /**
967         * Whether this *nested* form wants to be submitted when parent form is submitted. By default,
968         * this is true, so when a parent form is submitted, the nested form is also submitted. If this
969         * method is overridden to return false, it will not be validated, processed nor submitted.
970         *
971         * @return {@code true} by default
972         */
973        protected boolean wantSubmitOnParentFormSubmit()
974        {
975                return true;
976        }
977
978        /**
979         * Process the form. Though you can override this method to provide your own algorithm, it is
980         * not recommended to do so.
981         *
982         * <p>
983         * See the class documentation for further details on the form processing
984         * </p>
985         *
986         * @param submittingComponent
987         *            component responsible for submitting the form, or <code>null</code> if none (eg
988         *            the form has been submitted via the enter key or javascript calling form.submit())
989         *
990         * @see #delegateSubmit(IFormSubmitter) for an easy way to process submitting component in the
991         *      default manner
992         */
993        public void process(IFormSubmitter submittingComponent)
994        {
995                if (!isEnabledInHierarchy() || !isVisibleInHierarchy())
996                {
997                        // since process() can be called outside of the default form workflow, an additional
998                        // check is needed
999
1000                        // FIXME throw listener exception
1001                        return;
1002                }
1003
1004                // run validation
1005                validate();
1006
1007                // If a validation error occurred
1008                if (hasError())
1009                {
1010                        // mark all children as invalid
1011                        markFormComponentsInvalid();
1012
1013                        // let subclass handle error
1014                        callOnError(submittingComponent);
1015                }
1016                else
1017                {
1018                        // mark all children as valid
1019                        markFormComponentsValid();
1020
1021                        // before updating, call the interception method for clients
1022                        beforeUpdateFormComponentModels();
1023
1024                        // Update model using form data
1025                        updateFormComponentModels();
1026
1027                        // validate model objects after input values have been bound
1028                        internalOnValidateModelObjects();
1029                        if (hasError())
1030                        {
1031                                callOnError(submittingComponent);
1032                                return;
1033                        }
1034
1035                        // Form has no error
1036                        delegateSubmit(submittingComponent);
1037                }
1038        }
1039
1040        /**
1041         * Calls onError on this {@link Form} and any enabled and visible nested form, if the respective
1042         * {@link Form} actually has errors.
1043         *
1044         * @param submitter
1045         */
1046        protected void callOnError(IFormSubmitter submitter)
1047        {
1048                final Form<?> processingForm = findFormToProcess(submitter);
1049
1050                if (submitter != null)
1051                {
1052                        submitter.onError();
1053                }
1054
1055                // invoke Form#onSubmit(..) going from innermost to outermost
1056                Visits.visitPostOrder(processingForm, new IVisitor<Form<?>, Void>()
1057                {
1058                        @Override
1059                        public void component(Form<?> form, IVisit<Void> visit)
1060                        {
1061                                if (!form.isEnabledInHierarchy() || !form.isVisibleInHierarchy())
1062                                {
1063                                        visit.dontGoDeeper();
1064                                        return;
1065                                }
1066                                if (form.hasError())
1067                                {
1068                                        form.onError();
1069                                }
1070                        }
1071                }, new ClassVisitFilter(Form.class));
1072        }
1073
1074
1075        /**
1076         * Sets FLAG_SUBMITTED to true on this form and every enabled nested form.
1077         * @param submitter
1078         */
1079        private void markFormsSubmitted(IFormSubmitter submitter)
1080        {
1081                setFlag(FLAG_SUBMITTED, true);
1082                Form<?> formToProcess = findFormToProcess(submitter);
1083
1084                visitChildren(Form.class, new IVisitor<Component, Void>()
1085                {
1086                        @Override
1087                        public void component(final Component component, final IVisit<Void> visit)
1088                        {
1089                                Form<?> form = (Form<?>)component;
1090                                if ((form.wantSubmitOnParentFormSubmit() || form == formToProcess)
1091                                        && form.isEnabledInHierarchy() && form.isVisibleInHierarchy())
1092                                {
1093                                        form.setFlag(FLAG_SUBMITTED, true);
1094                                        return;
1095                                }
1096                                visit.dontGoDeeper();
1097                        }
1098                });
1099        }
1100
1101        /**
1102         * Sets the default IFormSubmittingComponent. If set (not null), a hidden submit component will
1103         * be rendered right after the form tag, so that when users press enter in a textfield, this
1104         * submit component's action will be selected. If no default component is set (so unset by
1105         * calling this method with null), nothing additional is rendered.
1106         * <p>
1107         * WARNING: note that this is a best effort only. Unfortunately having a 'default' button in a
1108         * form is ill defined in the standards, and of course IE has it's own way of doing things.
1109         * </p>
1110         * There can be only one default button per form hierarchy. So if you set default button on a
1111         * nested form, it will actually delegate the call to root form. </b>
1112         *
1113         * @param submittingComponent
1114         *            The component to set as the default submitting component, or null when you want to
1115         *            'unset' any previously set default component
1116         */
1117        public final void setDefaultButton(IFormSubmittingComponent submittingComponent)
1118        {
1119                if (isRootForm())
1120                {
1121                        defaultSubmittingComponent = submittingComponent;
1122                }
1123                else
1124                {
1125                        getRootForm().setDefaultButton(submittingComponent);
1126                }
1127        }
1128
1129        /**
1130         * Sets the maximum size for uploads. If null, the setting
1131         * {@link org.apache.wicket.settings.ApplicationSettings#getDefaultMaximumUploadSize()} is used.
1132         *
1133         * @param maxSize
1134         *            The maximum size
1135         */
1136        public void setMaxSize(final Bytes maxSize)
1137        {
1138                this.maxSize = maxSize;
1139        }
1140
1141        /**
1142         * Sets maximum size of each file in upload request.
1143         *
1144         * @param fileMaxSize
1145         */
1146        public void setFileMaxSize(Bytes fileMaxSize)
1147        {
1148                this.fileMaxSize = fileMaxSize;
1149        }
1150
1151        /**
1152         * Sets maximum amount of files in upload request.
1153         *
1154         * @param fileCountMax
1155         */
1156        public void setFileCountMax(long fileCountMax)
1157        {
1158                this.fileCountMax = fileCountMax;
1159        }
1160
1161        /**
1162         * Set to true to use enctype='multipart/form-data', and to process file uploads by default
1163         * multiPart = false
1164         *
1165         * @param multiPart
1166         *            whether this form should behave as a multipart form
1167         */
1168        public void setMultiPart(boolean multiPart)
1169        {
1170                if (multiPart)
1171                {
1172                        this.multiPart |= MULTIPART_HARD;
1173                }
1174                else
1175                {
1176                        this.multiPart &= ~MULTIPART_HARD;
1177                }
1178        }
1179
1180        /**
1181         * @see org.apache.wicket.Component#setVersioned(boolean)
1182         */
1183        @Override
1184        public final Component setVersioned(final boolean isVersioned)
1185        {
1186                super.setVersioned(isVersioned);
1187
1188                // Search for FormComponents like TextField etc.
1189                visitFormComponents(new IVisitor<FormComponent<?>, Void>()
1190                {
1191                        @Override
1192                        public void component(final FormComponent<?> formComponent, IVisit<Void> visit)
1193                        {
1194                                formComponent.setVersioned(isVersioned);
1195                        }
1196                });
1197                return this;
1198        }
1199
1200        /**
1201         * Convenient and typesafe way to visit all the form components on a form.
1202         *
1203         * @param <R>
1204         *            return object type
1205         * @param visitor
1206         *            The visitor interface to call
1207         * @return user provided in callback
1208         */
1209        public final <R> R visitFormComponents(final IVisitor<FormComponent<?>, R> visitor)
1210        {
1211                return visitChildren(FormComponent.class, visitor);
1212        }
1213
1214        /**
1215         * Convenient and typesafe way to visit all the form components on a form postorder (deepest
1216         * first)
1217         *
1218         * @param <R>
1219         *            Return object type
1220         * @param visitor
1221         *            The visitor interface to call
1222         * @return whatever you provided
1223         */
1224        public final <R> R visitFormComponentsPostOrder(
1225                final IVisitor<? extends FormComponent<?>, R> visitor)
1226        {
1227                return FormComponent.visitFormComponentsPostOrder(this, visitor);
1228        }
1229
1230        /**
1231         * Find out whether there is any registered error for a form component.
1232         *
1233         * @return whether there is any registered error for a form component
1234         */
1235        private boolean anyFormComponentError()
1236        {
1237                // Check ALL children for error messages irrespective of FormComponents or not
1238                Boolean error = visitChildren(Component.class, new IVisitor<Component, Boolean>()
1239                {
1240                        @Override
1241                        public void component(final Component component, final IVisit<Boolean> visit)
1242                        {
1243                                if (component.hasErrorMessage() && component.isVisibleInHierarchy() && component.isEnabledInHierarchy())
1244                                {
1245                                        visit.stop(true);
1246                                }
1247                        }
1248                });
1249
1250                return (error != null) && error;
1251        }
1252
1253        /**
1254         * Visits the form's children FormComponents and inform them that a new user input is available
1255         * in the Request
1256         */
1257        private void inputChanged()
1258        {
1259                visitFormComponentsPostOrder(new IVisitor<FormComponent<?>, Void>()
1260                {
1261                        @Override
1262                        public void component(final FormComponent<?> formComponent, IVisit<Void> visit)
1263                        {
1264                                formComponent.inputChanged();
1265                        }
1266                });
1267        }
1268
1269        /**
1270         * If a default IFormSubmittingComponent was set on this form, this method will be called to
1271         * render an extra field with an invisible style so that pressing enter in one of the textfields
1272         * will do a form submit using this component. This method is overridable as what we do is best
1273         * effort only, and may not what you want in specific situations. So if you have specific
1274         * usability concerns, or want to follow another strategy, you may override this method.
1275         *
1276         * @see #addDefaultSubmitButtonHandler(IHeaderResponse)
1277         */
1278        protected void appendDefaultButtonField()
1279        {
1280                AppendingStringBuffer buffer = new AppendingStringBuffer();
1281
1282                // hidden div
1283                buffer.append(String.format("<div hidden=\"\" class=\"%s\">",
1284                        getString(HIDDEN_FIELDS_CSS_CLASS_KEY)));
1285
1286                // add an empty textfield (otherwise IE doesn't work)
1287                buffer.append("<input type=\"text\" tabindex=\"-1\" autocomplete=\"off\"/>");
1288
1289                // add the submitting component
1290                buffer
1291                        .append(String.format("<input id=\"%s\" type=\"submit\" tabindex=\"-1\" name=\"%s\" />",
1292                                getHiddenFieldsId(HIDDEN_FIELDS_SUBMIT_IDX),
1293                                defaultSubmittingComponent.getInputName()));
1294
1295                // close div
1296                buffer.append("</div>");
1297
1298                getResponse().write(buffer);
1299        }
1300
1301        /**
1302         * Where {@link #appendDefaultButtonField()} renders the markup for default submit button
1303         * handling, this method attaches the event handler to its 'click' event. The 'click' event on
1304         * the hidden submit button will be dispatched to the selected default submit button. As with
1305         * {@link #appendDefaultButtonField()} this method can be overridden when the generated code
1306         * needs to be adjusted for a specific usecase.
1307         *
1308         * @param headerResponse
1309         *            The header response.
1310         */
1311        protected void addDefaultSubmitButtonHandler(IHeaderResponse headerResponse)
1312        {
1313                final Component submittingComponent = (Component) defaultSubmittingComponent;
1314                AppendingStringBuffer buffer = new AppendingStringBuffer();
1315                buffer.append("var b=document.getElementById('");
1316                buffer.append(submittingComponent.getMarkupId());
1317                buffer.append("'); if (b!=null && b.onclick!=null && typeof(b.onclick) != 'undefined') ");
1318                buffer.append(
1319                        "{  var r = Wicket.bind(b.onclick, b)(); if (r != false) b.click(); } else { b.click(); };  return false;");
1320                headerResponse.render(OnEventHeaderItem
1321                        .forMarkupId(getHiddenFieldsId(HIDDEN_FIELDS_SUBMIT_IDX), "click", buffer.toString()));
1322        }
1323
1324        /**
1325         * Template method to allow clients to do any processing (like recording the current model so
1326         * that, in case onSubmit does further validation, the model can be rolled back) before the
1327         * actual updating of form component models is done.
1328         */
1329        protected void beforeUpdateFormComponentModels()
1330        {
1331        }
1332
1333        /**
1334         * Called (by the default implementation of 'process') when all fields validated, the form was
1335         * updated and it's data was allowed to be persisted. It is meant for delegating further
1336         * processing to clients.
1337         * <p>
1338         * This implementation first finds out whether the form processing was triggered by a nested
1339         * IFormSubmittingComponent of this form. If that is the case, that component's
1340         * onSubmitBefore/AfterForm methods are called appropriately..
1341         * </p>
1342         * <p>
1343         * Regardless of whether a submitting component was found, the form's onSubmit method is called
1344         * next.
1345         * </p>
1346         *
1347         * @param submittingComponent
1348         *            the component that triggered this form processing, or null if the processing was
1349         *            triggered by something else (like a non-Wicket submit button or a javascript
1350         *            execution)
1351         */
1352        protected void delegateSubmit(IFormSubmitter submittingComponent)
1353        {
1354                final Form<?> processingForm = findFormToProcess(submittingComponent);
1355
1356                // collect all forms innermost to outermost before any hierarchy is changed
1357                final List<Form<?>> forms = Generics.newArrayList(3);
1358                Visits.visitPostOrder(processingForm, new IVisitor<Form<?>, Void>()
1359                {
1360                        @Override
1361                        public void component(Form<?> form, IVisit<Void> visit)
1362                        {
1363                                if (form.isSubmitted())
1364                                {
1365                                        forms.add(form);
1366                                }
1367                        }
1368                }, new ClassVisitFilter(Form.class));
1369
1370                // process submitting component (if specified)
1371                if (submittingComponent != null)
1372                {
1373                        // invoke submit on component
1374                        submittingComponent.onSubmit();
1375                }
1376
1377                // invoke Form#onSubmit(..)
1378                for (Form<?> form : forms)
1379                {
1380                        form.onSubmit();
1381                }
1382
1383                if (submittingComponent != null)
1384                {
1385                        submittingComponent.onAfterSubmit();
1386                }
1387        }
1388
1389        /**
1390         * Returns the id which will be used for the hidden div containing all parameter fields.
1391         *
1392         * @param idx
1393         *            The index of the div to keep different divs apart.
1394         * @return the id of the hidden div
1395         */
1396        private final String getHiddenFieldsId(int idx)
1397        {
1398                return getInputNamePrefix() + getMarkupId() + "_hf_" + idx;
1399        }
1400
1401        /**
1402         * Gets the HTTP submit method that will appear in form markup. If no method is specified in the
1403         * template, "post" is the default. Note that the markup-declared HTTP method may not correspond
1404         * to the one actually used to submit the form; in an Ajax submit, for example, JavaScript event
1405         * handlers may submit the form with a "get" even when the form method is declared as "post."
1406         * Therefore this method should not be considered a guarantee of the HTTP method used, but a
1407         * value for the markup only. Override if you have a requirement to alter this behavior.
1408         *
1409         * @return the submit method specified in markup.
1410         */
1411        protected String getMethod()
1412        {
1413                String method = getMarkupAttributes().getString("method");
1414                return (method != null) ? method : METHOD_POST;
1415        }
1416
1417        /**
1418         *
1419         * @see org.apache.wicket.Component#getStatelessHint()
1420         */
1421        @Override
1422        protected boolean getStatelessHint()
1423        {
1424                return false;
1425        }
1426
1427        /**
1428         * @return True if is multipart
1429         */
1430        public boolean isMultiPart()
1431        {
1432                if (multiPart == 0)
1433                {
1434                        Boolean anyEmbeddedMultipart = visitChildren(Component.class,
1435                                        new IVisitor<Component, Boolean>()
1436                                        {
1437                                                @Override
1438                                                public void component(final Component component, final IVisit<Boolean> visit)
1439                                                {
1440                                                        boolean isMultiPart = false;
1441                                                        if (component instanceof Form<?>)
1442                                                        {
1443                                                                Form<?> form = (Form<?>)component;
1444                                                                if (form.isVisibleInHierarchy() && form.isEnabledInHierarchy())
1445                                                                {
1446                                                                        isMultiPart = (form.multiPart & MULTIPART_HARD) != 0;
1447                                                                }
1448                                                        }
1449                                                        else if (component instanceof FormComponent<?>)
1450                                                        {
1451                                                                FormComponent<?> fc = (FormComponent<?>)component;
1452                                                                if (fc.isVisibleInHierarchy() && fc.isEnabledInHierarchy())
1453                                                                {
1454                                                                        isMultiPart = fc.isMultiPart();
1455                                                                }
1456                                                        }
1457
1458                                                        if (isMultiPart)
1459                                                        {
1460                                                                visit.stop(true);
1461                                                        }
1462                                                }
1463
1464                                        });
1465
1466                        if (Boolean.TRUE.equals(anyEmbeddedMultipart)) {
1467                                multiPart |= MULTIPART_HINT_YES;
1468                        } else {
1469                                multiPart |= MULTIPART_HINT_NO;
1470                        }
1471                }
1472
1473                return (multiPart & (MULTIPART_HARD | MULTIPART_HINT_YES)) != 0;
1474        }
1475
1476        /**
1477         * Handles multi-part processing of the submitted data.
1478         * <strong>WARNING</strong> If this method is overridden it can break {@link FileUploadField}s on this form
1479         *
1480         * @return false if form is multipart and upload failed
1481         */
1482        protected boolean handleMultiPart()
1483        {
1484                if (isMultiPart())
1485                {
1486                        // Change the request to a multipart web request so parameters are
1487                        // parsed out correctly
1488                        try
1489                        {
1490                                ServletWebRequest request = (ServletWebRequest)getRequest();
1491                                final MultipartServletWebRequest multipartWebRequest = request.newMultipartWebRequest(
1492                                        getMaxSize(), getPage().getId());
1493                                multipartWebRequest.setFileMaxSize(getFileMaxSize());
1494                                multipartWebRequest.setFileCountMax(getFileCountMax());
1495                                multipartWebRequest.parseFileParts();
1496
1497                                // TODO: Can't this be detected from header?
1498                                getRequestCycle().setRequest(multipartWebRequest);
1499                        }
1500                        catch (final FileUploadException fux)
1501                        {
1502                                // Create model with exception and maximum size values
1503                                final Map<String, Object> model = new HashMap<>();
1504                                model.put("exception", fux);
1505                                model.put("maxSize", getMaxSize());
1506                                model.put("fileMaxSize", getFileMaxSize());
1507                                model.put("fileCountMax", getFileCountMax());
1508
1509                                onFileUploadException(fux, model);
1510
1511                                // don't process the form if there is a FileUploadException
1512                                return false;
1513                        }
1514                }
1515                return true;
1516        }
1517
1518        /**
1519         * The default message may look like ".. may not exceed 10240 Bytes..". Which is ok, but
1520         * sometimes you may want something like "10KB". By subclassing this method you may replace
1521         * maxSize in the model or add you own property and use that in your error message.
1522         * <p>
1523         * Don't forget to call super.onFileUploadException(e, model) at the end of your method.
1524         *
1525         * @param e
1526         * @param model
1527         */
1528        protected void onFileUploadException(final FileUploadException e,
1529                final Map<String, Object> model)
1530        {
1531                if (e instanceof FileUploadBase.SizeLimitExceededException)
1532                {
1533                        String msg = getString(UPLOAD_TOO_LARGE_RESOURCE_KEY, Model.ofMap(model));
1534                        error(msg);
1535                }
1536                else if (e instanceof FileUploadBase.FileSizeLimitExceededException)
1537                {
1538                        String msg = getString(UPLOAD_SINGLE_FILE_TOO_LARGE_RESOURCE_KEY, Model.ofMap(model));
1539                        error(msg);
1540                }
1541                else if (e instanceof FileCountLimitExceededException)
1542                {
1543                        String msg = getString(UPLOAD_TOO_MANY_FILES_RESOURCE_KEY, Model.ofMap(model));
1544                        error(msg);
1545                }
1546                else
1547                {
1548                        String msg = getString(UPLOAD_FAILED_RESOURCE_KEY, Model.ofMap(model));
1549                        error(msg);
1550
1551                        log.warn(msg, e);
1552                }
1553        }
1554
1555        /**
1556         * @see org.apache.wicket.Component#internalOnModelChanged()
1557         */
1558        @Override
1559        protected void internalOnModelChanged()
1560        {
1561                // Visit all the form components and validate each
1562                visitFormComponentsPostOrder(new IVisitor<FormComponent<?>, Void>()
1563                {
1564                        @Override
1565                        public void component(final FormComponent<?> formComponent, IVisit<Void> visit)
1566                        {
1567                                // If form component is using form model
1568                                if (formComponent.sameInnermostModel(Form.this))
1569                                {
1570                                        formComponent.modelChanged();
1571                                }
1572                        }
1573                });
1574        }
1575
1576        /**
1577         * Mark each form component on this form invalid.
1578         */
1579        protected final void markFormComponentsInvalid()
1580        {
1581                // call invalidate methods of all nested form components
1582                visitFormComponentsPostOrder(new IVisitor<FormComponent<?>, Void>()
1583                {
1584                        @Override
1585                        public void component(final FormComponent<?> formComponent, IVisit<Void> visit)
1586                        {
1587                                if (formComponent.isVisibleInHierarchy())
1588                                {
1589                                        formComponent.invalid();
1590                                }
1591                        }
1592                });
1593        }
1594
1595        /**
1596         * Mark each form component on this form and on nested forms valid.
1597         */
1598        protected final void markFormComponentsValid()
1599        {
1600                internalMarkFormComponentsValid();
1601                markNestedFormComponentsValid();
1602        }
1603
1604        /**
1605         * Mark each form component on nested form valid.
1606         */
1607        private void markNestedFormComponentsValid()
1608        {
1609                visitChildren(Form.class, new IVisitor<Form<?>, Void>()
1610                {
1611                        @Override
1612                        public void component(final Form<?> form, final IVisit<Void> visit)
1613                        {
1614                                if (form.isSubmitted())
1615                                {
1616                                        form.internalMarkFormComponentsValid();
1617                                }
1618                                else
1619                                {
1620                                        visit.dontGoDeeper();
1621                                }
1622                        }
1623                });
1624        }
1625
1626        /**
1627         * Mark each form component on this form valid.
1628         */
1629        private void internalMarkFormComponentsValid()
1630        {
1631                // call valid methods of all nested form components
1632                visitFormComponentsPostOrder(new IVisitor<FormComponent<?>, Void>()
1633                {
1634                        @Override
1635                        public void component(final FormComponent<?> formComponent, IVisit<Void> visit)
1636                        {
1637                                if (formComponent.getForm() == Form.this && formComponent.isVisibleInHierarchy())
1638                                {
1639                                        formComponent.valid();
1640                                }
1641                        }
1642                });
1643        }
1644
1645        /**
1646         * @see org.apache.wicket.Component#onComponentTag(ComponentTag)
1647         */
1648        @Override
1649        protected void onComponentTag(final ComponentTag tag)
1650        {
1651                super.onComponentTag(tag);
1652
1653                if (isRootForm())
1654                {
1655                        checkComponentTag(tag, "form");
1656
1657                        String method = getMethod().toLowerCase(Locale.ROOT);
1658                        tag.put("method", method);
1659                        String url = getActionUrl().toString();
1660                        if (encodeUrlInHiddenFields())
1661                        {
1662                                int i = url.indexOf('?');
1663                                String action = (i > -1) ? url.substring(0, i) : "";
1664                                tag.put("action", action);
1665                                // alternatively, we could just put an empty string here, so
1666                                // that mounted paths stay in good order. I decided against this
1667                                // as I'm not sure whether that could have side effects with
1668                                // other encoders
1669                        }
1670                        else
1671                        {
1672                                tag.put("action", url);
1673                        }
1674
1675                        if (isMultiPart())
1676                        {
1677                                if (METHOD_GET.equalsIgnoreCase(method))
1678                                {
1679                                        log.warn("Form with id '{}' is multipart. It should use method 'POST'!",
1680                                                getId());
1681                                        tag.put("method", METHOD_POST.toLowerCase(Locale.ROOT));
1682                                }
1683
1684                                tag.put("enctype", ENCTYPE_MULTIPART_FORM_DATA);
1685                                //
1686                                // require the application-encoding for multipart/form-data to be sure to
1687                                // get multipart-uploaded characters with the proper encoding on the following
1688                                // request.
1689                                //
1690                                // for details see: http://stackoverflow.com/questions/546365
1691                                //
1692                                tag.put("accept-charset", getApplication().getRequestCycleSettings()
1693                                        .getResponseRequestEncoding());
1694                        }
1695                        else
1696                        {
1697                                // sanity check
1698                                String enctype = (String)tag.getAttributes().get("enctype");
1699                                if (ENCTYPE_MULTIPART_FORM_DATA.equalsIgnoreCase(enctype))
1700                                {
1701                                        // though not set explicitly in Java, this is a multipart
1702                                        // form
1703                                        setMultiPart(true);
1704                                }
1705                        }
1706                }
1707                else
1708                {
1709                        adjustNestedTagName(tag);
1710                        tag.remove("method");
1711                        tag.remove("action");
1712                        tag.remove("enctype");
1713                }
1714        }
1715
1716        // WICKET-6658 form is not allowed, anything else can stay as is
1717        private void adjustNestedTagName(ComponentTag tag) {
1718                if ("form".equalsIgnoreCase(tag.getName()))
1719                {
1720                        tag.setName("div");
1721                }
1722        }
1723
1724        /**
1725         * Generates the action url for the form
1726         *
1727         * @return action url
1728         */
1729        protected CharSequence getActionUrl()
1730        {
1731                return urlForListener(new PageParameters());
1732        }
1733
1734        /**
1735         * @see org.apache.wicket.Component#renderPlaceholderTag(org.apache.wicket.markup.ComponentTag,
1736         *      org.apache.wicket.request.Response)
1737         */
1738        @Override
1739        protected void renderPlaceholderTag(ComponentTag tag, Response response)
1740        {
1741                if (!isRootForm())
1742                {
1743                        // WICKET-2166
1744                        adjustNestedTagName(tag);
1745                }
1746
1747                super.renderPlaceholderTag(tag, response);
1748        }
1749
1750        /**
1751         * Should URL query parameters be encoded in hidden fields, by default <code>true</code>
1752         * for {@link #METHOD_GET} only.
1753         * <p>
1754         * In that case, the parameters must <em>not</em> be written as query parameters, as the browser
1755         * would strip them from the action url before appending the form values.
1756         *
1757         * @return true if form's method is 'get'
1758         *
1759         * @see #getMethod()
1760         */
1761        protected boolean encodeUrlInHiddenFields()
1762        {
1763                return METHOD_GET.equalsIgnoreCase(getMethod());
1764        }
1765
1766        /**
1767         * Append an additional hidden input tag to support anchor tags that can submit a form.
1768         *
1769         * @param markupStream
1770         *            The markup stream
1771         * @param openTag
1772         *            The open tag for the body
1773         */
1774        @Override
1775        public void onComponentTagBody(final MarkupStream markupStream, final ComponentTag openTag)
1776        {
1777                if (isRootForm())
1778                {
1779                        // get the hidden field id
1780                        writeHiddenFields();
1781                }
1782
1783                // do the rest of the processing
1784                super.onComponentTagBody(markupStream, openTag);
1785        }
1786
1787        @Override
1788        public void renderHead(IHeaderResponse response)
1789        {
1790                super.renderHead(response);
1791
1792                if (hasDefaultSubmittingComponent())
1793                {
1794                        addDefaultSubmitButtonHandler(response);
1795                }
1796        }
1797
1798        /**
1799         * Writes the markup for the hidden input fields and default button field if applicable to the
1800         * current response.
1801         */
1802        public final void writeHiddenFields()
1803        {
1804                getResponse().write(String.format("<div id=\"%s\" hidden=\"\" class=\"%s\">",
1805                        getHiddenFieldsId(HIDDEN_FIELDS_PARAMS_IDX),
1806                        getString(HIDDEN_FIELDS_CSS_CLASS_KEY)));
1807                // if the parameters are not in the action attribute, they have to be written as hidden fields
1808                if (encodeUrlInHiddenFields())
1809                {
1810                        AppendingStringBuffer buffer = new AppendingStringBuffer();
1811
1812                        String url = getActionUrl().toString();
1813                        int i = url.indexOf('?');
1814                        String queryString = (i > -1) ? url.substring(i + 1) : url;
1815                        String[] params = Strings.split(queryString, '&');
1816
1817                        writeParamsAsHiddenFields(params, buffer);
1818
1819                        getResponse().write(buffer);
1820                }
1821                getResponse().write("</div>");
1822
1823                // if a default submitting component was set, handle the rendering of that
1824                if (hasDefaultSubmittingComponent())
1825                {
1826                        appendDefaultButtonField();
1827                }
1828        }
1829
1830        private boolean hasDefaultSubmittingComponent()
1831        {
1832                if (defaultSubmittingComponent instanceof Component)
1833                {
1834                        final Component submittingComponent = (Component) defaultSubmittingComponent;
1835                        return submittingComponent.isVisibleInHierarchy()
1836                                && submittingComponent.isEnabledInHierarchy();
1837                }
1838                return false;
1839        }
1840
1841        /**
1842         *
1843         * @param params
1844         * @param buffer
1845         */
1846        protected void writeParamsAsHiddenFields(String[] params, AppendingStringBuffer buffer)
1847        {
1848                for (String param : params)
1849                {
1850                        String[] pair = Strings.split(param, '=');
1851
1852                        buffer.append("<input type=\"hidden\" name=\"")
1853                                .append(recode(pair[0]))
1854                                .append("\" value=\"")
1855                                .append(pair.length > 1 ? recode(pair[1]) : "")
1856                                .append("\" />");
1857                }
1858        }
1859
1860        /**
1861         * Take URL-encoded query string value, decode it and return HTML-escaped version
1862         *
1863         * @param s
1864         *            value to decode
1865         * @return URL decoded and HTML escaped value
1866         */
1867        private String recode(String s)
1868        {
1869                String un = UrlDecoder.QUERY_INSTANCE.decode(s, getRequest().getCharset());
1870                return Strings.escapeMarkup(un).toString();
1871        }
1872
1873        /**
1874         * @see org.apache.wicket.Component#onDetach()
1875         */
1876        @Override
1877        protected void onDetach()
1878        {
1879                setFlag(FLAG_SUBMITTED, false);
1880
1881                super.onDetach();
1882        }
1883
1884        /**
1885         * Method to override if you want to do something special when an error occurs (other than
1886         * simply displaying validation errors).
1887         */
1888        protected void onError()
1889        {
1890        }
1891
1892        @Override
1893        public void onEvent(IEvent<?> event) {
1894                if (event.getPayload() instanceof AjaxRequestTarget) {
1895                        // WICKET-6171 clear multipart hint, it might change during Ajax requests without this form being rendered
1896                        this.multiPart &= MULTIPART_HARD;
1897                }
1898        }
1899
1900        @Override
1901        protected void onBeforeRender()
1902        {
1903                // clear multipart hint, it will be reevaluated by #isMultiPart()
1904                this.multiPart &= MULTIPART_HARD;
1905
1906                super.onBeforeRender();
1907        }
1908
1909        /**
1910         * Implemented by subclasses to deal with form submits.
1911         */
1912        protected void onSubmit()
1913        {
1914        }
1915
1916        /**
1917         * Update the model of all components on this form and nested forms using the fields that were
1918         * sent with the current request. This method only updates models when the Form.validate() is
1919         * called first that takes care of the conversion for the FormComponents.
1920         *
1921         * Normally this method will not be called when a validation error occurs in one of the form
1922         * components.
1923         *
1924         * @see org.apache.wicket.markup.html.form.FormComponent#updateModel()
1925         */
1926        protected final void updateFormComponentModels()
1927        {
1928                internalUpdateFormComponentModels();
1929                updateNestedFormComponentModels();
1930        }
1931
1932        /**
1933         * Update the model of all components on nested forms.
1934         *
1935         * @see #updateFormComponentModels()
1936         */
1937        private void updateNestedFormComponentModels()
1938        {
1939                visitChildren(Form.class, new IVisitor<Form<?>, Void>()
1940                {
1941                        @Override
1942                        public void component(final Form<?> form, final IVisit<Void> visit)
1943                        {
1944                                if (form.isSubmitted())
1945                                {
1946                                        form.internalUpdateFormComponentModels();
1947                                }
1948                                else
1949                                {
1950                                        visit.dontGoDeeper();
1951                                }
1952                        }
1953                });
1954        }
1955
1956        /**
1957         * Update the model of all components on this form.
1958         *
1959         * @see #updateFormComponentModels()
1960         */
1961        private void internalUpdateFormComponentModels()
1962        {
1963                FormComponent.visitComponentsPostOrder(this, new FormModelUpdateVisitor(this));
1964        }
1965
1966        /**
1967         * Validates the form by checking required fields, converting raw input and running validators
1968         * for every form component, and last running global form validators. This method is typically
1969         * called before updating any models.
1970         * <p>
1971         * NOTE: in most cases, custom validations on the form can be achieved using an IFormValidator
1972         * that can be added using addValidator().
1973         * </p>
1974         */
1975        protected final void validate()
1976        {
1977                // since this method can be called directly by users, this additional check is needed
1978                if (isEnabledInHierarchy() && isVisibleInHierarchy())
1979                {
1980                        validateNestedForms();
1981                        validateComponents();
1982                        validateFormValidators();
1983                        onValidate();
1984                }
1985        }
1986
1987        /**
1988         * Callback during the validation stage of the form
1989         */
1990        protected void onValidate()
1991        {
1992
1993        }
1994
1995        /**
1996         * Calls {@linkplain #onValidateModelObjects()} on this form and all nested forms that are
1997         * visible and enabled
1998         */
1999        private void internalOnValidateModelObjects()
2000        {
2001                onValidateModelObjects();
2002                visitChildren(Form.class, new IVisitor<Form<?>, Void>()
2003                {
2004                        @Override
2005                        public void component(Form<?> form, IVisit<Void> visit)
2006                        {
2007                                if (form.isSubmitted())
2008                                {
2009                                        form.onValidateModelObjects();
2010                                }
2011                                else
2012                                {
2013                                        visit.dontGoDeeper();
2014                                }
2015                        }
2016                });
2017        }
2018
2019        /**
2020         * Called after form components have updated their models. This is a late-stage validation that
2021         * allows outside frameworks to validate any beans that the form is updating.
2022         *
2023         * This validation method is not preferred because at this point any errors will not unroll any
2024         * changes to the model object, so the model object is in a modified state potentially
2025         * containing illegal values. However, with external frameworks there may not be an alternate
2026         * way to validate the model object. A good example of this is a JSR303 Bean Validator
2027         * validating the model object to check any class-level constraints, in order to check such
2028         * constraints the model object must contain the values set by the user.
2029         */
2030        protected void onValidateModelObjects()
2031        {
2032        }
2033
2034        /**
2035         * Triggers type conversion on form components
2036         */
2037        protected final void validateComponents()
2038        {
2039                visitFormComponentsPostOrder(new ValidationVisitor()
2040                {
2041                        @Override
2042                        public void validate(final FormComponent<?> formComponent)
2043                        {
2044                                final Form<?> form = formComponent.getForm();
2045                                if (form == Form.this && form.isEnabledInHierarchy() && form.isVisibleInHierarchy())
2046                                {
2047                                        formComponent.validate();
2048                                }
2049                        }
2050                });
2051        }
2052
2053        /**
2054         * Checks if the specified form component visible and is attached to a page
2055         *
2056         * @param fc
2057         *            form component
2058         *
2059         * @return true if the form component and all its parents are visible and there component is in
2060         *         page's hierarchy
2061         */
2062        private boolean isFormComponentVisibleInPage(FormComponent<?> fc)
2063        {
2064                if (fc == null)
2065                {
2066                        throw new IllegalArgumentException("Argument `fc` cannot be null");
2067                }
2068                return fc.isVisibleInHierarchy();
2069        }
2070
2071
2072        /**
2073         * Validates form with the given form validator
2074         *
2075         * @param validator
2076         */
2077        protected final void validateFormValidator(final IFormValidator validator)
2078        {
2079                Args.notNull(validator, "validator");
2080
2081                final FormComponent<?>[] dependents = validator.getDependentFormComponents();
2082
2083                boolean validate = true;
2084
2085                if (dependents != null)
2086                {
2087                        for (final FormComponent<?> dependent : dependents)
2088                        {
2089                                // check if the dependent component is valid
2090                                if (!dependent.isValid())
2091                                {
2092                                        validate = false;
2093                                        break;
2094                                }
2095                                // check if the dependent component is visible and is attached to
2096                                // the page
2097                                else if (!isFormComponentVisibleInPage(dependent))
2098                                {
2099                                        if (log.isWarnEnabled())
2100                                        {
2101                                                log.warn("IFormValidator in form `" +
2102                                                        getPageRelativePath() +
2103                                                        "` depends on a component that has been removed from the page or is no longer visible. " +
2104                                                        "Offending component id `" + dependent.getId() + "`.");
2105                                        }
2106                                        validate = false;
2107                                        break;
2108                                }
2109                        }
2110                }
2111
2112                if (validate)
2113                {
2114                        validator.validate(this);
2115                }
2116        }
2117
2118        /**
2119         * Triggers any added {@link IFormValidator}s.
2120         */
2121        protected final void validateFormValidators()
2122        {
2123                for (Behavior behavior : getBehaviors())
2124                {
2125                        if (behavior instanceof IFormValidator)
2126                        {
2127                                validateFormValidator((IFormValidator)behavior);
2128                        }
2129                }
2130        }
2131
2132        /**
2133         * Validates {@link FormComponent}s as well as {@link IFormValidator}s in nested {@link Form}s.
2134         *
2135         * @see #validate()
2136         */
2137        private void validateNestedForms()
2138        {
2139                Visits.visitPostOrder(this, new IVisitor<Form<?>, Void>()
2140                {
2141                        @Override
2142                        public void component(final Form<?> form, final IVisit<Void> visit)
2143                        {
2144                                if (form == Form.this)
2145                                {
2146                                        // skip self, only process children
2147                                        visit.stop();
2148                                        return;
2149                                }
2150
2151                                if (form.isSubmitted())
2152                                {
2153                                        form.validateComponents();
2154                                        form.validateFormValidators();
2155                                        form.onValidate();
2156                                }
2157                        }
2158                }, new ClassVisitFilter(Form.class));
2159        }
2160
2161        /**
2162         * Allows to customize input names of form components inside this form.
2163         *
2164         * @return String that well be used as prefix to form component input names
2165         */
2166        protected String getInputNamePrefix()
2167        {
2168                return "";
2169        }
2170
2171        /**
2172         * @param component
2173         * @return The parent form for component
2174         */
2175        public static Form<?> findForm(Component component)
2176        {
2177                return component.findParent(Form.class);
2178        }
2179
2180        /**
2181         * Utility method to assemble an id to distinct form components from different nesting levels.
2182         * Useful to generate input names attributes.
2183         *
2184         * @param component
2185         * @return form relative identification string
2186         */
2187        public static String getRootFormRelativeId(Component component)
2188        {
2189                String id = component.getId();
2190                final PrependingStringBuffer inputName = new PrependingStringBuffer(id.length());
2191                Component c = component;
2192                while (true)
2193                {
2194                        inputName.prepend(id);
2195                        c = c.getParent();
2196                        if (c == null || (c instanceof Form<?> && ((Form<?>)c).isRootForm()) ||
2197                                c instanceof Page)
2198                        {
2199                                break;
2200                        }
2201                        inputName.prepend(Component.PATH_SEPARATOR);
2202                        id = c.getId();
2203                }
2204
2205                /*
2206                 * Certain input names causes problems with JavaScript. If the input name would cause a
2207                 * problem, we create a replacement unique name by prefixing the name with a path that would
2208                 * otherwise never be used (blank id in path).
2209                 *
2210                 * Input names must start with [A-Za-z] according to HTML 4.01 spec. HTML 5 allows almost
2211                 * anything.
2212                 */
2213                if (JavaScriptReservedNames.isNameReserved(inputName.toString()))
2214                {
2215                        inputName.prepend(Component.PATH_SEPARATOR);
2216                        inputName.prepend(Component.PATH_SEPARATOR);
2217                        inputName.prepend("p");
2218                }
2219                return inputName.toString();
2220        }
2221
2222        /**
2223         * Get the request parameters for a form submit,
2224         * according to the request's method or the form's method as fallback.
2225         *
2226         * @param component any component inside the form or the form itself
2227         * @return parameters
2228         */
2229        static IRequestParameters getRequestParameters(Component component) {
2230                String method = Form.METHOD_POST;
2231                final Request request = component.getRequest();
2232                if (request.getContainerRequest() instanceof HttpServletRequest)
2233                {
2234                        method = ((HttpServletRequest)request.getContainerRequest()).getMethod();
2235                }
2236                else
2237                {
2238                        final Form<?> form;
2239                        if (component instanceof Form) {
2240                                form = (Form<?>)component;
2241                        } else {
2242                                form = component.findParent(Form.class);
2243                        }
2244
2245                        if (form != null)
2246                        {
2247                                method = form.getMethod();
2248                        }
2249                }
2250
2251                final IRequestParameters parameters;
2252                switch (method.toLowerCase(Locale.ROOT))
2253                {
2254                        case Form.METHOD_POST:
2255                                parameters = request.getPostParameters();
2256                                break;
2257                        case Form.METHOD_GET:
2258                                parameters = request.getQueryParameters();
2259                                break;
2260                        default:
2261                                parameters = EmptyRequestParameters.INSTANCE;
2262                }
2263
2264                return parameters;
2265        }
2266
2267        /**
2268         * Response when a submission method mismatch is detected
2269         *
2270         * @see Form#getMethod()
2271         *
2272         * @author igor
2273         */
2274        public static enum MethodMismatchResponse {
2275                /**
2276                 * Continue processing.
2277                 */
2278                CONTINUE,
2279
2280                /**
2281                 * Abort processing.
2282                 */
2283                ABORT
2284        }
2285}