• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

hazendaz / httpunit / 755

14 Feb 2026 07:14PM UTC coverage: 80.526%. Remained the same
755

push

github

hazendaz
[ci] Fix badge

3213 of 4105 branches covered (78.27%)

Branch coverage included in aggregate %.

8245 of 10124 relevant lines covered (81.44%)

0.81 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

88.84
/src/main/java/com/meterware/httpunit/WebForm.java
1
/*
2
 * SPDX-License-Identifier: MIT
3
 * See LICENSE file for details.
4
 *
5
 * Copyright 2000-2026 Russell Gold
6
 * Copyright 2021-2000 hazendaz
7
 */
8
package com.meterware.httpunit;
9

10
import com.meterware.httpunit.protocol.ParameterProcessor;
11
import com.meterware.httpunit.protocol.UploadFileSpec;
12
import com.meterware.httpunit.scripting.FormScriptable;
13
import com.meterware.httpunit.scripting.IdentifiedDelegate;
14
import com.meterware.httpunit.scripting.NamedDelegate;
15
import com.meterware.httpunit.scripting.ScriptableDelegate;
16

17
import java.io.File;
18
import java.io.IOException;
19
import java.net.URL;
20
import java.net.UnknownServiceException;
21
import java.util.ArrayList;
22
import java.util.HashMap;
23
import java.util.List;
24
import java.util.Map;
25

26
import org.w3c.dom.Node;
27
import org.w3c.dom.html.HTMLCollection;
28
import org.w3c.dom.html.HTMLFormElement;
29
import org.xml.sax.SAXException;
30

31
/**
32
 * This class represents a form in an HTML page. Users of this class may examine the parameters defined for the form,
33
 * the structure of the form (as a DOM), or the text of the form. They may also create a {@link WebRequest} to simulate
34
 * the submission of the form.
35
 **/
