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