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