36
public class WebForm extends WebRequestSource {
37

38
    /** The Constant NO_VALUES. */
39
    private static final String[] NO_VALUES = {};
1✔
40

41
    /** The buttons. */
42
    private Button[] _buttons;
43

44
    /** The submit buttons in this form. **/
45
    private SubmitButton[] _submitButtons;
46

47
    /** The character set in which the form will be submitted. **/
48
    private String _characterSet;
49

50
    /** The button list. */
51
    private List<SubmitButton> _buttonVector;
52

53
    /** The preset parameters. */
54
    private FormControl[] _presetParameters;
55

56
    /** The presets. */
57
    private ArrayList _presets;
58

59
    /** The registry. */
60
    private ElementRegistry _registry;
61

62
    /** Predicate to match a link's name. **/
63
    public static final HTMLElementPredicate MATCH_NAME;
64

65
    /** The dom element. */
66
    private HTMLFormElement _domElement;
67

68
    /**
69
     * Submits this form using the web client from which it was originally obtained.
70
     *
71
     * @return the web response
72
     *
73
     * @throws IOException
74
     *             Signals that an I/O exception has occurred.
75
     * @throws SAXException
76
     *             the SAX exception
77
     */
78
    public WebResponse submit() throws IOException, SAXException {
79
        return submit(getDefaultButton());
1✔
80
    }
81

82
    /**
83
     * Submits this form using the web client from which it was originally obtained. Will usually return the result of
84
     * that submission; however, if the submit button's 'onclick' or the form's 'onsubmit' event is triggered and
85
     * inhibits the submission, will return the updated contents of the frame containing this form.
86
     *
87
     * @param button
88
     *            the button
89
     *
90
     * @return the web response
91
     *
92
     * @throws IOException
93
     *             Signals that an I/O exception has occurred.
94
     * @throws SAXException
95
     *             the SAX exception
96
     */
97
    public WebResponse submit(SubmitButton button) throws IOException, SAXException {
98
        return submit(button, 0, 0);
1✔
99
    }
100

101
    /**
102
     * Submits this form using the web client from which it was originally obtained. Will usually return the result of
103
     * that submission; however, if the submit button's 'onclick' or the form's 'onsubmit' event is triggered and
104
     * inhibits the submission, will return the updated contents of the frame containing this form.
105
     *
106
     * @param button
107
     *            the button
108
     * @param x
109
     *            the x
110
     * @param y
111
     *            the y
112
     *
113
     * @return the web response
114
     *
115
     * @throws IOException
116
     *             Signals that an I/O exception has occurred.
117
     * @throws SAXException
118
     *             the SAX exception
119
     */
120
    public WebResponse submit(SubmitButton button, int x, int y) throws IOException, SAXException {
121
        if (button == null) {
1✔
122
            throw new IllegalSubmitButtonException("?", "?");
1✔
123
        }
124
        button.doOnClickSequence(x, y);
1✔
125
        return getCurrentFrameContents();
1✔
126
    }
127

128
    /**
129
     * Submits this form using the web client from which it was originally obtained, ignoring any buttons defined for
130
     * the form.
131
     *
132
     * @return the web response
133
     *
134
     * @throws SAXException
135
     *             the SAX exception
136
     * @throws IOException
137
     *             Signals that an I/O exception has occurred.
138
     */
139
    public WebResponse submitNoButton() throws SAXException, IOException {
140
        return submit(SubmitButton.createFakeSubmitButton(this /* fake */));
1✔
141
    }
142

143
    @Override
144
    protected WebResponse submitRequest(String event, WebRequest request) throws IOException, SAXException {
145
        try {
146
            return super.submitRequest(event, request);
1✔
147
        } catch (UnknownServiceException e) {
1✔
148
            throw new UnsupportedActionException(
1✔
149
                    "HttpUnit does not support " + request.getURL().getProtocol() + " URLs in form submissions");
1✔
150
        }
151
    }
152

153
    /**
154
     * Submits the form without also invoking the button's "onclick" event.
155
     *
156
     * @param button
157
     *            the button
158
     *
159
     * @return the web response
160
     *
161
     * @throws IOException
162
     *             Signals that an I/O exception has occurred.
163
     * @throws SAXException
164
     *             the SAX exception
165
     */
166
    WebResponse doFormSubmit(SubmitButton button) throws IOException, SAXException {
167
        return submitRequest(getAttribute("onsubmit"), getRequest(button));
×
168
    }
169

170
    /**
171
     * Do form submit.
172
     *
173
     * @param button
174
     *            the button
175
     * @param x
176
     *            the x
177
     * @param y
178
     *            the y
179
     *
180
     * @return the web response
181
     *
182
     * @throws IOException
183
     *             Signals that an I/O exception has occurred.
184
     * @throws SAXException
185
     *             the SAX exception
186
     */
187
    WebResponse doFormSubmit(SubmitButton button, int x, int y) throws IOException, SAXException {
188
        return submitRequest(getAttribute("onsubmit"), getRequest(button, x, y));
1✔
189
    }
190

191
    /**
192
     * Returns the method defined for this form.
193
     *
194
     * @return the method
195
     */
196
    public String getMethod() {
197
        return getAttribute("method", "GET");
1✔
198
    }
199

200
    /**
201
     * Returns the action defined for this form.
202
     *
203
     * @return the action
204
     */
205
    public String getAction() {
206
        return getDestination();
1✔
207
    }
208

209
    /**
210
     * Returns true if a parameter with given name exists in this form.
211
     *
212
     * @param soughtName
213
     *            the sought name
214
     *
215
     * @return true, if successful
216
     */
217
    public boolean hasParameterNamed(String soughtName) {
218
        return getFormParameters().containsKey(soughtName);
1✔
219
    }
220

221
    /**
222
     * Returns true if a parameter starting with a given name exists,.
223
     *
224
     * @param prefix
225
     *            the prefix
226
     *
227
     * @return true, if successful
228
     */
229
    public boolean hasParameterStartingWithPrefix(String prefix) {
230
        String[] names = getParameterNames();
1✔
231
        for (String name : names) {
1!
232
            if (name.startsWith(prefix)) {
1✔
233
                return true;
1✔
234
            }
235
        }
236
        return false;
×
237
    }
238

239
    /**
240
     * Returns an array containing all of the buttons defined for this form.
241
     *
242
     * @return the buttons
243
     */
244
    public Button[] getButtons() {
245
        if (_buttons == null) {
1✔
246
            FormControl[] controls = getFormControls();
1✔
247
            List<FormControl> buttonList = new ArrayList<>();
1✔
248
            for (FormControl control : controls) {
1✔
249
                if (control instanceof Button) {
1✔
250
                    buttonList.add(control);
1✔
251
                }
252
            }
253
            _buttons = (Button[]) buttonList.toArray(new Button[buttonList.size()]);
1✔
254
        }
255
        return _buttons;
1✔
256
    }
257

258
    /**
259
     * Gets the button.
260
     *
261
     * @param predicate
262
     *            the predicate
263
     * @param criteria
264
     *            the criteria
265
     *
266
     * @return the button
267
     */
268
    public Button getButton(HTMLElementPredicate predicate, Object criteria) {
269
        Button[] buttons = getButtons();
1✔
270
        for (Button button : buttons) {
1!
271
            if (predicate.matchesCriteria(button, criteria)) {
1✔
272
                return button;
1✔
273
            }
274
        }
275
        return null;
×
276
    }
277

278
    /**
279
     * Convenience method which returns the button with the specified ID.
280
     *
281
     * @param buttonID
282
     *            the button ID
283
     *
284
     * @return the button with ID
285
     */
286
    public Button getButtonWithID(String buttonID) {
287
        return getButton(Button.WITH_ID, buttonID);
1✔
288
    }
289

290
    /**
291
     * Returns an array containing the submit buttons defined for this form.
292
     *
293
     * @return the submit buttons
294
     */
295
    public SubmitButton[] getSubmitButtons() {
296
        if (_submitButtons == null) {
1✔
297
            List<SubmitButton> buttons = getSubmitButtonVector();
1✔
298
            _submitButtons = buttons.toArray(new SubmitButton[buttons.size()]);
1✔
299
        }
300
        return _submitButtons;
1✔
301
    }
302

303
    /**
304
     * Returns the submit button defined in this form with the specified name. If more than one such button exists, will
305
     * return the first found. If no such button is found, will return null.
306
     *
307
     * @param name
308
     *            the name
309
     *
310
     * @return the submit button
311
     */
312
    public SubmitButton getSubmitButton(String name) {
313
        SubmitButton[] buttons = getSubmitButtons();
1✔
314
        for (SubmitButton button : buttons) {
1✔
315
            if (button.getName().equals(name)) {
1✔
316
                return button;
1✔
317
            }
318
        }
319
        return null;
1✔
320
    }
321

322
    /**
323
     * Returns the submit button defined in this form with the specified name and value. If more than one such button
324
     * exists, will return the first found. If no such button is found, will return null.
325
     *
326
     * @param name
327
     *            the name
328
     * @param value
329
     *            the value
330
     *
331
     * @return the submit button
332
     */
333
    public SubmitButton getSubmitButton(String name, String value) {
334
        SubmitButton[] buttons = getSubmitButtons();
1✔
335
        for (SubmitButton button : buttons) {
1!
336
            if (button.getName().equals(name) && button.getValue().equals(value)) {
1!
337
                return button;
1✔
338
            }
339
        }
340
        return null;
×
341
    }
342

343
    /**
344
     * Returns the submit button defined in this form with the specified ID. If more than one such button exists, will
345
     * return the first found. If no such button is found, will return null.
346
     *
347
     * @param ID
348
     *            the id
349
     *
350
     * @return the submit button with ID
351
     */
352
    public SubmitButton getSubmitButtonWithID(String ID) {
353
        SubmitButton[] buttons = getSubmitButtons();
1✔
354
        for (SubmitButton button : buttons) {
1!
355
            if (button.getID().equals(ID)) {
1!
356
                return button;
1✔
357
            }
358
        }
359
        return null;
×
360
    }
361

362
    /**
363
     * Creates and returns a web request which will simulate the submission of this form with a button with the
364
     * specified name and value.
365
     *
366
     * @param submitButtonName
367
     *            the submit button name
368
     * @param submitButtonValue
369
     *            the submit button value
370
     *
371
     * @return the request
372
     */
373
    public WebRequest getRequest(String submitButtonName, String submitButtonValue) {
374
        SubmitButton sb = getSubmitButton(submitButtonName, submitButtonValue);
1✔
375
        if (sb == null) {
1!
376
            throw new IllegalSubmitButtonException(submitButtonName, submitButtonValue);
×
377
        }
378
        return getRequest(sb);
1✔
379
    }
380

381
    /**
382
     * Creates and returns a web request which will simulate the submission of this form with a button with the
383
     * specified name.
384
     *
385
     * @param submitButtonName
386
     *            the submit button name
387
     *
388
     * @return the request
389
     */
390
    public WebRequest getRequest(String submitButtonName) {
391
        SubmitButton sb = getSubmitButton(submitButtonName);
1✔
392
        if (sb == null) {
1!
393
            throw new IllegalSubmitButtonException(submitButtonName, "");
×
394
        }
395
        return getRequest(sb);
1✔
396
    }
397

398
    /**
399
     * Creates and returns a web request which will simulate the submission of this form by pressing the specified
400
     * button. If the button is null, simulates the pressing of the default button.
401
     *
402
     * @param button
403
     *            the button
404
     *
405
     * @return the request
406
     */
407
    public WebRequest getRequest(SubmitButton button) {
408
        return getRequest(button, 0, 0);
1✔
409
    }
410

411
    /**
412
     * Creates and returns a web request which will simulate the submission of this form by pressing the specified
413
     * button. If the button is null, simulates the pressing of the default button.
414
     *
415
     * @param button
416
     *            - the submitbutton to be pressed - may be null
417
     * @param x
418
     *            - the x position
419
     * @param y
420
     *            - the y position
421
     *
422
     * @return the request
423
     */
424
    public WebRequest getRequest(SubmitButton button, int x, int y) {
425
        if (button == null) {
1✔
426
            button = getDefaultButton();
1✔
427
        }
428

429
        if (HttpUnitOptions.getParameterValuesValidated()) {
1✔
430
            if (button == null) {
1✔
431
                throw new IllegalUnnamedSubmitButtonException();
1✔
432
            }
433
            if (button.isFake()) {
1✔
434
                // bypass checks
435
            } else if (!getSubmitButtonVector().contains(button)) {
1✔
436
                throw new IllegalSubmitButtonException(button);
1✔
437
            } else if (!button.wasEnabled()) {
1✔
438
                // this is too late for the check of isDisabled()
439
                // onclick has already been done ...
440
                // [ 1289151 ] Order of events in button.click() is wrong
441
                button.throwDisabledException();
×
442
            }
443
        }
444

445
        SubmitButton[] buttons = getSubmitButtons();
1✔
446
        for (SubmitButton button2 : buttons) {
1✔
447
            button2.setPressed(false);
1✔
448
        }
449
        button.setPressed(true);
1✔
450

451
        if (getMethod().equalsIgnoreCase("post")) {
1✔
452
            return new PostMethodWebRequest(this, button, x, y);
1✔
453
        }
454
        return new GetMethodWebRequest(this, WebRequest.newParameterHolder(this), button, x, y);
1✔
455
    }
456

457
    /**
458
     * Creates and returns a web request which includes the specified button. If no button is specified, will include
459
     * the default button, if any. No parameter validation will be done on the returned request and no scripts will be
460
     * run when it is submitted.
461
     *
462
     * @param button
463
     *            the button
464
     *
465
     * @return the web request
466
     */
467
    public WebRequest newUnvalidatedRequest(SubmitButton button) {
468
        return newUnvalidatedRequest(button, 0, 0);
1✔
469
    }
470

471
    /**
472
     * Creates and returns a web request which includes the specified button and position. If no button is specified,
473
     * will include the default button, if any. No parameter validation will be done on the returned request and no
474
     * scripts will be run when it is submitted.
475
     *
476
     * @param button
477
     *            the button
478
     * @param x
479
     *            the x
480
     * @param y
481
     *            the y
482
     *
483
     * @return the web request
484
     */
485
    public WebRequest newUnvalidatedRequest(SubmitButton button, int x, int y) {
486
        if (button == null) {
1✔
487
            button = getDefaultButton();
1✔
488
        }
489

490
        SubmitButton[] buttons = getSubmitButtons();
1✔
491
        for (SubmitButton button2 : buttons) {
1✔
492
            button2.setPressed(false);
1✔
493
        }
494
        button.setPressed(true);
1✔
495

496
        if (getMethod().equalsIgnoreCase("post")) {
1✔
497
            return new PostMethodWebRequest(this, new UncheckedParameterHolder(this), button, x, y);
1✔
498
        }
499
        return new GetMethodWebRequest(this, new UncheckedParameterHolder(this), button, x, y);
1✔
500
    }
501

502
    /**
503
     * Gets the scripted submit request.
504
     *
505
     * @return the scripted submit request
506
     */
507
    private WebRequest getScriptedSubmitRequest() {
508
        SubmitButton[] buttons = getSubmitButtons();
1✔
509
        for (SubmitButton button : buttons) {
1✔
510
            button.setPressed(false);
1✔
511
        }
512

513
        if (getMethod().equalsIgnoreCase("post")) {
1✔
514
            return new PostMethodWebRequest(this);
1✔
515
        }
516
        return new GetMethodWebRequest(this);
1✔
517

518
    }
519

520
    /**
521
     * Returns the default value of the named parameter. If the parameter does not exist returns null.
522
     *
523
     * @param name
524
     *            the name
525
     *
526
     * @return the parameter value
527
     */
528
    public String getParameterValue(String name) {
529
        String[] values = getParameterValues(name);
1✔
530
        return values.length == 0 ? null : values[0];
1✔
531
    }
532

533
    /**
534
     * Returns the displayed options defined for the specified parameter name.
535
     *
536
     * @param name
537
     *            the name
538
     *
539
     * @return the options
540
     */
541
    public String[] getOptions(String name) {
542
        return getParameter(name).getOptions();
1✔
543
    }
544

545
    /**
546
     * Returns the option values defined for the specified parameter name.
547
     *
548
     * @param name
549
     *            the name
550
     *
551
     * @return the option values
552
     */
553
    public String[] getOptionValues(String name) {
554
        return getParameter(name).getOptionValues();
1✔
555
    }
556

557
    /**
558
     * Returns true if the named parameter accepts multiple values.
559
     *
560
     * @param name
561
     *            the name
562
     *
563
     * @return true, if is multi valued parameter
564
     */
565
    public boolean isMultiValuedParameter(String name) {
566
        return getParameter(name).isMultiValuedParameter();
×
567
    }
568

569
    /**
570
     * Returns the number of text parameters in this form with the specified name.
571
     *
572
     * @param name
573
     *            the name
574
     *
575
     * @return the num text parameters
576
     */
577
    public int getNumTextParameters(String name) {
578
        return getParameter(name).getNumTextParameters();
1✔
579
    }
580

581
    /**
582
     * Returns true if the named parameter accepts free-form text.
583
     *
584
     * @param name
585
     *            the name
586
     *
587
     * @return true, if is text parameter
588
     */
589
    public boolean isTextParameter(String name) {
590
        return getParameter(name).isTextParameter();
×
591
    }
592

593
    /**
594
     * Returns true if this form is to be submitted using mime encoding (the default is URL encoding).
595
     **/
596
    @Override
597
    public boolean isSubmitAsMime() {
598
        return "multipart/form-data".equalsIgnoreCase(getAttribute("enctype"));
1✔
599
    }
600

601
    /**
602
     * Gets the scriptable object.
603
     *
604
     * @return the scriptable object
605
     */
606
    public FormScriptable getScriptableObject() {
607
        return (FormScriptable) getScriptingHandler();
1✔
608
    }
609

610
    /**
611
     * Resets all parameters to their initial values.
612
     */
613
    public void reset() {
614
        if (handleEvent("onreset")) {
1!
615
            resetControls();
1✔
616
        }
617
    }
1✔
618

619
    /**
620
     * Reset controls.
621
     */
622
    private void resetControls() {
623
        FormControl[] controls = getFormControls();
1✔
624
        for (FormControl control : controls) {
1✔
625
            control.reset();
1✔
626
        }
627
    }
1✔
628

629
    @Override
630
    public ScriptableDelegate newScriptable() {
631
        return new Scriptable();
1✔
632
    }
633

634
    // ---------------------------------- WebRequestSource methods
635
    // --------------------------------
636

637
    /**
638
     * Returns the character set encoding for this form.
639
     **/
640
    @Override
641
    public String getCharacterSet() {
642
        return _characterSet;
1✔
643
    }
644

645
    /**
646
     * Returns true if the named parameter accepts files for upload.
647
     **/
648
    @Override
649
    public boolean isFileParameter(String name) {
650
        return getParameter(name).isFileParameter();
1✔
651
    }
652

653
    /**
654
     * Returns an array containing the names of the parameters defined for this form.
655
     **/
656
    @Override
657
    public String[] getParameterNames() {
658
        List parameterNames = new ArrayList(getFormParameters().keySet());
1✔
659
        return (String[]) parameterNames.toArray(new String[parameterNames.size()]);
1✔
660
    }
661

662
    /**
663
     * Returns the multiple default values of the named parameter.
664
     **/
665
    @Override
666
    public String[] getParameterValues(String name) {
667
        final FormParameter parameter = getParameter(name);
1✔
668
        return parameter.getValues();
1✔
669
    }
670

671
    /**
672
     * Returns true if the named parameter is read-only. If more than one control exists with the same name, will return
673
     * true only if all such controls are read-only.
674
     *
675
     * @param name
676
     *            the name
677
     *
678
     * @return true, if is read only parameter
679
     */
680
    public boolean isReadOnlyParameter(String name) {
681
        return getParameter(name).isReadOnlyParameter();
1✔
682
    }
683

684
    /**
685
     * Returns true if the named parameter is disabled. If more than one control exists with the same name, will return
686
     * true only if all such controls are read-only.
687
     *
688
     * @param name
689
     *            the name
690
     *
691
     * @return true, if is disabled parameter
692
     */
693
    public boolean isDisabledParameter(String name) {
694
        return getParameter(name).isDisabledParameter();
1✔
695
    }
696

697
    /**
698
     * Returns true if the named parameter is hidden. If more than one control exists with the same name, will return
699
     * true only if all such controls are hidden.
700
     *
701
     * @param name
702
     *            the name
703
     *
704
     * @return true, if is hidden parameter
705
     */
706
    public boolean isHiddenParameter(String name) {
707
        return getParameter(name).isHiddenParameter();
1✔
708
    }
709

710
    /**
711
     * Creates and returns a web request which will simulate the submission of this form with an unnamed submit button.
712
     **/
713
    @Override
714
    public WebRequest getRequest() {
715
        return getRequest((SubmitButton) null);
1✔
716
    }
717

718
    /**
719
     * Creates and returns a web request based on the current state of this form. No parameter validation will be done
720
     * and there is no guarantee over the order of parameters transmitted.
721
     *
722
     * @return the web request
723
     */
724
    public WebRequest newUnvalidatedRequest() {
725
        return newUnvalidatedRequest(null);
1✔
726
    }
727

728
    /**
729
     * Records a parameter defined by including it in the destination URL. Ignores any parameters whose name matches a
730
     * form control.
731
     **/
732
    @Override
733
    protected void addPresetParameter(String name, String value) {
734
        FormControl[] formControls = getFormControls();
1✔
735
        for (FormControl formControl : formControls) {
1✔
736
            if (formControl.getName().equals(name)) {
1✔
737
                return;
1✔
738
            }
739
        }
740
        _presets.add(new PresetFormParameter(this, name, value));
1✔
741
    }
1✔
742

743
    @Override
744
    protected String getEmptyParameterValue() {
745
        return null;
1✔
746
    }
747

748
    // ---------------------------------- ParameterHolder methods
749
    // --------------------------------
750

751
    /**
752
     * Specifies the position at which an image button (if any) was clicked.
753
     **/
754
    @Override
755
    void selectImageButtonPosition(SubmitButton imageButton, int x, int y) {
756
        imageButton.setLocation(x, y);
1✔
757
    }
1✔
758

759
    /**
760
     * Iterates through the fixed, predefined parameters in this holder, recording them in the supplied parameter
761
     * processor.\ These parameters always go on the URL, no matter what encoding method is used.
762
     **/
763

764
    @Override
765
    void recordPredefinedParameters(ParameterProcessor processor) throws IOException {
766
        FormControl[] controls = getPresetParameters();
1✔
767
        for (FormControl control : controls) {
1✔
768
            control.addValues(processor, getCharacterSet());
1✔
769
        }
770
    }
1✔
771

772
    /**
773
     * Iterates through the parameters in this holder, recording them in the supplied parameter processor.
774
     **/
775
    @Override
776
    public void recordParameters(ParameterProcessor processor) throws IOException {
777
        FormControl[] controls = getFormControls();
1✔
778
        for (FormControl control : controls) {
1✔
779
            control.addValues(processor, getCharacterSet());
1✔
780
        }
781
    }
1✔
782

783
    /**
784
     * Removes a parameter name from this collection.
785
     **/
786
    @Override
787
    public void removeParameter(String name) {
788
        setParameter(name, NO_VALUES);
1✔
789
    }
1✔
790

791
    /**
792
     * Sets the value of a parameter in this form.
793
     *
794
     * @param name
795
     *            - the name of the parameter
796
     * @param value
797
     *            - the value of the parameter
798
     **/
799
    @Override
800
    public void setParameter(String name, String value) {
801
        setParameter(name, new String[] { value });
1✔
802
    }
1✔
803

804
    /**
805
     * Sets the multiple values of a parameter in this form. This is generally used when there are multiple controls
806
     * with the same name in the form.
807
     *
808
     * @param name
809
     *            the name
810
     * @param values
811
     *            the values
812
     */
813
    @Override
814
    public void setParameter(String name, final String[] values) {
815
        FormParameter parameter = getParameter(name);
1✔
816
        if (parameter.isUnknown()) {
1✔
817
            throw new NoSuchParameterException(name);
1✔
818
        }
819
        if (parameter.isFileParameter()) {
1✔
820
            if (values != NO_VALUES) {
1✔
821
                throw new InvalidFileParameterException(name, values);
1✔
822
            }
823
            parameter.setFiles(new UploadFileSpec[0]);
1✔
824
        } else {
825
            parameter.setValues(values);
1✔
826
        }
827

828
    }
1✔
829

830
    /**
831
     * Sets the multiple values of a file upload parameter in a web request.
832
     **/
833
    @Override
834
    public void setParameter(String name, UploadFileSpec[] files) {
835
        FormParameter parameter = getParameter(name);
1✔
836
        if (parameter == null || !parameter.isFileParameter()) {
1!
837
            throw new NoSuchParameterException(name);
1✔
838
        }
839
        parameter.setFiles(files);
1✔
840
    }
1✔
841

842
    /**
843
     * Sets the single value of a file upload parameter in this form. A more convenient way to do this than using
844
     * {@link #setParameter(String,com.meterware.httpunit.protocol.UploadFileSpec[])}
845
     *
846
     * @param name
847
     *            the name
848
     * @param file
849
     *            the file
850
     */
851
    public void setParameter(String name, File file) {
852
        setParameter(name, new UploadFileSpec[] { new UploadFileSpec(file) });
1✔
853
    }
1✔
854

855
    /**
856
     * Toggles the value of the specified checkbox parameter.
857
     *
858
     * @param name
859
     *            the name of the checkbox parameter
860
     *
861
     * @throws IllegalArgumentException
862
     *             if the specified parameter is not a checkbox or there is more than one control with that name.
863
     */
864
    public void toggleCheckbox(String name) {
865
        FormParameter parameter = getParameter(name);
1✔
866
        if (parameter == null) {
1!
867
            throw new NoSuchParameterException(name);
×
868
        }
869
        parameter.toggleCheckbox();
1✔
870
    }
1✔
871

872
    /**
873
     * Toggles the value of the specified checkbox parameter.
874
     *
875
     * @param name
876
     *            the name of the checkbox parameter
877
     * @param value
878
     *            of the checkbox parameter
879
     *
880
     * @throws IllegalArgumentException
881
     *             if the specified parameter is not a checkbox or if there is no checkbox with the specified name and
882
     *             value.
883
     */
884
    public void toggleCheckbox(String name, String value) {
885
        FormParameter parameter = getParameter(name);
1✔
886
        if (parameter == null) {
1!
887
            throw new NoSuchParameterException(name);
×
888
        }
889
        parameter.toggleCheckbox(value);
1✔
890
    }
1✔
891

892
    /**
893
     * Sets the value of the specified checkbox parameter.
894
     *
895
     * @param name
896
     *            the name of the checkbox parameter
897
     * @param state
898
     *            the new state of the checkbox
899
     *
900
     * @throws IllegalArgumentException
901
     *             if the specified parameter is not a checkbox or there is more than one control with that name.
902
     */
903
    public void setCheckbox(String name, boolean state) {
904
        FormParameter parameter = getParameter(name);
1✔
905
        if (parameter == null) {
1!
906
            throw new NoSuchParameterException(name);
×
907
        }
908
        parameter.setValue(state);
1✔
909
    }
1✔
910

911
    /**
912
     * Sets the value of the specified checkbox parameter.
913
     *
914
     * @param name
915
     *            the name of the checkbox parameter
916
     * @param value
917
     *            of the checkbox parameter
918
     * @param state
919
     *            the new state of the checkbox
920
     *
921
     * @throws IllegalArgumentException
922
     *             if the specified parameter is not a checkbox or if there is no checkbox with the specified name and
923
     *             value.
924
     */
925
    public void setCheckbox(String name, String value, boolean state) {
926
        FormParameter parameter = getParameter(name);
1✔
927
        if (parameter == null) {
1!
928
            throw new NoSuchParameterException(name);
×
929
        }
930
        parameter.setValue(value, state);
1✔
931
    }
1✔
932

933
    /**
934
     * Scriptable implementation for the WebForm.
935
     */
936
    public class Scriptable extends HTMLElementScriptable implements NamedDelegate, IdentifiedDelegate, FormScriptable {
937

938
        /**
939
         * Gets the action.
940
         *
941
         * @return the action
942
         */
943
        public String getAction() {
944
            return WebForm.this.getAction();
1✔
945
        }
946

947
        @Override
948
        public void setAction(String newAction) {
949
            setDestination(newAction);
1✔
950
            _presetParameters = null;
1✔
951
        }
1✔
952

953
        /**
954
         * Submit.
955
         *
956
         * @throws IOException
957
         *             Signals that an I/O exception has occurred.
958
         * @throws SAXException
959
         *             the SAX exception
960
         */
961
        public void submit() throws IOException, SAXException {
962
            submitRequest(getScriptedSubmitRequest());
1✔
963
        }
1✔
964

965
        /**
966
         * Reset.
967
         *
968
         * @throws IOException
969
         *             Signals that an I/O exception has occurred.
970
         * @throws SAXException
971
         *             the SAX exception
972
         */
973
        public void reset() throws IOException, SAXException {
974
            resetControls();
1✔
975
        }
1✔
976

977
        /**
978
         * return the name of the WebForm
979
         *
980
         * @return the name
981
         */
982
        @Override
983
        public String getName() {
984
            return WebForm.this.getName().length() != 0 ? WebForm.this.getName() : WebForm.this.getID();
1✔
985
        }
986

987
        /**
988
         * return the id of the WebForm
989
         *
990
         * @return the id
991
         */
992
        @Override
993
        public String getID() {
994
            return WebForm.this.getID();
1✔
995
        }
996

997
        /**
998
         * get the Object for the given propertyName
999
         *
1000
         * @param propertyName
1001
         *            - the name of the property to get
1002
         *
1003
         * @return the Object for the property
1004
         */
1005
        @Override
1006
        public Object get(String propertyName) {
1007
            if (propertyName.equals("target")) {
1✔
1008
                return getTarget();
1✔
1009
            }
1010
            if (propertyName.equals("action")) {
1✔
1011
                return getAction();
1✔
1012
            }
1013
            if (propertyName.equals("length")) {
1✔
1014
                return Integer.valueOf(getFormControls().length);
1✔
1015
            }
1016
            final FormParameter parameter = getParameter(propertyName);
1✔
1017
            if (!parameter.isUnknown()) {
1✔
1018
                return parameter.getScriptableObject();
1✔
1019
            }
1020
            FormControl control = getControlWithID(propertyName);
1✔
1021
            return control == null ? super.get(propertyName) : control.getScriptingHandler();
1✔
1022
        }
1023

1024
        /**
1025
         * Sets the value of the named property. Will throw a runtime exception if the property does not exist or cannot
1026
         * accept the specified value.
1027
         *
1028
         * @param propertyName
1029
         *            - the name of the property
1030
         * @param value
1031
         *            - the new value
1032
         **/
1033
        @Override
1034
        public void set(String propertyName, Object value) {
1035
            if (propertyName.equals("target")) {
1✔
1036
                setTargetAttribute(value.toString());
1✔
1037
            } else if (propertyName.equals("action")) {
1✔
1038
                setAction(value.toString());
1✔
1039
            } else if (propertyName.equals("name")) {
1✔
1040
                getElement().setAttribute("name", value.toString());
1✔
1041
            } else if (value instanceof String) {
1!
1042
                setParameterValue(propertyName, (String) value);
×
1043
            } else if (value instanceof Number) {
1!
1044
                setParameterValue(propertyName, HttpUnitUtils.trimmedValue((Number) value));
1✔
1045
            } else {
1046
                super.set(propertyName, value);
×
1047
            }
1048
        }
1✔
1049

1050
        @Override
1051
        public void setParameterValue(String name, String value) {
1052
            final Object scriptableObject = getParameter(name).getScriptableObject();
1✔
1053
            if (scriptableObject instanceof ScriptableDelegate) {
1!
1054
                ((ScriptableDelegate) scriptableObject).set("value", value);
1✔
1055
            } else if (scriptableObject instanceof ScriptableDelegate[]) {
×
1056
                ((ScriptableDelegate[]) scriptableObject)[0].set("value", value);
×
1057
            }
1058
        }
1✔
1059

1060
        /**
1061
         * Gets the element delegates.
1062
         *
1063
         * @return the element delegates
1064
         */
1065
        public ScriptableDelegate[] getElementDelegates() {
1066
            FormControl[] controls = getFormControls();
1✔
1067
            ScriptableDelegate[] result = new ScriptableDelegate[controls.length];
1✔
1068
            for (int i = 0; i < result.length; i++) {
1✔
1069
                result[i] = (ScriptableDelegate) controls[i].getScriptingHandler();
1✔
1070
            }
1071
            return result;
1✔
1072
        }
1073

1074
        /**
1075
         * Gets the elements by tag name.
1076
         *
1077
         * @param name
1078
         *            the name
1079
         *
1080
         * @return the elements by tag name
1081
         *
1082
         * @throws SAXException
1083
         *             the SAX exception
1084
         */
1085
        public ScriptableDelegate[] getElementsByTagName(String name) throws SAXException {
1086
            return getDelegates(getHTMLPage().getElementsByTagName(getElement(), name));
1✔
1087
        }
1088

1089
        /**
1090
         * Instantiates a new scriptable.
1091
         */
1092
        Scriptable() {
1✔
1093
            super(WebForm.this);
1✔
1094
        }
1✔
1095
    }
1096

1097
    // ---------------------------------- package members
1098
    // --------------------------------
1099

1100
    /**
1101
     * Contructs a web form given the URL of its source page and the DOM extracted from that page.
1102
     *
1103
     * @param response
1104
     *            the response
1105
     * @param baseURL
1106
     *            the base URL
1107
     * @param node
1108
     *            the node
1109
     * @param frame
1110
     *            the frame
1111
     * @param defaultTarget
1112
     *            the default target
1113
     * @param characterSet
1114
     *            the character set
1115
     * @param registry
1116
     *            the registry
1117
     */
1118
    WebForm(WebResponse response, URL baseURL, Node node, FrameSelector frame, String defaultTarget,
1119
            String characterSet, ElementRegistry registry) {
1120
        super(response, node, baseURL, "action", frame, defaultTarget);
1✔
1121
        _characterSet = characterSet;
1✔
1122
        _registry = registry;
1✔
1123
        _domElement = (HTMLFormElement) node;
1✔
1124
    }
1✔
1125

1126
    /**
1127
     * Returns the form control which is part of this form with the specified ID.
1128
     *
1129
     * @param id
1130
     *            the id
1131
     *
1132
     * @return the control with ID
1133
     */
1134
    public FormControl getControlWithID(String id) {
1135
        FormControl[] controls = getFormControls();
1✔
1136
        for (FormControl control : controls) {
1✔
1137
            if (control.getID().equals(id)) {
1✔
1138
                return control;
1✔
1139
            }
1140
        }
1141
        return null;
1✔
1142
    }
1143

1144
    // ---------------------------------- private members
1145
    // --------------------------------
1146

1147
    /**
1148
     * Gets the default button.
1149
     *
1150
     * @return the default button
1151
     */
1152
    private SubmitButton getDefaultButton() {
1153
        if (getSubmitButtons().length == 1) {
1✔
1154
            return getSubmitButtons()[0];
1✔
1155
        }
1156
        return getSubmitButton("");
1✔
1157
    }
1158

1159
    /**
1160
     * get the list of submit buttons - will always contain at least one button - if the original list has none a faked
1161
     * submit button will be added.
1162
     *
1163
     * @return a list with the submit buttons
1164
     */
1165
    private List<SubmitButton> getSubmitButtonVector() {
1166
        if (_buttonVector == null) {
1✔
1167
            _buttonVector = new ArrayList<>();
1✔
1168
            FormControl[] controls = getFormControls();
1✔
1169
            for (FormControl control : controls) {
1✔
1170
                if (control instanceof SubmitButton) {
1✔
1171
                    SubmitButton sb = (SubmitButton) control;
1✔
1172
                    sb.rememberEnableState();
1✔
1173
                    _buttonVector.add(sb);
1✔
1174
                }
1175
            }
1176

1177
            /**
1178
             * make sure that there is always at least one submit button if none is in the list add a faked one
1179
             */
1180
            if (_buttonVector.isEmpty()) {
1✔
1181
                _buttonVector.add(SubmitButton.createFakeSubmitButton(this));
1✔
1182
            }
1183
        }
1184
        return _buttonVector;
1✔
1185
    }
1186

1187
    /**
1188
     * Gets the preset parameters.
1189
     *
1190
     * @return the preset parameters
1191
     */
1192
    private FormControl[] getPresetParameters() {
1193
        if (_presetParameters == null) {
1✔
1194
            _presets = new ArrayList<>();
1✔
1195
            loadDestinationParameters();
1✔
1196
            _presetParameters = (FormControl[]) _presets.toArray(new FormControl[_presets.size()]);
1✔
1197
        }
1198
        return _presetParameters;
1✔
1199
    }
1200

1201
    /**
1202
     * New form control.
1203
     *
1204
     * @param child
1205
     *            the child
1206
     *
1207
     * @return the form control
1208
     */
1209
    FormControl newFormControl(Node child) {
1210
        return FormControl.newFormParameter(this, child);
1✔
1211
    }
1212

1213
    /**
1214
     * Returns an array of form parameter attributes for this form.
1215
     *
1216
     * @return the form controls
1217
     */
1218
    private FormControl[] getFormControls() {
1219
        HTMLCollection controlElements = _domElement.getElements();
1✔
1220
        FormControl[] controls = new FormControl[controlElements.getLength()];
1✔
1221
        for (int i = 0; i < controls.length; i++) {
1✔
1222
            controls[i] = getControlForNode(controlElements.item(i));
1✔
1223
        }
1224
        return controls;
1✔
1225
    }
1226

1227
    /**
1228
     * Gets the control for node.
1229
     *
1230
     * @param node
1231
     *            the node
1232
     *
1233
     * @return the control for node
1234
     */
1235
    private FormControl getControlForNode(Node node) {
1236
        if (_registry.hasNode(node)) {
1!
1237
            return (FormControl) _registry.getRegisteredElement(node);
1✔
1238
        }
1239
        return (FormControl) _registry.registerElement(node, newFormControl(node));
×
1240
    }
1241

1242
    /**
1243
     * get the form parameter with the given name.
1244
     *
1245
     * @param name
1246
     *            the name
1247
     *
1248
     * @return the form parameter with this name
1249
     */
1250
    public FormParameter getParameter(String name) {
1251
        final FormParameter parameter = (FormParameter) getFormParameters().get(name);
1✔
1252
        return parameter != null ? parameter : FormParameter.getUNKNOWN_PARAMETER();
1✔
1253
    }
1254

1255
    /**
1256
     * Returns a map of parameter name to form parameter objects. Each form parameter object represents the set of form
1257
     * controls with a particular name. Unnamed parameters are ignored.
1258
     *
1259
     * @return the form parameters
1260
     */
1261
    private Map getFormParameters() {
1262
        Map formParameters = new HashMap<>();
1✔
1263
        loadFormParameters(formParameters, getPresetParameters());
1✔
1264
        loadFormParameters(formParameters, getFormControls());
1✔
1265
        return formParameters;
1✔
1266
    }
1267

1268
    /**
1269
     * Load form parameters.
1270
     *
1271
     * @param formParameters
1272
     *            the form parameters
1273
     * @param controls
1274
     *            the controls
1275
     */
1276
    private void loadFormParameters(Map formParameters, FormControl[] controls) {
1277
        for (FormControl control : controls) {
1✔
1278
            if (control.getName().isEmpty()) {
1✔
1279
                continue;
1✔
1280
            }
1281
            FormParameter parameter = (FormParameter) formParameters.get(control.getName());
1✔
1282
            if (parameter == null) {
1✔
1283
                parameter = new FormParameter();
1✔
1284
                formParameters.put(control.getName(), parameter);
1✔
1285
            }
1286
            parameter.addControl(control);
1✔
1287
        }
1288
    }
1✔
1289

1290
    static {
1291
        MATCH_NAME = (htmlElement, criteria) -> HttpUnitUtils.matches(((WebForm) htmlElement).getName(),
1✔
1292
                (String) criteria);
1293

1294
    }
1✔
1295

1296
    // ===========================---===== exception class
1297
    // NoSuchParameterException =========================================
1298

1299
    /**
1300
     * This exception is thrown on an attempt to set a file parameter to a non file type.
1301
     */
1302
    class InvalidFileParameterException extends IllegalRequestParameterException {
1303

1304
        /** The Constant serialVersionUID. */
1305
        private static final long serialVersionUID = 1L;
1306

1307
        /**
1308
         * construct a new InvalidFileParameterException for the given parameter name and value list.
1309
         *
1310
         * @param parameterName
1311
         *            the parameter name
1312
         * @param values
1313
         *            the values
1314
         */
1315
        InvalidFileParameterException(String parameterName, String[] values) {
1✔
1316
            _parameterName = parameterName;
1✔
1317
            _values = values;
1✔
1318
        }
1✔
1319

1320
        /**
1321
         * get the message for this exception
1322
         */
1323
        @Override
1324
        public String getMessage() {
1325
            StringBuilder valueList = new StringBuilder();
1✔
1326
            String delim = "";
1✔
1327
            for (String _value : _values) {
1✔
1328
                valueList.append(delim).append("'").append(_value).append("'");
1✔
1329
                delim = ", ";
1✔
1330
            }
1331
            return "The file parameter with the name '" + _parameterName
1✔
1332
                    + "' must have type File but the string values " + valueList.append(" where supplied").toString();
1✔
1333
        }
1334

1335
        /** The parameter name. */
1336
        private String _parameterName;
1337

1338
        /** The values. */
1339
        private String[] _values;
1340
    }
1341

1342
    /**
1343
     * This exception is thrown on an attempt to set a parameter to a value not permitted to it by the form.
1344
     **/
1345
    class NoSuchParameterException extends IllegalRequestParameterException {
1346

1347
        /** The Constant serialVersionUID. */
1348
        private static final long serialVersionUID = 1L;
1349

1350
        /**
1351
         * Instantiates a new no such parameter exception.
1352
         *
1353
         * @param parameterName
1354
         *            the parameter name
1355
         */
1356
        NoSuchParameterException(String parameterName) {
1✔
1357
            _parameterName = parameterName;
1✔
1358
        }
1✔
1359

1360
        @Override
1361
        public String getMessage() {
1362
            return "No parameter named '" + _parameterName + "' is defined in the form";
1✔
1363
        }
1364

1365
        /** The parameter name. */
1366
        private String _parameterName;
1367

1368
    }
1369

1370
    // ============================= exception class
1371
    // IllegalUnnamedSubmitButtonException
1372
    // ======================================
1373

1374
    /**
1375
     * This exception is thrown on an attempt to define a form request with a button not defined on that form.
1376
     **/
1377
    class IllegalUnnamedSubmitButtonException extends IllegalRequestParameterException {
1378

1379
        /** The Constant serialVersionUID. */
1380
        private static final long serialVersionUID = 1L;
1381

1382
        /**
1383
         * Instantiates a new illegal unnamed submit button exception.
1384
         */
1385
        IllegalUnnamedSubmitButtonException() {
1✔
1386
        }
1✔
1387

1388
        @Override
1389
        public String getMessage() {
1390
            return "This form has more than one submit button, none unnamed. You must specify the button to be used.";
×
1391
        }
1392

1393
    }
1394

1395
    // ============================= exception class
1396
    // IllegalSubmitButtonException ======================================
1397

1398
    /**
1399
     * This exception is thrown on an attempt to define a form request with a button not defined on that form.
1400
     **/
1401
    class IllegalSubmitButtonException extends IllegalRequestParameterException {
1402

1403
        /** The Constant serialVersionUID. */
1404
        private static final long serialVersionUID = 1L;
1405

1406
        /**
1407
         * Instantiates a new illegal submit button exception.
1408
         *
1409
         * @param button
1410
         *            the button
1411
         */
1412
        IllegalSubmitButtonException(SubmitButton button) {
1✔
1413
            _name = button.getName();
1✔
1414
            _value = button.getValue();
1✔
1415
        }
1✔
1416

1417
        /**
1418
         * Instantiates a new illegal submit button exception.
1419
         *
1420
         * @param name
1421
         *            the name
1422
         * @param value
1423
         *            the value
1424
         */
1425
        IllegalSubmitButtonException(String name, String value) {
1✔
1426
            _name = name;
1✔
1427
            _value = value;
1✔
1428
        }
1✔
1429

1430
        @Override
1431
        public String getMessage() {
1432
            return "Specified submit button (name=\"" + _name + "\" value=\"" + _value + "\") not part of this form.";
×
1433
        }
1434

1435
        /** The name. */
1436
        private String _name;
1437

1438
        /** The value. */
1439
        private String _value;
1440

1441
    }
1442

1443
    // ============================= exception class
1444
    // IllegalUnnamedSubmitButtonException
1445
    // ======================================
1446

1447
}
1448

