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        private static final String UPLOAD_FAILED_RESOURCE_KEY = "uploadFailed";
259
260        private static final String UPLOAD_TOO_LARGE_RESOURCE_KEY = "uploadTooLarge";
261        private static final String UPLOAD_SINGLE_FILE_TOO_LARGE_RESOURCE_KEY = "uploadSingleFileTooLarge";
262        private 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 submited via a javascript submit event, when false via
582         *            the {@code submit()} method.
583         * @return the javascript code that submits the form.
584         *
585         * @see #findSubmitter()
586         */
587        public final CharSequence getJsForSubmitter(IFormSubmittingComponent submitter, boolean triggerEvent)
588        {
589                Form<?> root = getRootForm();
590
591                String param = submitter.getInputName() + "=x";
592
593                AppendingStringBuffer buffer = new AppendingStringBuffer();
594                buffer.append(String.format("var f = document.getElementById('%s');", root.getMarkupId()));
595                buffer.append(String.format("document.getElementById('%s').innerHTML += '",
596                        root.getHiddenFieldsId(HIDDEN_FIELDS_PARAMS_IDX)));
597                writeParamsAsHiddenFields(new String[] {param}, buffer);
598                buffer.append("';");
599
600                if (triggerEvent)
601                {
602                        buffer.append("Wicket.Event.fire(f, 'submit');");
603                }
604                else
605                {
606                        buffer.append("f.submit();");
607                }
608                return buffer;
609        }
610
611        /**
612         * Gets the maximum size for uploads. If null, the setting
613         * {@link org.apache.wicket.settings.ApplicationSettings#getDefaultMaximumUploadSize()} is used.
614         *
615         *
616         * @return the maximum size
617         */
618        public final Bytes getMaxSize()
619        {
620                /*
621                 * NOTE: This method should remain final otherwise it will be impossible to set a default
622                 * max size smaller then the one specified in applications settings because the inner form
623                 * will return the default unless it is specifically set in the traversal. With this method
624                 * remaining final we can tell when the value is explicitly set by the user.
625                 *
626                 * If the value needs to be dynamic it can be set in oncofigure() instead of overriding this
627                 * method.
628                 */
629
630                final Bytes[] maxSize = { this.maxSize };
631                if (maxSize[0] == null)
632                {
633                        visitChildren(Form.class, new IVisitor<Form<?>, Bytes>()
634                        {
635                                @Override
636                                public void component(Form<?> component, IVisit<Bytes> visit)
637                                {
638                                        maxSize[0] = LongValue.maxNullSafe(maxSize[0], component.maxSize);
639                                }
640                        });
641                }
642                if (maxSize[0] == null)
643                {
644                        return getApplication().getApplicationSettings().getDefaultMaximumUploadSize();
645                }
646                return maxSize[0];
647        }
648
649        /**
650         * Gets maximum size for each file of an upload.
651         *
652         * @return
653         */
654        public Bytes getFileMaxSize()
655        {
656                return fileMaxSize;
657        }
658
659        /**
660         * Gets maximum count of files in the form
661         *
662         * @return
663         */
664        public long getFileCountMax()
665        {
666                return fileCountMax;
667        }
668
669        /**
670         * Returns the root form or this, if this is the root form.
671         *
672         * @return root form or this form
673         */
674        public Form<?> getRootForm()
675        {
676                Form<?> form;
677                Form<?> parent = this;
678                do
679                {
680                        form = parent;
681                        parent = form.findParent(Form.class);
682                }
683                while (parent != null);
684
685                return form;
686        }
687
688        /**
689         * Returns the prefix used when building validator keys. This allows a form to use a separate
690         * "set" of keys. For example if prefix "short" is returned, validator key short.Required will
691         * be tried instead of Required key.
692         * <p>
693         * This can be useful when different designs are used for a form. In a form where error messages
694         * are displayed next to their respective form components as opposed to at the top of the form,
695         * the ${label} attribute is of little use and only causes redundant information to appear in
696         * the message. Forms like these can return the "short" (or any other string) validator prefix
697         * and declare key: short.Required=required to override the longer message which is usually
698         * declared like this: Required=${label} is a required field
699         * <p>
700         * Returned prefix will be used for all form components. The prefix can also be overridden on
701         * form component level by overriding {@link FormComponent#getValidatorKeyPrefix()}
702         *
703         * @return prefix prepended to validator keys
704         */
705        public String getValidatorKeyPrefix()
706        {
707                return null;
708        }
709
710        /**
711         * Gets whether the current form has any error registered.
712         *
713         * @return True if this form has at least one error.
714         */
715        public final boolean hasError()
716        {
717                // if this form itself has an error message
718                if (hasErrorMessage())
719                {
720                        return true;
721                }
722
723                // the form doesn't have any errors, now check any nested form
724                // components
725                return anyFormComponentError();
726        }
727
728        /**
729         * Returns whether the form is a root form, which means that there's no other form in it's
730         * parent hierarchy.
731         *
732         * @return true if form is a root form, false otherwise
733         */
734        public boolean isRootForm()
735        {
736                return findParent(Form.class) == null;
737        }
738
739        /**
740         * Checks if this form has been submitted during the current request
741         *
742         * @return true if the form has been submitted during this request, false otherwise
743         */
744        public final boolean isSubmitted()
745        {
746                return getFlag(FLAG_SUBMITTED);
747        }
748
749        /**
750         * THIS METHOD IS NOT PART OF THE WICKET API. DO NOT ATTEMPT TO OVERRIDE OR CALL IT.
751         *
752         * Handles form submissions.
753         *
754         * @see #onFormSubmitted(IFormSubmitter)
755         */
756        @Override
757        public final void onRequest()
758        {
759                onFormSubmitted(null);
760        }
761
762        /**
763         * Called when a form has been submitted using a method differing from return value of
764         * {@link #getMethod()}. For example, someone can copy and paste the action url and invoke the
765         * form using a {@code GET} instead of the desired {@code POST}. This method allows the user to
766         * react to this situation.
767         *
768         * @return response that can either abort or continue the processing of the form
769         */
770        protected MethodMismatchResponse onMethodMismatch()
771        {
772                return MethodMismatchResponse.CONTINUE;
773        }
774
775        /**
776         * THIS METHOD IS NOT PART OF THE WICKET API. DO NOT ATTEMPT TO OVERRIDE OR CALL IT.
777         *
778         * Handles form submissions.
779         *
780         * @param submitter
781         *            listener that will receive form processing events, if {@code null} the form will
782         *            attempt to locate one
783         *
784         * @see Form#validate()
785         */
786        public final void onFormSubmitted(IFormSubmitter submitter)
787        {
788                // check methods match
789                if (getRequest().getContainerRequest() instanceof HttpServletRequest)
790                {
791                        String desiredMethod = getMethod();
792                        String actualMethod = ((HttpServletRequest)getRequest().getContainerRequest()).getMethod();
793                        if (!actualMethod.equalsIgnoreCase(desiredMethod))
794                        {
795                                MethodMismatchResponse response = onMethodMismatch();
796                                switch (response)
797                                {
798                                        case ABORT :
799                                                return;
800                                        case CONTINUE :
801                                                break;
802                                        default :
803                                                throw new IllegalStateException("Invalid " +
804                                                                MethodMismatchResponse.class.getName() + " value: " + response);
805                                }
806                        }
807                }
808
809                markFormsSubmitted(submitter);
810
811                if (handleMultiPart())
812                {
813                        // Tells FormComponents that a new user input has come
814                        inputChanged();
815
816                        // First, see if the processing was triggered by a IFormSubmittingComponent
817                        if (submitter == null)
818                        {
819                                submitter = findSubmitter();
820
821                                if (submitter instanceof IFormSubmittingComponent)
822                                {
823                                        IFormSubmittingComponent submittingComponent = (IFormSubmittingComponent)submitter;
824                                        Component component = (Component)submitter;
825
826                                        if (!component.isVisibleInHierarchy())
827                                        {
828                                                throw new WicketRuntimeException("Submit Button " +
829                                                        submittingComponent.getInputName() + " (path=" +
830                                                        component.getPageRelativePath() + ") is not visible");
831                                        }
832
833                                        if (!component.isEnabledInHierarchy())
834                                        {
835                                                throw new WicketRuntimeException("Submit Button " +
836                                                        submittingComponent.getInputName() + " (path=" +
837                                                        component.getPageRelativePath() + ") is not enabled");
838                                        }
839                                }
840                        }
841
842                        // When processing was triggered by a Wicket IFormSubmittingComponent and that
843                        // component indicates it wants to be called immediately
844                        // (without processing), call the IFormSubmittingComponent.onSubmit* methods right
845                        // away.
846                        if (submitter != null && !submitter.getDefaultFormProcessing())
847                        {
848                                submitter.onSubmit();
849                                submitter.onAfterSubmit();
850                        }
851                        else
852                        {
853                                // the submit request might be for one of the nested forms, so let's
854                                // find the right one:
855                                final Form<?> formToProcess = findFormToProcess(submitter);
856
857                                // process the form for this request
858                                formToProcess.process(submitter);
859                        }
860                }
861                // If multi part did fail check if an error is registered and call
862                // onError
863                else if (hasError())
864                {
865                        callOnError(submitter);
866                }
867
868                // update auto labels if we are inside an ajax request
869                getRequestCycle().find(AjaxRequestTarget.class).ifPresent(target -> {
870                        visitChildren(FormComponent.class, new IVisitor<FormComponent<?>, Void>()
871                        {
872                                @Override
873                                public void component(FormComponent<?> component, IVisit<Void> visit)
874                                {
875                                        component.updateAutoLabels(target);
876                                }
877                        });
878                });
879        }
880
881        /**
882         * This method finds the correct form that should be processed based on the submitting component
883         * (if there is one) and correctly handles nested forms by also looking at
884         * {@link #wantSubmitOnNestedFormSubmit()} throughout the form hierarchy. The form that needs to
885         * be processed is:
886         * <ul>
887         * <li>if there is no submitting component (i.e. a "default submit"): this form.</li>
888         * <li>if only one form exists (this): this form.</li>
889         * <li>if nested forms exist:
890         * <ul>
891         * <li>if the submitting component points at the root form: the root form</li>
892         * <li>if the submitting component points at a nested form:
893         * <ul>
894         * <li>starting at that nested form, the outermost form that returns true for
895         * {@link #wantSubmitOnNestedFormSubmit()}</li>
896         * <li>if no outer form returns true for that, the nested form is returned.</li>
897         * </ul>
898         * </li>
899         * </ul>
900         * </li>
901         * </ul>
902         *
903         * @param submitter
904         *            The submitting component, if any. May be null.
905         * @return The form that needs to be processed.
906         */
907        private Form<?> findFormToProcess(IFormSubmitter submitter)
908        {
909                if (submitter == null)
910                {
911                        // no submitting component => default form submit => so *this* is the
912                        // form to process
913                        return this;
914                }
915                else
916                {
917                        // some button submitted this request, this is the form it belongs to:
918                        final Form<?> targetedForm = submitter.getForm();
919                        if (targetedForm == null)
920                        {
921                                throw new IllegalStateException(
922                                        "submitting component must not return 'null' on getForm()");
923                        }
924
925                        final Form<?> rootForm = getRootForm();
926                        if (targetedForm == rootForm)
927                        {
928                                // the submitting component points at the root form => so let's just go with
929                                // root, everything else will be submitted with it anyway.
930                                return rootForm;
931                        }
932                        else
933                        {
934                                // a different form was targeted. let's find the outermost form that wants to be
935                                // submitted.
936                                Form<?> formThatWantsToBeSubmitted = targetedForm;
937                                Form<?> current = targetedForm.findParent(Form.class);
938                                while (current != null)
939                                {
940                                        if (current.wantSubmitOnNestedFormSubmit())
941                                        {
942                                                formThatWantsToBeSubmitted = current;
943                                        }
944                                        current = current.findParent(Form.class);
945                                }
946                                return formThatWantsToBeSubmitted;
947                        }
948                }
949        }
950
951        /**
952         * Whether this form wants to be submitted too if a nested form is submitted. By default, this
953         * is false, so when a nested form is submitted, this form will <em>not</em> be submitted. If
954         * this method is overridden to return true, this form <em>will</em> be submitted.
955         *
956         * @return Whether this form wants to be submitted too if a nested form is submitted.
957         */
958        // TODO wicket-7 migration guide: changed from public to protected
959        protected boolean wantSubmitOnNestedFormSubmit()
960        {
961                return false;
962        }
963
964        /**
965         * Whether this *nested* form wants to be submitted when parent form is submitted. By default,
966         * this is true, so when a parent form is submitted, the nested form is also submitted. If this
967         * method is overridden to return false, it will not be validated, processed nor submitted.
968         *
969         * @return {@code true} by default
970         */
971        protected boolean wantSubmitOnParentFormSubmit()
972        {
973                return true;
974        }
975
976        /**
977         * Process the form. Though you can override this method to provide your own algorithm, it is
978         * not recommended to do so.
979         *
980         * <p>
981         * See the class documentation for further details on the form processing
982         * </p>
983         *
984         * @param submittingComponent
985         *            component responsible for submitting the form, or <code>null</code> if none (eg
986         *            the form has been submitted via the enter key or javascript calling form.submit())
987         *
988         * @see #delegateSubmit(IFormSubmitter) for an easy way to process submitting component in the
989         *      default manner
990         */
991        public void process(IFormSubmitter submittingComponent)
992        {
993                if (!isEnabledInHierarchy() || !isVisibleInHierarchy())
994                {
995                        // since process() can be called outside of the default form workflow, an additional
996                        // check is needed
997
998                        // FIXME throw listener exception
999                        return;
1000                }
1001
1002                // run validation
1003                validate();
1004
1005                // If a validation error occurred
1006                if (hasError())
1007                {
1008                        // mark all children as invalid
1009                        markFormComponentsInvalid();
1010
1011                        // let subclass handle error
1012                        callOnError(submittingComponent);
1013                }
1014                else
1015                {
1016                        // mark all children as valid
1017                        markFormComponentsValid();
1018
1019                        // before updating, call the interception method for clients
1020                        beforeUpdateFormComponentModels();
1021
1022                        // Update model using form data
1023                        updateFormComponentModels();
1024
1025                        // validate model objects after input values have been bound
1026                        internalOnValidateModelObjects();
1027                        if (hasError())
1028                        {
1029                                callOnError(submittingComponent);
1030                                return;
1031                        }
1032
1033                        // Form has no error
1034                        delegateSubmit(submittingComponent);
1035                }
1036        }
1037
1038        /**
1039         * Calls onError on this {@link Form} and any enabled and visible nested form, if the respective
1040         * {@link Form} actually has errors.
1041         *
1042         * @param submitter
1043         */
1044        protected void callOnError(IFormSubmitter submitter)
1045        {
1046                final Form<?> processingForm = findFormToProcess(submitter);
1047
1048                if (submitter != null)
1049                {
1050                        submitter.onError();
1051                }
1052
1053                // invoke Form#onSubmit(..) going from innermost to outermost
1054                Visits.visitPostOrder(processingForm, new IVisitor<Form<?>, Void>()
1055                {
1056                        @Override
1057                        public void component(Form<?> form, IVisit<Void> visit)
1058                        {
1059                                if (!form.isEnabledInHierarchy() || !form.isVisibleInHierarchy())
1060                                {
1061                                        visit.dontGoDeeper();
1062                                        return;
1063                                }
1064                                if (form.hasError())
1065                                {
1066                                        form.onError();
1067                                }
1068                        }
1069                }, new ClassVisitFilter(Form.class));
1070        }
1071
1072
1073        /**
1074         * Sets FLAG_SUBMITTED to true on this form and every enabled nested form.
1075         * @param submitter
1076         */
1077        private void markFormsSubmitted(IFormSubmitter submitter)
1078        {
1079                setFlag(FLAG_SUBMITTED, true);
1080                Form<?> formToProcess = findFormToProcess(submitter);
1081
1082                visitChildren(Form.class, new IVisitor<Component, Void>()
1083                {
1084                        @Override
1085                        public void component(final Component component, final IVisit<Void> visit)
1086                        {
1087                                Form<?> form = (Form<?>)component;
1088                                if ((form.wantSubmitOnParentFormSubmit() || form == formToProcess)
1089                                        && form.isEnabledInHierarchy() && form.isVisibleInHierarchy())
1090                                {
1091                                        form.setFlag(FLAG_SUBMITTED, true);
1092                                        return;
1093                                }
1094                                visit.dontGoDeeper();
1095                        }
1096                });
1097        }
1098
1099        /**
1100         * Sets the default IFormSubmittingComponent. If set (not null), a hidden submit component will
1101         * be rendered right after the form tag, so that when users press enter in a textfield, this
1102         * submit component's action will be selected. If no default component is set (so unset by
1103         * calling this method with null), nothing additional is rendered.
1104         * <p>
1105         * WARNING: note that this is a best effort only. Unfortunately having a 'default' button in a
1106         * form is ill defined in the standards, and of course IE has it's own way of doing things.
1107         * </p>
1108         * There can be only one default button per form hierarchy. So if you set default button on a
1109         * nested form, it will actually delegate the call to root form. </b>
1110         *
1111         * @param submittingComponent
1112         *            The component to set as the default submitting component, or null when you want to
1113         *            'unset' any previously set default component
1114         */
1115        public final void setDefaultButton(IFormSubmittingComponent submittingComponent)
1116        {
1117                if (isRootForm())
1118                {
1119                        defaultSubmittingComponent = submittingComponent;
1120                }
1121                else
1122                {
1123                        getRootForm().setDefaultButton(submittingComponent);
1124                }
1125        }
1126
1127        /**
1128         * Sets the maximum size for uploads. If null, the setting
1129         * {@link org.apache.wicket.settings.ApplicationSettings#getDefaultMaximumUploadSize()} is used.
1130         *
1131         * @param maxSize
1132         *            The maximum size
1133         */
1134        public void setMaxSize(final Bytes maxSize)
1135        {
1136                this.maxSize = maxSize;
1137        }
1138
1139        /**
1140         * Sets maximum size of each file in upload request.
1141         *
1142         * @param fileMaxSize
1143         */
1144        public void setFileMaxSize(Bytes fileMaxSize)
1145        {
1146                this.fileMaxSize = fileMaxSize;
1147        }
1148
1149        /**
1150         * Sets maximum amount of files in upload request.
1151         *
1152         * @param fileCountMax
1153         */
1154        public void setFileCountMax(long fileCountMax)
1155        {
1156                this.fileCountMax = fileCountMax;
1157        }
1158
1159        /**
1160         * Set to true to use enctype='multipart/form-data', and to process file uploads by default
1161         * multiPart = false
1162         *
1163         * @param multiPart
1164         *            whether this form should behave as a multipart form
1165         */
1166        public void setMultiPart(boolean multiPart)
1167        {
1168                if (multiPart)
1169                {
1170                        this.multiPart |= MULTIPART_HARD;
1171                }
1172                else
1173                {
1174                        this.multiPart &= ~MULTIPART_HARD;
1175                }
1176        }
1177
1178        /**
1179         * @see org.apache.wicket.Component#setVersioned(boolean)
1180         */
1181        @Override
1182        public final Component setVersioned(final boolean isVersioned)
1183        {
1184                super.setVersioned(isVersioned);
1185
1186                // Search for FormComponents like TextField etc.
1187                visitFormComponents(new IVisitor<FormComponent<?>, Void>()
1188                {
1189                        @Override
1190                        public void component(final FormComponent<?> formComponent, IVisit<Void> visit)
1191                        {
1192                                formComponent.setVersioned(isVersioned);
1193                        }
1194                });
1195                return this;
1196        }
1197
1198        /**
1199         * Convenient and typesafe way to visit all the form components on a form.
1200         *
1201         * @param <R>
1202         *            return object type
1203         * @param visitor
1204         *            The visitor interface to call
1205         * @return user provided in callback
1206         */
1207        public final <R> R visitFormComponents(final IVisitor<FormComponent<?>, R> visitor)
1208        {
1209                return visitChildren(FormComponent.class, visitor);
1210        }
1211
1212        /**
1213         * Convenient and typesafe way to visit all the form components on a form postorder (deepest
1214         * first)
1215         *
1216         * @param <R>
1217         *            Return object type
1218         * @param visitor
1219         *            The visitor interface to call
1220         * @return whatever you provided
1221         */
1222        public final <R> R visitFormComponentsPostOrder(
1223                final IVisitor<? extends FormComponent<?>, R> visitor)
1224        {
1225                return FormComponent.visitFormComponentsPostOrder(this, visitor);
1226        }
1227
1228        /**
1229         * Find out whether there is any registered error for a form component.
1230         *
1231         * @return whether there is any registered error for a form component
1232         */
1233        private boolean anyFormComponentError()
1234        {
1235                // Check ALL children for error messages irrespective of FormComponents or not
1236                Boolean error = visitChildren(Component.class, new IVisitor<Component, Boolean>()
1237                {
1238                        @Override
1239                        public void component(final Component component, final IVisit<Boolean> visit)
1240                        {
1241                                if (component.hasErrorMessage() && component.isVisibleInHierarchy() && component.isEnabledInHierarchy())
1242                                {
1243                                        visit.stop(true);
1244                                }
1245                        }
1246                });
1247
1248                return (error != null) && error;
1249        }
1250
1251        /**
1252         * Visits the form's children FormComponents and inform them that a new user input is available
1253         * in the Request
1254         */
1255        private void inputChanged()
1256        {
1257                visitFormComponentsPostOrder(new IVisitor<FormComponent<?>, Void>()
1258                {
1259                        @Override
1260                        public void component(final FormComponent<?> formComponent, IVisit<Void> visit)
1261                        {
1262                                formComponent.inputChanged();
1263                        }
1264                });
1265        }
1266
1267        /**
1268         * If a default IFormSubmittingComponent was set on this form, this method will be called to
1269         * render an extra field with an invisible style so that pressing enter in one of the textfields
1270         * will do a form submit using this component. This method is overridable as what we do is best
1271         * effort only, and may not what you want in specific situations. So if you have specific
1272         * usability concerns, or want to follow another strategy, you may override this method.
1273         *
1274         * @see #addDefaultSubmitButtonHandler(IHeaderResponse)
1275         */
1276        protected void appendDefaultButtonField()
1277        {
1278                AppendingStringBuffer buffer = new AppendingStringBuffer();
1279
1280                // hidden div
1281                buffer.append(String.format("<div hidden=\"\" class=\"%s\">",
1282                        getString(HIDDEN_FIELDS_CSS_CLASS_KEY)));
1283
1284                // add an empty textfield (otherwise IE doesn't work)
1285                buffer.append("<input type=\"text\" tabindex=\"-1\" autocomplete=\"off\"/>");
1286
1287                // add the submitting component
1288                buffer
1289                        .append(String.format("<input id=\"%s\" type=\"submit\" tabindex=\"-1\" name=\"%s\" />",
1290                                getHiddenFieldsId(HIDDEN_FIELDS_SUBMIT_IDX),
1291                                defaultSubmittingComponent.getInputName()));
1292
1293                // close div
1294                buffer.append("</div>");
1295
1296                getResponse().write(buffer);
1297        }
1298
1299        /**
1300         * Where {@link #appendDefaultButtonField()} renders the markup for default submit button
1301         * handling, this method attaches the event handler to its 'click' event. The 'click' event on
1302         * the hidden submit button will be dispatched to the selected default submit button. As with
1303         * {@link #appendDefaultButtonField()} this method can be overridden when the generated code
1304         * needs to be adjusted for a specific usecase.
1305         *
1306         * @param headerResponse
1307         *            The header response.
1308         */
1309        protected void addDefaultSubmitButtonHandler(IHeaderResponse headerResponse)
1310        {
1311                final Component submittingComponent = (Component) defaultSubmittingComponent;
1312                AppendingStringBuffer buffer = new AppendingStringBuffer();
1313                buffer.append("var b=document.getElementById('");
1314                buffer.append(submittingComponent.getMarkupId());
1315                buffer.append("'); if (b!=null && b.onclick!=null && typeof(b.onclick) != 'undefined') ");
1316                buffer.append(
1317                        "{  var r = Wicket.bind(b.onclick, b)(); if (r != false) b.click(); } else { b.click(); };  return false;");
1318                headerResponse.render(OnEventHeaderItem
1319                        .forMarkupId(getHiddenFieldsId(HIDDEN_FIELDS_SUBMIT_IDX), "click", buffer.toString()));
1320        }
1321
1322        /**
1323         * Template method to allow clients to do any processing (like recording the current model so
1324         * that, in case onSubmit does further validation, the model can be rolled back) before the
1325         * actual updating of form component models is done.
1326         */
1327        protected void beforeUpdateFormComponentModels()
1328        {
1329        }
1330
1331        /**
1332         * Called (by the default implementation of 'process') when all fields validated, the form was
1333         * updated and it's data was allowed to be persisted. It is meant for delegating further
1334         * processing to clients.
1335         * <p>
1336         * This implementation first finds out whether the form processing was triggered by a nested
1337         * IFormSubmittingComponent of this form. If that is the case, that component's
1338         * onSubmitBefore/AfterForm methods are called appropriately..
1339         * </p>
1340         * <p>
1341         * Regardless of whether a submitting component was found, the form's onSubmit method is called
1342         * next.
1343         * </p>
1344         *
1345         * @param submittingComponent
1346         *            the component that triggered this form processing, or null if the processing was
1347         *            triggered by something else (like a non-Wicket submit button or a javascript
1348         *            execution)
1349         */
1350        protected void delegateSubmit(IFormSubmitter submittingComponent)
1351        {
1352                final Form<?> processingForm = findFormToProcess(submittingComponent);
1353
1354                // collect all forms innermost to outermost before any hierarchy is changed
1355                final List<Form<?>> forms = Generics.newArrayList(3);
1356                Visits.visitPostOrder(processingForm, new IVisitor<Form<?>, Void>()
1357                {
1358                        @Override
1359                        public void component(Form<?> form, IVisit<Void> visit)
1360                        {
1361                                if (form.isSubmitted())
1362                                {
1363                                        forms.add(form);
1364                                }
1365                        }
1366                }, new ClassVisitFilter(Form.class));
1367
1368                // process submitting component (if specified)
1369                if (submittingComponent != null)
1370                {
1371                        // invoke submit on component
1372                        submittingComponent.onSubmit();
1373                }
1374
1375                // invoke Form#onSubmit(..)
1376                for (Form<?> form : forms)
1377                {
1378                        form.onSubmit();
1379                }
1380
1381                if (submittingComponent != null)
1382                {
1383                        submittingComponent.onAfterSubmit();
1384                }
1385        }
1386
1387        /**
1388         * Returns the id which will be used for the hidden div containing all parameter fields.
1389         *
1390         * @param idx
1391         *            The index of the div to keep different divs apart.
1392         * @return the id of the hidden div
1393         */
1394        private final String getHiddenFieldsId(int idx)
1395        {
1396                return getInputNamePrefix() + getMarkupId() + "_hf_" + idx;
1397        }
1398
1399        /**
1400         * Gets the HTTP submit method that will appear in form markup. If no method is specified in the
1401         * template, "post" is the default. Note that the markup-declared HTTP method may not correspond
1402         * to the one actually used to submit the form; in an Ajax submit, for example, JavaScript event
1403         * handlers may submit the form with a "get" even when the form method is declared as "post."
1404         * Therefore this method should not be considered a guarantee of the HTTP method used, but a
1405         * value for the markup only. Override if you have a requirement to alter this behavior.
1406         *
1407         * @return the submit method specified in markup.
1408         */
1409        protected String getMethod()
1410        {
1411                String method = getMarkupAttributes().getString("method");
1412                return (method != null) ? method : METHOD_POST;
1413        }
1414
1415        /**
1416         *
1417         * @see org.apache.wicket.Component#getStatelessHint()
1418         */
1419        @Override
1420        protected boolean getStatelessHint()
1421        {
1422                return false;
1423        }
1424
1425        /**
1426         * @return True if is multipart
1427         */
1428        public boolean isMultiPart()
1429        {
1430                if (multiPart == 0)
1431                {
1432                        Boolean anyEmbeddedMultipart = visitChildren(Component.class,
1433                                        new IVisitor<Component, Boolean>()
1434                                        {
1435                                                @Override
1436                                                public void component(final Component component, final IVisit<Boolean> visit)
1437                                                {
1438                                                        boolean isMultiPart = false;
1439                                                        if (component instanceof Form<?>)
1440                                                        {
1441                                                                Form<?> form = (Form<?>)component;
1442                                                                if (form.isVisibleInHierarchy() && form.isEnabledInHierarchy())
1443                                                                {
1444                                                                        isMultiPart = (form.multiPart & MULTIPART_HARD) != 0;
1445                                                                }
1446                                                        }
1447                                                        else if (component instanceof FormComponent<?>)
1448                                                        {
1449                                                                FormComponent<?> fc = (FormComponent<?>)component;
1450                                                                if (fc.isVisibleInHierarchy() && fc.isEnabledInHierarchy())
1451                                                                {
1452                                                                        isMultiPart = fc.isMultiPart();
1453                                                                }
1454                                                        }
1455
1456                                                        if (isMultiPart)
1457                                                        {
1458                                                                visit.stop(true);
1459                                                        }
1460                                                }
1461
1462                                        });
1463
1464                        if (Boolean.TRUE.equals(anyEmbeddedMultipart)) {
1465                                multiPart |= MULTIPART_HINT_YES;
1466                        } else {
1467                                multiPart |= MULTIPART_HINT_NO;
1468                        }
1469                }
1470
1471                return (multiPart & (MULTIPART_HARD | MULTIPART_HINT_YES)) != 0;
1472        }
1473
1474        /**
1475         * Handles multi-part processing of the submitted data.
1476         * <strong>WARNING</strong> If this method is overridden it can break {@link FileUploadField}s on this form
1477         *
1478         * @return false if form is multipart and upload failed
1479         */
1480        protected boolean handleMultiPart()
1481        {
1482                if (isMultiPart())
1483                {
1484                        // Change the request to a multipart web request so parameters are
1485                        // parsed out correctly
1486                        try
1487                        {
1488                                ServletWebRequest request = (ServletWebRequest)getRequest();
1489                                final MultipartServletWebRequest multipartWebRequest = request.newMultipartWebRequest(
1490                                        getMaxSize(), getPage().getId());
1491                                multipartWebRequest.setFileMaxSize(getFileMaxSize());
1492                                multipartWebRequest.setFileCountMax(getFileCountMax());
1493                                multipartWebRequest.parseFileParts();
1494
1495                                // TODO: Can't this be detected from header?
1496                                getRequestCycle().setRequest(multipartWebRequest);
1497                        }
1498                        catch (final FileUploadException fux)
1499                        {
1500                                // Create model with exception and maximum size values
1501                                final Map<String, Object> model = new HashMap<>();
1502                                model.put("exception", fux);
1503                                model.put("maxSize", getMaxSize());
1504                                model.put("fileMaxSize", getFileMaxSize());
1505                                model.put("fileCountMax", getFileCountMax());
1506
1507                                onFileUploadException(fux, model);
1508
1509                                // don't process the form if there is a FileUploadException
1510                                return false;
1511                        }
1512                }
1513                return true;
1514        }
1515
1516        /**
1517         * The default message may look like ".. may not exceed 10240 Bytes..". Which is ok, but
1518         * sometimes you may want something like "10KB". By subclassing this method you may replace
1519         * maxSize in the model or add you own property and use that in your error message.
1520         * <p>
1521         * Don't forget to call super.onFileUploadException(e, model) at the end of your method.
1522         *
1523         * @param e
1524         * @param model
1525         */
1526        protected void onFileUploadException(final FileUploadException e,
1527                final Map<String, Object> model)
1528        {
1529                if (e instanceof FileUploadBase.SizeLimitExceededException)
1530                {
1531                        String msg = getString(UPLOAD_TOO_LARGE_RESOURCE_KEY, Model.ofMap(model));
1532                        error(msg);
1533                }
1534                else if (e instanceof FileUploadBase.FileSizeLimitExceededException)
1535                {
1536                        String msg = getString(UPLOAD_SINGLE_FILE_TOO_LARGE_RESOURCE_KEY, Model.ofMap(model));
1537                        error(msg);
1538                }
1539                else if (e instanceof FileCountLimitExceededException)
1540                {
1541                        String msg = getString(UPLOAD_TOO_MANY_FILES_RESOURCE_KEY, Model.ofMap(model));
1542                        error(msg);
1543                }
1544                else
1545                {
1546                        String msg = getString(UPLOAD_FAILED_RESOURCE_KEY, Model.ofMap(model));
1547                        error(msg);
1548
1549                        log.warn(msg, e);
1550                }
1551        }
1552
1553        /**
1554         * @see org.apache.wicket.Component#internalOnModelChanged()
1555         */
1556        @Override
1557        protected void internalOnModelChanged()
1558        {
1559                // Visit all the form components and validate each
1560                visitFormComponentsPostOrder(new IVisitor<FormComponent<?>, Void>()
1561                {
1562                        @Override
1563                        public void component(final FormComponent<?> formComponent, IVisit<Void> visit)
1564                        {
1565                                // If form component is using form model
1566                                if (formComponent.sameInnermostModel(Form.this))
1567                                {
1568                                        formComponent.modelChanged();
1569                                }
1570                        }
1571                });
1572        }
1573
1574        /**
1575         * Mark each form component on this form invalid.
1576         */
1577        protected final void markFormComponentsInvalid()
1578        {
1579                // call invalidate methods of all nested form components
1580                visitFormComponentsPostOrder(new IVisitor<FormComponent<?>, Void>()
1581                {
1582                        @Override
1583                        public void component(final FormComponent<?> formComponent, IVisit<Void> visit)
1584                        {
1585                                if (formComponent.isVisibleInHierarchy())
1586                                {
1587                                        formComponent.invalid();
1588                                }
1589                        }
1590                });
1591        }
1592
1593        /**
1594         * Mark each form component on this form and on nested forms valid.
1595         */
1596        protected final void markFormComponentsValid()
1597        {
1598                internalMarkFormComponentsValid();
1599                markNestedFormComponentsValid();
1600        }
1601
1602        /**
1603         * Mark each form component on nested form valid.
1604         */
1605        private void markNestedFormComponentsValid()
1606        {
1607                visitChildren(Form.class, new IVisitor<Form<?>, Void>()
1608                {
1609                        @Override
1610                        public void component(final Form<?> form, final IVisit<Void> visit)
1611                        {
1612                                if (form.isSubmitted())
1613                                {
1614                                        form.internalMarkFormComponentsValid();
1615                                }
1616                                else
1617                                {
1618                                        visit.dontGoDeeper();
1619                                }
1620                        }
1621                });
1622        }
1623
1624        /**
1625         * Mark each form component on this form valid.
1626         */
1627        private void internalMarkFormComponentsValid()
1628        {
1629                // call valid methods of all nested form components
1630                visitFormComponentsPostOrder(new IVisitor<FormComponent<?>, Void>()
1631                {
1632                        @Override
1633                        public void component(final FormComponent<?> formComponent, IVisit<Void> visit)
1634                        {
1635                                if (formComponent.getForm() == Form.this && formComponent.isVisibleInHierarchy())
1636                                {
1637                                        formComponent.valid();
1638                                }
1639                        }
1640                });
1641        }
1642
1643        /**
1644         * @see org.apache.wicket.Component#onComponentTag(ComponentTag)
1645         */
1646        @Override
1647        protected void onComponentTag(final ComponentTag tag)
1648        {
1649                super.onComponentTag(tag);
1650
1651                if (isRootForm())
1652                {
1653                        checkComponentTag(tag, "form");
1654
1655                        String method = getMethod().toLowerCase(Locale.ROOT);
1656                        tag.put("method", method);
1657                        String url = getActionUrl().toString();
1658                        if (encodeUrlInHiddenFields())
1659                        {
1660                                int i = url.indexOf('?');
1661                                String action = (i > -1) ? url.substring(0, i) : "";
1662                                tag.put("action", action);
1663                                // alternatively, we could just put an empty string here, so
1664                                // that mounted paths stay in good order. I decided against this
1665                                // as I'm not sure whether that could have side effects with
1666                                // other encoders
1667                        }
1668                        else
1669                        {
1670                                tag.put("action", url);
1671                        }
1672
1673                        if (isMultiPart())
1674                        {
1675                                if (METHOD_GET.equalsIgnoreCase(method))
1676                                {
1677                                        log.warn("Form with id '{}' is multipart. It should use method 'POST'!",
1678                                                getId());
1679                                        tag.put("method", METHOD_POST.toLowerCase(Locale.ROOT));
1680                                }
1681
1682                                tag.put("enctype", ENCTYPE_MULTIPART_FORM_DATA);
1683                                //
1684                                // require the application-encoding for multipart/form-data to be sure to
1685                                // get multipart-uploaded characters with the proper encoding on the following
1686                                // request.
1687                                //
1688                                // for details see: http://stackoverflow.com/questions/546365
1689                                //
1690                                tag.put("accept-charset", getApplication().getRequestCycleSettings()
1691                                        .getResponseRequestEncoding());
1692                        }
1693                        else
1694                        {
1695                                // sanity check
1696                                String enctype = (String)tag.getAttributes().get("enctype");
1697                                if (ENCTYPE_MULTIPART_FORM_DATA.equalsIgnoreCase(enctype))
1698                                {
1699                                        // though not set explicitly in Java, this is a multipart
1700                                        // form
1701                                        setMultiPart(true);
1702                                }
1703                        }
1704                }
1705                else
1706                {
1707                        adjustNestedTagName(tag);
1708                        tag.remove("method");
1709                        tag.remove("action");
1710                        tag.remove("enctype");
1711                }
1712        }
1713
1714        // WICKET-6658 form is not allowed, anything else can stay as is
1715        private void adjustNestedTagName(ComponentTag tag) {
1716                if ("form".equalsIgnoreCase(tag.getName()))
1717                {
1718                        tag.setName("div");
1719                }
1720        }
1721
1722        /**
1723         * Generates the action url for the form
1724         *
1725         * @return action url
1726         */
1727        protected CharSequence getActionUrl()
1728        {
1729                return urlForListener(new PageParameters());
1730        }
1731
1732        /**
1733         * @see org.apache.wicket.Component#renderPlaceholderTag(org.apache.wicket.markup.ComponentTag,
1734         *      org.apache.wicket.request.Response)
1735         */
1736        @Override
1737        protected void renderPlaceholderTag(ComponentTag tag, Response response)
1738        {
1739                if (!isRootForm())
1740                {
1741                        // WICKET-2166
1742                        adjustNestedTagName(tag);
1743                }
1744
1745                super.renderPlaceholderTag(tag, response);
1746        }
1747
1748        /**
1749         * Should URL query parameters be encoded in hidden fields, by default <code>true</code>
1750         * for {@link #METHOD_GET} only.
1751         * <p>
1752         * In that case, the parameters must <em>not</em> be written as query parameters, as the browser
1753         * would strip them from the action url before appending the form values.
1754         *
1755         * @return true if form's method is 'get'
1756         *
1757         * @see #getMethod()
1758         */
1759        protected boolean encodeUrlInHiddenFields()
1760        {
1761                return METHOD_GET.equalsIgnoreCase(getMethod());
1762        }
1763
1764        /**
1765         * Append an additional hidden input tag to support anchor tags that can submit a form.
1766         *
1767         * @param markupStream
1768         *            The markup stream
1769         * @param openTag
1770         *            The open tag for the body
1771         */
1772        @Override
1773        public void onComponentTagBody(final MarkupStream markupStream, final ComponentTag openTag)
1774        {
1775                if (isRootForm())
1776                {
1777                        // get the hidden field id
1778                        writeHiddenFields();
1779                }
1780
1781                // do the rest of the processing
1782                super.onComponentTagBody(markupStream, openTag);
1783        }
1784
1785        @Override
1786        public void renderHead(IHeaderResponse response)
1787        {
1788                super.renderHead(response);
1789
1790                if (hasDefaultSubmittingComponent())
1791                {
1792                        addDefaultSubmitButtonHandler(response);
1793                }
1794        }
1795
1796        /**
1797         * Writes the markup for the hidden input fields and default button field if applicable to the
1798         * current response.
1799         */
1800        public final void writeHiddenFields()
1801        {
1802                getResponse().write(String.format("<div id=\"%s\" hidden=\"\" class=\"%s\">",
1803                        getHiddenFieldsId(HIDDEN_FIELDS_PARAMS_IDX),
1804                        getString(HIDDEN_FIELDS_CSS_CLASS_KEY)));
1805                // if the parameters are not in the action attribute, they have to be written as hidden fields
1806                if (encodeUrlInHiddenFields())
1807                {
1808                        AppendingStringBuffer buffer = new AppendingStringBuffer();
1809
1810                        String url = getActionUrl().toString();
1811                        int i = url.indexOf('?');
1812                        String queryString = (i > -1) ? url.substring(i + 1) : url;
1813                        String[] params = Strings.split(queryString, '&');
1814
1815                        writeParamsAsHiddenFields(params, buffer);
1816
1817                        getResponse().write(buffer);
1818                }
1819                getResponse().write("</div>");
1820
1821                // if a default submitting component was set, handle the rendering of that
1822                if (hasDefaultSubmittingComponent())
1823                {
1824                        appendDefaultButtonField();
1825                }
1826        }
1827
1828        private boolean hasDefaultSubmittingComponent()
1829        {
1830                if (defaultSubmittingComponent instanceof Component)
1831                {
1832                        final Component submittingComponent = (Component) defaultSubmittingComponent;
1833                        return submittingComponent.isVisibleInHierarchy()
1834                                && submittingComponent.isEnabledInHierarchy();
1835                }
1836                return false;
1837        }
1838
1839        /**
1840         *
1841         * @param params
1842         * @param buffer
1843         */
1844        protected void writeParamsAsHiddenFields(String[] params, AppendingStringBuffer buffer)
1845        {
1846                for (String param : params)
1847                {
1848                        String[] pair = Strings.split(param, '=');
1849
1850                        buffer.append("<input type=\"hidden\" name=\"")
1851                                .append(recode(pair[0]))
1852                                .append("\" value=\"")
1853                                .append(pair.length > 1 ? recode(pair[1]) : "")
1854                                .append("\" />");
1855                }
1856        }
1857
1858        /**
1859         * Take URL-encoded query string value, decode it and return HTML-escaped version
1860         *
1861         * @param s
1862         *            value to decode
1863         * @return URL decoded and HTML escaped value
1864         */
1865        private String recode(String s)
1866        {
1867                String un = UrlDecoder.QUERY_INSTANCE.decode(s, getRequest().getCharset());
1868                return Strings.escapeMarkup(un).toString();
1869        }
1870
1871        /**
1872         * @see org.apache.wicket.Component#onDetach()
1873         */
1874        @Override
1875        protected void onDetach()
1876        {
1877                setFlag(FLAG_SUBMITTED, false);
1878
1879                super.onDetach();
1880        }
1881
1882        /**
1883         * Method to override if you want to do something special when an error occurs (other than
1884         * simply displaying validation errors).
1885         */
1886        protected void onError()
1887        {
1888        }
1889
1890        @Override
1891        public void onEvent(IEvent<?> event) {
1892                if (event.getPayload() instanceof AjaxRequestTarget) {
1893                        // WICKET-6171 clear multipart hint, it might change during Ajax requests without this form being rendered
1894                        this.multiPart &= MULTIPART_HARD;
1895                }
1896        }
1897
1898        @Override
1899        protected void onBeforeRender()
1900        {
1901                // clear multipart hint, it will be reevaluated by #isMultiPart()
1902                this.multiPart &= MULTIPART_HARD;
1903
1904                super.onBeforeRender();
1905        }
1906
1907        /**
1908         * Implemented by subclasses to deal with form submits.
1909         */
1910        protected void onSubmit()
1911        {
1912        }
1913
1914        /**
1915         * Update the model of all components on this form and nested forms using the fields that were
1916         * sent with the current request. This method only updates models when the Form.validate() is
1917         * called first that takes care of the conversion for the FormComponents.
1918         *
1919         * Normally this method will not be called when a validation error occurs in one of the form
1920         * components.
1921         *
1922         * @see org.apache.wicket.markup.html.form.FormComponent#updateModel()
1923         */
1924        protected final void updateFormComponentModels()
1925        {
1926                internalUpdateFormComponentModels();
1927                updateNestedFormComponentModels();
1928        }
1929
1930        /**
1931         * Update the model of all components on nested forms.
1932         *
1933         * @see #updateFormComponentModels()
1934         */
1935        private void updateNestedFormComponentModels()
1936        {
1937                visitChildren(Form.class, new IVisitor<Form<?>, Void>()
1938                {
1939                        @Override
1940                        public void component(final Form<?> form, final IVisit<Void> visit)
1941                        {
1942                                if (form.isSubmitted())
1943                                {
1944                                        form.internalUpdateFormComponentModels();
1945                                }
1946                                else
1947                                {
1948                                        visit.dontGoDeeper();
1949                                }
1950                        }
1951                });
1952        }
1953
1954        /**
1955         * Update the model of all components on this form.
1956         *
1957         * @see #updateFormComponentModels()
1958         */
1959        private void internalUpdateFormComponentModels()
1960        {
1961                FormComponent.visitComponentsPostOrder(this, new FormModelUpdateVisitor(this));
1962        }
1963
1964        /**
1965         * Validates the form by checking required fields, converting raw input and running validators
1966         * for every form component, and last running global form validators. This method is typically
1967         * called before updating any models.
1968         * <p>
1969         * NOTE: in most cases, custom validations on the form can be achieved using an IFormValidator
1970         * that can be added using addValidator().
1971         * </p>
1972         */
1973        protected final void validate()
1974        {
1975                // since this method can be called directly by users, this additional check is needed
1976                if (isEnabledInHierarchy() && isVisibleInHierarchy())
1977                {
1978                        validateNestedForms();
1979                        validateComponents();
1980                        validateFormValidators();
1981                        onValidate();
1982                }
1983        }
1984
1985        /**
1986         * Callback during the validation stage of the form
1987         */
1988        protected void onValidate()
1989        {
1990
1991        }
1992
1993        /**
1994         * Calls {@linkplain #onValidateModelObjects()} on this form and all nested forms that are
1995         * visible and enabled
1996         */
1997        private void internalOnValidateModelObjects()
1998        {
1999                onValidateModelObjects();
2000                visitChildren(Form.class, new IVisitor<Form<?>, Void>()
2001                {
2002                        @Override
2003                        public void component(Form<?> form, IVisit<Void> visit)
2004                        {
2005                                if (form.isSubmitted())
2006                                {
2007                                        form.onValidateModelObjects();
2008                                }
2009                                else
2010                                {
2011                                        visit.dontGoDeeper();
2012                                }
2013                        }
2014                });
2015        }
2016
2017        /**
2018         * Called after form components have updated their models. This is a late-stage validation that
2019         * allows outside frameworks to validate any beans that the form is updating.
2020         *
2021         * This validation method is not preferred because at this point any errors will not unroll any
2022         * changes to the model object, so the model object is in a modified state potentially
2023         * containing illegal values. However, with external frameworks there may not be an alternate
2024         * way to validate the model object. A good example of this is a JSR303 Bean Validator
2025         * validating the model object to check any class-level constraints, in order to check such
2026         * constraints the model object must contain the values set by the user.
2027         */
2028        protected void onValidateModelObjects()
2029        {
2030        }
2031
2032        /**
2033         * Triggers type conversion on form components
2034         */
2035        protected final void validateComponents()
2036        {
2037                visitFormComponentsPostOrder(new ValidationVisitor()
2038                {
2039                        @Override
2040                        public void validate(final FormComponent<?> formComponent)
2041                        {
2042                                final Form<?> form = formComponent.getForm();
2043                                if (form == Form.this && form.isEnabledInHierarchy() && form.isVisibleInHierarchy())
2044                                {
2045                                        formComponent.validate();
2046                                }
2047                        }
2048                });
2049        }
2050
2051        /**
2052         * Checks if the specified form component visible and is attached to a page
2053         *
2054         * @param fc
2055         *            form component
2056         *
2057         * @return true if the form component and all its parents are visible and there component is in
2058         *         page's hierarchy
2059         */
2060        private boolean isFormComponentVisibleInPage(FormComponent<?> fc)
2061        {
2062                if (fc == null)
2063                {
2064                        throw new IllegalArgumentException("Argument `fc` cannot be null");
2065                }
2066                return fc.isVisibleInHierarchy();
2067        }
2068
2069
2070        /**
2071         * Validates form with the given form validator
2072         *
2073         * @param validator
2074         */
2075        protected final void validateFormValidator(final IFormValidator validator)
2076        {
2077                Args.notNull(validator, "validator");
2078
2079                final FormComponent<?>[] dependents = validator.getDependentFormComponents();
2080
2081                boolean validate = true;
2082
2083                if (dependents != null)
2084                {
2085                        for (final FormComponent<?> dependent : dependents)
2086                        {
2087                                // check if the dependent component is valid
2088                                if (!dependent.isValid())
2089                                {
2090                                        validate = false;
2091                                        break;
2092                                }
2093                                // check if the dependent component is visible and is attached to
2094                                // the page
2095                                else if (!isFormComponentVisibleInPage(dependent))
2096                                {
2097                                        if (log.isWarnEnabled())
2098                                        {
2099                                                log.warn("IFormValidator in form `" +
2100                                                        getPageRelativePath() +
2101                                                        "` depends on a component that has been removed from the page or is no longer visible. " +
2102                                                        "Offending component id `" + dependent.getId() + "`.");
2103                                        }
2104                                        validate = false;
2105                                        break;
2106                                }
2107                        }
2108                }
2109
2110                if (validate)
2111                {
2112                        validator.validate(this);
2113                }
2114        }
2115
2116        /**
2117         * Triggers any added {@link IFormValidator}s.
2118         */
2119        protected final void validateFormValidators()
2120        {
2121                for (Behavior behavior : getBehaviors())
2122                {
2123                        if (behavior instanceof IFormValidator)
2124                        {
2125                                validateFormValidator((IFormValidator)behavior);
2126                        }
2127                }
2128        }
2129
2130        /**
2131         * Validates {@link FormComponent}s as well as {@link IFormValidator}s in nested {@link Form}s.
2132         *
2133         * @see #validate()
2134         */
2135        private void validateNestedForms()
2136        {
2137                Visits.visitPostOrder(this, new IVisitor<Form<?>, Void>()
2138                {
2139                        @Override
2140                        public void component(final Form<?> form, final IVisit<Void> visit)
2141                        {
2142                                if (form == Form.this)
2143                                {
2144                                        // skip self, only process children
2145                                        visit.stop();
2146                                        return;
2147                                }
2148
2149                                if (form.isSubmitted())
2150                                {
2151                                        form.validateComponents();
2152                                        form.validateFormValidators();
2153                                        form.onValidate();
2154                                }
2155                        }
2156                }, new ClassVisitFilter(Form.class));
2157        }
2158
2159        /**
2160         * Allows to customize input names of form components inside this form.
2161         *
2162         * @return String that well be used as prefix to form component input names
2163         */
2164        protected String getInputNamePrefix()
2165        {
2166                return "";
2167        }
2168
2169        /**
2170         * @param component
2171         * @return The parent form for component
2172         */
2173        public static Form<?> findForm(Component component)
2174        {
2175                return component.findParent(Form.class);
2176        }
2177
2178        /**
2179         * Utility method to assemble an id to distinct form components from different nesting levels.
2180         * Useful to generate input names attributes.
2181         *
2182         * @param component
2183         * @return form relative identification string
2184         */
2185        public static String getRootFormRelativeId(Component component)
2186        {
2187                String id = component.getId();
2188                final PrependingStringBuffer inputName = new PrependingStringBuffer(id.length());
2189                Component c = component;
2190                while (true)
2191                {
2192                        inputName.prepend(id);
2193                        c = c.getParent();
2194                        if (c == null || (c instanceof Form<?> && ((Form<?>)c).isRootForm()) ||
2195                                c instanceof Page)
2196                        {
2197                                break;
2198                        }
2199                        inputName.prepend(Component.PATH_SEPARATOR);
2200                        id = c.getId();
2201                }
2202
2203                /*
2204                 * Certain input names causes problems with JavaScript. If the input name would cause a
2205                 * problem, we create a replacement unique name by prefixing the name with a path that would
2206                 * otherwise never be used (blank id in path).
2207                 *
2208                 * Input names must start with [A-Za-z] according to HTML 4.01 spec. HTML 5 allows almost
2209                 * anything.
2210                 */
2211                if (JavaScriptReservedNames.isNameReserved(inputName.toString()))
2212                {
2213                        inputName.prepend(Component.PATH_SEPARATOR);
2214                        inputName.prepend(Component.PATH_SEPARATOR);
2215                        inputName.prepend("p");
2216                }
2217                return inputName.toString();
2218        }
2219
2220        /**
2221         * Get the request parameters for a form submit,
2222         * according to the request's method or the form's method as fallback.
2223         *
2224         * @param component any component inside the form or the form itself
2225         * @return parameters
2226         */
2227        static IRequestParameters getRequestParameters(Component component) {
2228                String method = Form.METHOD_POST;
2229                final Request request = component.getRequest();
2230                if (request.getContainerRequest() instanceof HttpServletRequest)
2231                {
2232                        method = ((HttpServletRequest)request.getContainerRequest()).getMethod();
2233                }
2234                else
2235                {
2236                        final Form<?> form;
2237                        if (component instanceof Form) {
2238                                form = (Form<?>)component;
2239                        } else {
2240                                form = component.findParent(Form.class);
2241                        }
2242
2243                        if (form != null)
2244                        {
2245                                method = form.getMethod();
2246                        }
2247                }
2248
2249                final IRequestParameters parameters;
2250                switch (method.toLowerCase(Locale.ROOT))
2251                {
2252                        case Form.METHOD_POST:
2253                                parameters = request.getPostParameters();
2254                                break;
2255                        case Form.METHOD_GET:
2256                                parameters = request.getQueryParameters();
2257                                break;
2258                        default:
2259                                parameters = EmptyRequestParameters.INSTANCE;
2260                }
2261
2262                return parameters;
2263        }
2264
2265        /**
2266         * Response when a submission method mismatch is detected
2267         *
2268         * @see Form#getMethod()
2269         *
2270         * @author igor
2271         */
2272        public static enum MethodMismatchResponse {
2273                /**
2274                 * Continue processing.
2275                 */
2276                CONTINUE,
2277
2278                /**
2279                 * Abort processing.
2280                 */
2281                ABORT
2282        }
2283}