1449
// ========================================== class PresetFormParameter
1450
// =================================================
1451

1452
class PresetFormParameter extends FormControl {
1453

1454
    PresetFormParameter(WebForm form, String name, String value) {
1455
        super(form);
1✔
1456
        _name = name;
1✔
1457
        _value = value;
1✔
1458
    }
1✔
1459

1460
    /**
1461
     * Returns the name of this control..
1462
     **/
1463
    @Override
1464
    public String getName() {
1465
        return _name;
1✔
1466
    }
1467

1468
    /**
1469
     * Returns true if this control is read-only.
1470
     **/
1471
    @Override
1472
    public boolean isReadOnly() {
1473
        return true;
×
1474
    }
1475

1476
    /**
1477
     * Returns true if this control accepts free-form text.
1478
     **/
1479
    @Override
1480
    public boolean isTextControl() {
1481
        return true;
×
1482
    }
1483

1484
    /**
1485
     * Remove any required values for this control from the list, throwing an exception if they are missing.
1486
     **/
1487
    @Override
1488
    void claimRequiredValues(List values) {
1489
        if (_value != null) {
×
1490
            claimValueIsRequired(values, _value);
×
1491
        }
1492
    }
×
1493

1494
    @Override
1495
    public String getType() {
1496
        return UNDEFINED_TYPE;
×
1497
    }
1498

1499
    /**
1500
     * Returns the current value(s) associated with this control. These values will be transmitted to the server if the
1501
     * control is 'successful'.
1502
     **/
1503
    @Override
1504
    public String[] getValues() {
1505
        if (_values == null) {
×
1506
            _values = new String[] { _value };
×
1507
        }
1508
        return _values;
×
1509
    }
1510

1511
    @Override
1512
    protected void addValues(ParameterProcessor processor, String characterSet) throws IOException {
1513
        processor.addParameter(_name, _value, characterSet);
1✔
1514
    }
1✔
1515

1516
    private String _name;
1517
    private String _value;
1518
    private String[] _values;
1519
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc