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

hazendaz / httpunit / 636

05 Dec 2025 03:27AM UTC coverage: 80.509%. Remained the same
636

push

github

hazendaz
Cleanup more old since tags

you guessed it, at this point going to jautodoc the rest so the warnings on builds go away ;)

3213 of 4105 branches covered (78.27%)

Branch coverage included in aggregate %.

8249 of 10132 relevant lines covered (81.42%)

0.81 hits per line

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

88.87
/src/main/java/com/meterware/httpunit/WebForm.java
1
/*
2
 * MIT License
3
 *
4
 * Copyright 2011-2025 Russell Gold
5
 *
6
 * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
7
 * documentation files (the "Software"), to deal in the Software without restriction, including without limitation
8
 * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and
9
 * to permit persons to whom the Software is furnished to do so, subject to the following conditions:
10
 *
11
 * The above copyright notice and this permission notice shall be included in all copies or substantial portions
12
 * of the Software.
13
 *
14
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
15
 * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
17
 * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
18
 * DEALINGS IN THE SOFTWARE.
19
 */
20
package com.meterware.httpunit;
21

22
import com.meterware.httpunit.protocol.ParameterProcessor;
23
import com.meterware.httpunit.protocol.UploadFileSpec;
24
import com.meterware.httpunit.scripting.FormScriptable;
25
import com.meterware.httpunit.scripting.IdentifiedDelegate;
26
import com.meterware.httpunit.scripting.NamedDelegate;
27
import com.meterware.httpunit.scripting.ScriptableDelegate;
28

29
import java.io.File;
30
import java.io.IOException;
31
import java.net.URL;
32
import java.net.UnknownServiceException;
33
import java.util.ArrayList;
34
import java.util.HashMap;
35
import java.util.List;
36
import java.util.Map;
37
import java.util.Vector;
38

39
import org.w3c.dom.Node;
40
import org.w3c.dom.html.HTMLCollection;
41
import org.w3c.dom.html.HTMLFormElement;
42
import org.xml.sax.SAXException;
43

44
/**
45
 * This class represents a form in an HTML page. Users of this class may examine the parameters defined for the form,
46
 * the structure of the form (as a DOM), or the text of the form. They may also create a {@link WebRequest} to simulate
47
 * the submission of the form.
48
 **/
49
public class WebForm extends WebRequestSource {
50
    private static final String[] NO_VALUES = {};
1✔
51

52
    private Button[] _buttons;
53

54
    /** The submit buttons in this form. **/
55
    private SubmitButton[] _submitButtons;
56

57
    /** The character set in which the form will be submitted. **/
58
    private String _characterSet;
59

60
    private Vector _buttonVector;
61

62
    private FormControl[] _presetParameters;
63
    private ArrayList _presets;
64

65
    private ElementRegistry _registry;
66

67
    /** Predicate to match a link's name. **/
68
    public static final HTMLElementPredicate MATCH_NAME;
69
    private HTMLFormElement _domElement;
70

71
    /**
72
     * Submits this form using the web client from which it was originally obtained.
73
     **/
74
    public WebResponse submit() throws IOException, SAXException {
75
        return submit(getDefaultButton());
1✔
76
    }
77

78
    /**
79
     * Submits this form using the web client from which it was originally obtained. Will usually return the result of
80
     * that submission; however, if the submit button's 'onclick' or the form's 'onsubmit' event is triggered and
81
     * inhibits the submission, will return the updated contents of the frame containing this form.
82
     **/
83
    public WebResponse submit(SubmitButton button) throws IOException, SAXException {
84
        return submit(button, 0, 0);
1✔
85
    }
86

87
    /**
88
     * Submits this form using the web client from which it was originally obtained. Will usually return the result of
89
     * that submission; however, if the submit button's 'onclick' or the form's 'onsubmit' event is triggered and
90
     * inhibits the submission, will return the updated contents of the frame containing this form.
91
     **/
92
    public WebResponse submit(SubmitButton button, int x, int y) throws IOException, SAXException {
93
        if (button == null) {
1✔
94
            throw new IllegalSubmitButtonException("?", "?");
1✔
95
        }
96
        button.doOnClickSequence(x, y);
1✔
97
        return getCurrentFrameContents();
1✔
98
    }
99

100
    /**
101
     * Submits this form using the web client from which it was originally obtained, ignoring any buttons defined for
102
     * the form.
103
     **/
104
    public WebResponse submitNoButton() throws SAXException, IOException {
105
        return submit(SubmitButton.createFakeSubmitButton(this /* fake */));
1✔
106
    }
107

108
    @Override
109
    protected WebResponse submitRequest(String event, WebRequest request) throws IOException, SAXException {
110
        try {
111
            return super.submitRequest(event, request);
1✔
112
        } catch (UnknownServiceException e) {
1✔
113
            throw new UnsupportedActionException(
1✔
114
                    "HttpUnit does not support " + request.getURL().getProtocol() + " URLs in form submissions");
1✔
115
        }
116
    }
117

118
    /**
119
     * Submits the form without also invoking the button's "onclick" event.
120
     */
121
    WebResponse doFormSubmit(SubmitButton button) throws IOException, SAXException {
122
        return submitRequest(getAttribute("onsubmit"), getRequest(button));
×
123
    }
124

125
    WebResponse doFormSubmit(SubmitButton button, int x, int y) throws IOException, SAXException {
126
        return submitRequest(getAttribute("onsubmit"), getRequest(button, x, y));
1✔
127
    }
128

129
    /**
130
     * Returns the method defined for this form.
131
     **/
132
    public String getMethod() {
133
        return getAttribute("method", "GET");
1✔
134
    }
135

136
    /**
137
     * Returns the action defined for this form.
138
     **/
139
    public String getAction() {
140
        return getDestination();
1✔
141
    }
142

143
    /**
144
     * Returns true if a parameter with given name exists in this form.
145
     **/
146
    public boolean hasParameterNamed(String soughtName) {
147
        return getFormParameters().containsKey(soughtName);
1✔
148
    }
149

150
    /**
151
     * Returns true if a parameter starting with a given name exists,
152
     **/
153
    public boolean hasParameterStartingWithPrefix(String prefix) {
154
        String[] names = getParameterNames();
1✔
155
        for (String name : names) {
1!
156
            if (name.startsWith(prefix)) {
1✔
157
                return true;
1✔
158
            }
159
        }
160
        return false;
×
161
    }
162

163
    /**
164
     * Returns an array containing all of the buttons defined for this form.
165
     **/
166
    public Button[] getButtons() {
167
        if (_buttons == null) {
1✔
168
            FormControl[] controls = getFormControls();
1✔
169
            ArrayList buttonList = new ArrayList<>();
1✔
170
            for (FormControl control : controls) {
1✔
171
                if (control instanceof Button) {
1✔
172
                    buttonList.add(control);
1✔
173
                }
174
            }
175
            _buttons = (Button[]) buttonList.toArray(new Button[buttonList.size()]);
1✔
176
        }
177
        return _buttons;
1✔
178
    }
179

180
    public Button getButton(HTMLElementPredicate predicate, Object criteria) {
181
        Button[] buttons = getButtons();
1✔
182
        for (Button button : buttons) {
1!
183
            if (predicate.matchesCriteria(button, criteria)) {
1✔
184
                return button;
1✔
185
            }
186
        }
187
        return null;
×
188
    }
189

190
    /**
191
     * Convenience method which returns the button with the specified ID.
192
     */
193
    public Button getButtonWithID(String buttonID) {
194
        return getButton(Button.WITH_ID, buttonID);
1✔
195
    }
196

197
    /**
198
     * Returns an array containing the submit buttons defined for this form.
199
     **/
200
    public SubmitButton[] getSubmitButtons() {
201
        if (_submitButtons == null) {
1✔
202
            Vector buttons = getSubmitButtonVector();
1✔
203
            _submitButtons = new SubmitButton[buttons.size()];
1✔
204
            buttons.copyInto(_submitButtons);
1✔
205
        }
206
        return _submitButtons;
1✔
207
    }
208

209
    /**
210
     * Returns the submit button defined in this form with the specified name. If more than one such button exists, will
211
     * return the first found. If no such button is found, will return null.
212
     **/
213
    public SubmitButton getSubmitButton(String name) {
214
        SubmitButton[] buttons = getSubmitButtons();
1✔
215
        for (SubmitButton button : buttons) {
1✔
216
            if (button.getName().equals(name)) {
1✔
217
                return button;
1✔
218
            }
219
        }
220
        return null;
1✔
221
    }
222

223
    /**
224
     * Returns the submit button defined in this form with the specified name and value. If more than one such button
225
     * exists, will return the first found. If no such button is found, will return null.
226
     **/
227
    public SubmitButton getSubmitButton(String name, String value) {
228
        SubmitButton[] buttons = getSubmitButtons();
1✔
229
        for (SubmitButton button : buttons) {
1!
230
            if (button.getName().equals(name) && button.getValue().equals(value)) {
1!
231
                return button;
1✔
232
            }
233
        }
234
        return null;
×
235
    }
236

237
    /**
238
     * Returns the submit button defined in this form with the specified ID. If more than one such button exists, will
239
     * return the first found. If no such button is found, will return null.
240
     **/
241
    public SubmitButton getSubmitButtonWithID(String ID) {
242
        SubmitButton[] buttons = getSubmitButtons();
1✔
243
        for (SubmitButton button : buttons) {
1!
244
            if (button.getID().equals(ID)) {
1!
245
                return button;
1✔
246
            }
247
        }
248
        return null;
×
249
    }
250

251
    /**
252
     * Creates and returns a web request which will simulate the submission of this form with a button with the
253
     * specified name and value.
254
     **/
255
    public WebRequest getRequest(String submitButtonName, String submitButtonValue) {
256
        SubmitButton sb = getSubmitButton(submitButtonName, submitButtonValue);
1✔
257
        if (sb == null) {
1!
258
            throw new IllegalSubmitButtonException(submitButtonName, submitButtonValue);
×
259
        }
260
        return getRequest(sb);
1✔
261
    }
262

263
    /**
264
     * Creates and returns a web request which will simulate the submission of this form with a button with the
265
     * specified name.
266
     **/
267
    public WebRequest getRequest(String submitButtonName) {
268
        SubmitButton sb = getSubmitButton(submitButtonName);
1✔
269
        if (sb == null) {
1!
270
            throw new IllegalSubmitButtonException(submitButtonName, "");
×
271
        }
272
        return getRequest(sb);
1✔
273
    }
274

275
    /**
276
     * Creates and returns a web request which will simulate the submission of this form by pressing the specified
277
     * button. If the button is null, simulates the pressing of the default button.
278
     **/
279
    public WebRequest getRequest(SubmitButton button) {
280
        return getRequest(button, 0, 0);
1✔
281
    }
282

283
    /**
284
     * Creates and returns a web request which will simulate the submission of this form by pressing the specified
285
     * button. If the button is null, simulates the pressing of the default button.
286
     *
287
     * @param button
288
     *            - the submitbutton to be pressed - may be null
289
     * @param x
290
     *            - the x position
291
     * @param y
292
     *            - the y position
293
     **/
294
    public WebRequest getRequest(SubmitButton button, int x, int y) {
295
        if (button == null) {
1✔
296
            button = getDefaultButton();
1✔
297
        }
298

299
        if (HttpUnitOptions.getParameterValuesValidated()) {
1✔
300
            if (button == null) {
1✔
301
                throw new IllegalUnnamedSubmitButtonException();
1✔
302
            }
303
            if (button.isFake()) {
1✔
304
                // bypass checks
305
            } else if (!getSubmitButtonVector().contains(button)) {
1✔
306
                throw new IllegalSubmitButtonException(button);
1✔
307
            } else if (!button.wasEnabled()) {
1✔
308
                // this is too late for the check of isDisabled()
309
                // onclick has already been done ...
310
                // [ 1289151 ] Order of events in button.click() is wrong
311
                button.throwDisabledException();
×
312
            }
313
        }
314

315
        SubmitButton[] buttons = getSubmitButtons();
1✔
316
        for (SubmitButton button2 : buttons) {
1✔
317
            button2.setPressed(false);
1✔
318
        }
319
        button.setPressed(true);
1✔
320

321
        if (getMethod().equalsIgnoreCase("post")) {
1✔
322
            return new PostMethodWebRequest(this, button, x, y);
1✔
323
        }
324
        return new GetMethodWebRequest(this, WebRequest.newParameterHolder(this), button, x, y);
1✔
325
    }
326

327
    /**
328
     * Creates and returns a web request which includes the specified button. If no button is specified, will include
329
     * the default button, if any. No parameter validation will be done on the returned request and no scripts will be
330
     * run when it is submitted.
331
     **/
332
    public WebRequest newUnvalidatedRequest(SubmitButton button) {
333
        return newUnvalidatedRequest(button, 0, 0);
1✔
334
    }
335

336
    /**
337
     * Creates and returns a web request which includes the specified button and position. If no button is specified,
338
     * will include the default button, if any. No parameter validation will be done on the returned request and no
339
     * scripts will be run when it is submitted.
340
     **/
341
    public WebRequest newUnvalidatedRequest(SubmitButton button, int x, int y) {
342
        if (button == null) {
1✔
343
            button = getDefaultButton();
1✔
344
        }
345

346
        SubmitButton[] buttons = getSubmitButtons();
1✔
347
        for (SubmitButton button2 : buttons) {
1✔
348
            button2.setPressed(false);
1✔
349
        }
350
        button.setPressed(true);
1✔
351

352
        if (getMethod().equalsIgnoreCase("post")) {
1✔
353
            return new PostMethodWebRequest(this, new UncheckedParameterHolder(this), button, x, y);
1✔
354
        }
355
        return new GetMethodWebRequest(this, new UncheckedParameterHolder(this), button, x, y);
1✔
356
    }
357

358
    private WebRequest getScriptedSubmitRequest() {
359
        SubmitButton[] buttons = getSubmitButtons();
1✔
360
        for (SubmitButton button : buttons) {
1✔
361
            button.setPressed(false);
1✔
362
        }
363

364
        if (getMethod().equalsIgnoreCase("post")) {
1✔
365
            return new PostMethodWebRequest(this);
1✔
366
        }
367
        return new GetMethodWebRequest(this);
1✔
368

369
    }
370

371
    /**
372
     * Returns the default value of the named parameter. If the parameter does not exist returns null.
373
     **/
374
    public String getParameterValue(String name) {
375
        String[] values = getParameterValues(name);
1✔
376
        return values.length == 0 ? null : values[0];
1✔
377
    }
378

379
    /**
380
     * Returns the displayed options defined for the specified parameter name.
381
     **/
382
    public String[] getOptions(String name) {
383
        return getParameter(name).getOptions();
1✔
384
    }
385

386
    /**
387
     * Returns the option values defined for the specified parameter name.
388
     **/
389
    public String[] getOptionValues(String name) {
390
        return getParameter(name).getOptionValues();
1✔
391
    }
392

393
    /**
394
     * Returns true if the named parameter accepts multiple values.
395
     **/
396
    public boolean isMultiValuedParameter(String name) {
397
        return getParameter(name).isMultiValuedParameter();
×
398
    }
399

400
    /**
401
     * Returns the number of text parameters in this form with the specified name.
402
     **/
403
    public int getNumTextParameters(String name) {
404
        return getParameter(name).getNumTextParameters();
1✔
405
    }
406

407
    /**
408
     * Returns true if the named parameter accepts free-form text.
409
     **/
410
    public boolean isTextParameter(String name) {
411
        return getParameter(name).isTextParameter();
×
412
    }
413

414
    /**
415
     * Returns true if this form is to be submitted using mime encoding (the default is URL encoding).
416
     **/
417
    @Override
418
    public boolean isSubmitAsMime() {
419
        return "multipart/form-data".equalsIgnoreCase(getAttribute("enctype"));
1✔
420
    }
421

422
    public FormScriptable getScriptableObject() {
423
        return (FormScriptable) getScriptingHandler();
1✔
424
    }
425

426
    /**
427
     * Resets all parameters to their initial values.
428
     */
429
    public void reset() {
430
        if (handleEvent("onreset")) {
1!
431
            resetControls();
1✔
432
        }
433
    }
1✔
434

435
    private void resetControls() {
436
        FormControl[] controls = getFormControls();
1✔
437
        for (FormControl control : controls) {
1✔
438
            control.reset();
1✔
439
        }
440
    }
1✔
441

442
    @Override
443
    public ScriptableDelegate newScriptable() {
444
        return new Scriptable();
1✔
445
    }
446

447
    // ---------------------------------- WebRequestSource methods
448
    // --------------------------------
449

450
    /**
451
     * Returns the character set encoding for this form.
452
     **/
453
    @Override
454
    public String getCharacterSet() {
455
        return _characterSet;
1✔
456
    }
457

458
    /**
459
     * Returns true if the named parameter accepts files for upload.
460
     **/
461
    @Override
462
    public boolean isFileParameter(String name) {
463
        return getParameter(name).isFileParameter();
1✔
464
    }
465

466
    /**
467
     * Returns an array containing the names of the parameters defined for this form.
468
     **/
469
    @Override
470
    public String[] getParameterNames() {
471
        ArrayList parameterNames = new ArrayList(getFormParameters().keySet());
1✔
472
        return (String[]) parameterNames.toArray(new String[parameterNames.size()]);
1✔
473
    }
474

475
    /**
476
     * Returns the multiple default values of the named parameter.
477
     **/
478
    @Override
479
    public String[] getParameterValues(String name) {
480
        final FormParameter parameter = getParameter(name);
1✔
481
        return parameter.getValues();
1✔
482
    }
483

484
    /**
485
     * Returns true if the named parameter is read-only. If more than one control exists with the same name, will return
486
     * true only if all such controls are read-only.
487
     **/
488
    public boolean isReadOnlyParameter(String name) {
489
        return getParameter(name).isReadOnlyParameter();
1✔
490
    }
491

492
    /**
493
     * Returns true if the named parameter is disabled. If more than one control exists with the same name, will return
494
     * true only if all such controls are read-only.
495
     **/
496
    public boolean isDisabledParameter(String name) {
497
        return getParameter(name).isDisabledParameter();
1✔
498
    }
499

500
    /**
501
     * Returns true if the named parameter is hidden. If more than one control exists with the same name, will return
502
     * true only if all such controls are hidden.
503
     **/
504
    public boolean isHiddenParameter(String name) {
505
        return getParameter(name).isHiddenParameter();
1✔
506
    }
507

508
    /**
509
     * Creates and returns a web request which will simulate the submission of this form with an unnamed submit button.
510
     **/
511
    @Override
512
    public WebRequest getRequest() {
513
        return getRequest((SubmitButton) null);
1✔
514
    }
515

516
    /**
517
     * Creates and returns a web request based on the current state of this form. No parameter validation will be done
518
     * and there is no guarantee over the order of parameters transmitted.
519
     */
520
    public WebRequest newUnvalidatedRequest() {
521
        return newUnvalidatedRequest(null);
1✔
522
    }
523

524
    /**
525
     * Records a parameter defined by including it in the destination URL. Ignores any parameters whose name matches a
526
     * form control.
527
     **/
528
    @Override
529
    protected void addPresetParameter(String name, String value) {
530
        FormControl[] formControls = getFormControls();
1✔
531
        for (FormControl formControl : formControls) {
1✔
532
            if (formControl.getName().equals(name)) {
1✔
533
                return;
1✔
534
            }
535
        }
536
        _presets.add(new PresetFormParameter(this, name, value));
1✔
537
    }
1✔
538

539
    @Override
540
    protected String getEmptyParameterValue() {
541
        return null;
1✔
542
    }
543

544
    // ---------------------------------- ParameterHolder methods
545
    // --------------------------------
546

547
    /**
548
     * Specifies the position at which an image button (if any) was clicked.
549
     **/
550
    @Override
551
    void selectImageButtonPosition(SubmitButton imageButton, int x, int y) {
552
        imageButton.setLocation(x, y);
1✔
553
    }
1✔
554

555
    /**
556
     * Iterates through the fixed, predefined parameters in this holder, recording them in the supplied parameter
557
     * processor.\ These parameters always go on the URL, no matter what encoding method is used.
558
     **/
559

560
    @Override
561
    void recordPredefinedParameters(ParameterProcessor processor) throws IOException {
562
        FormControl[] controls = getPresetParameters();
1✔
563
        for (FormControl control : controls) {
1✔
564
            control.addValues(processor, getCharacterSet());
1✔
565
        }
566
    }
1✔
567

568
    /**
569
     * Iterates through the parameters in this holder, recording them in the supplied parameter processor.
570
     **/
571
    @Override
572
    public void recordParameters(ParameterProcessor processor) throws IOException {
573
        FormControl[] controls = getFormControls();
1✔
574
        for (FormControl control : controls) {
1✔
575
            control.addValues(processor, getCharacterSet());
1✔
576
        }
577
    }
1✔
578

579
    /**
580
     * Removes a parameter name from this collection.
581
     **/
582
    @Override
583
    public void removeParameter(String name) {
584
        setParameter(name, NO_VALUES);
1✔
585
    }
1✔
586

587
    /**
588
     * Sets the value of a parameter in this form.
589
     *
590
     * @param name
591
     *            - the name of the parameter
592
     * @param value
593
     *            - the value of the parameter
594
     **/
595
    @Override
596
    public void setParameter(String name, String value) {
597
        setParameter(name, new String[] { value });
1✔
598
    }
1✔
599

600
    /**
601
     * Sets the multiple values of a parameter in this form. This is generally used when there are multiple controls
602
     * with the same name in the form.
603
     *
604
     * @param name
605
     * @param values
606
     */
607
    @Override
608
    public void setParameter(String name, final String[] values) {
609
        FormParameter parameter = getParameter(name);
1✔
610
        if (parameter.isUnknown()) {
1✔
611
            throw new NoSuchParameterException(name);
1✔
612
        }
613
        if (parameter.isFileParameter()) {
1✔
614
            if (values != NO_VALUES) {
1✔
615
                throw new InvalidFileParameterException(name, values);
1✔
616
            }
617
            parameter.setFiles(new UploadFileSpec[0]);
1✔
618
        } else {
619
            parameter.setValues(values);
1✔
620
        }
621

622
    }
1✔
623

624
    /**
625
     * Sets the multiple values of a file upload parameter in a web request.
626
     **/
627
    @Override
628
    public void setParameter(String name, UploadFileSpec[] files) {
629
        FormParameter parameter = getParameter(name);
1✔
630
        if (parameter == null || !parameter.isFileParameter()) {
1!
631
            throw new NoSuchParameterException(name);
1✔
632
        }
633
        parameter.setFiles(files);
1✔
634
    }
1✔
635

636
    /**
637
     * Sets the single value of a file upload parameter in this form. A more convenient way to do this than using
638
     * {@link #setParameter(String,com.meterware.httpunit.protocol.UploadFileSpec[])}
639
     */
640
    public void setParameter(String name, File file) {
641
        setParameter(name, new UploadFileSpec[] { new UploadFileSpec(file) });
1✔
642
    }
1✔
643

644
    /**
645
     * Toggles the value of the specified checkbox parameter.
646
     *
647
     * @param name
648
     *            the name of the checkbox parameter
649
     *
650
     * @throws IllegalArgumentException
651
     *             if the specified parameter is not a checkbox or there is more than one control with that name.
652
     */
653
    public void toggleCheckbox(String name) {
654
        FormParameter parameter = getParameter(name);
1✔
655
        if (parameter == null) {
1!
656
            throw new NoSuchParameterException(name);
×
657
        }
658
        parameter.toggleCheckbox();
1✔
659
    }
1✔
660

661
    /**
662
     * Toggles the value of the specified checkbox parameter.
663
     *
664
     * @param name
665
     *            the name of the checkbox parameter
666
     * @param value
667
     *            of the checkbox parameter
668
     *
669
     * @throws IllegalArgumentException
670
     *             if the specified parameter is not a checkbox or if there is no checkbox with the specified name and
671
     *             value.
672
     */
673
    public void toggleCheckbox(String name, String value) {
674
        FormParameter parameter = getParameter(name);
1✔
675
        if (parameter == null) {
1!
676
            throw new NoSuchParameterException(name);
×
677
        }
678
        parameter.toggleCheckbox(value);
1✔
679
    }
1✔
680

681
    /**
682
     * Sets the value of the specified checkbox parameter.
683
     *
684
     * @param name
685
     *            the name of the checkbox parameter
686
     * @param state
687
     *            the new state of the checkbox
688
     *
689
     * @throws IllegalArgumentException
690
     *             if the specified parameter is not a checkbox or there is more than one control with that name.
691
     */
692
    public void setCheckbox(String name, boolean state) {
693
        FormParameter parameter = getParameter(name);
1✔
694
        if (parameter == null) {
1!
695
            throw new NoSuchParameterException(name);
×
696
        }
697
        parameter.setValue(state);
1✔
698
    }
1✔
699

700
    /**
701
     * Sets the value of the specified checkbox parameter.
702
     *
703
     * @param name
704
     *            the name of the checkbox parameter
705
     * @param value
706
     *            of the checkbox parameter
707
     * @param state
708
     *            the new state of the checkbox
709
     *
710
     * @throws IllegalArgumentException
711
     *             if the specified parameter is not a checkbox or if there is no checkbox with the specified name and
712
     *             value.
713
     */
714
    public void setCheckbox(String name, String value, boolean state) {
715
        FormParameter parameter = getParameter(name);
1✔
716
        if (parameter == null) {
1!
717
            throw new NoSuchParameterException(name);
×
718
        }
719
        parameter.setValue(value, state);
1✔
720
    }
1✔
721

722
    /**
723
     * Scriptable implementation for the WebForm
724
     */
725
    public class Scriptable extends HTMLElementScriptable implements NamedDelegate, IdentifiedDelegate, FormScriptable {
726
        public String getAction() {
727
            return WebForm.this.getAction();
1✔
728
        }
729

730
        @Override
731
        public void setAction(String newAction) {
732
            setDestination(newAction);
1✔
733
            _presetParameters = null;
1✔
734
        }
1✔
735

736
        public void submit() throws IOException, SAXException {
737
            submitRequest(getScriptedSubmitRequest());
1✔
738
        }
1✔
739

740
        public void reset() throws IOException, SAXException {
741
            resetControls();
1✔
742
        }
1✔
743

744
        /**
745
         * return the name of the WebForm
746
         *
747
         * @return the name
748
         */
749
        @Override
750
        public String getName() {
751
            return WebForm.this.getName().length() != 0 ? WebForm.this.getName() : WebForm.this.getID();
1✔
752
        }
753

754
        /**
755
         * return the id of the WebForm
756
         *
757
         * @return the id
758
         */
759
        @Override
760
        public String getID() {
761
            return WebForm.this.getID();
1✔
762
        }
763

764
        /**
765
         * get the Object for the given propertyName
766
         *
767
         * @param propertyName
768
         *            - the name of the property to get
769
         *
770
         * @return the Object for the property
771
         */
772
        @Override
773
        public Object get(String propertyName) {
774
            if (propertyName.equals("target")) {
1✔
775
                return getTarget();
1✔
776
            }
777
            if (propertyName.equals("action")) {
1✔
778
                return getAction();
1✔
779
            }
780
            if (propertyName.equals("length")) {
1✔
781
                return Integer.valueOf(getFormControls().length);
1✔
782
            }
783
            final FormParameter parameter = getParameter(propertyName);
1✔
784
            if (!parameter.isUnknown()) {
1✔
785
                return parameter.getScriptableObject();
1✔
786
            }
787
            FormControl control = getControlWithID(propertyName);
1✔
788
            return control == null ? super.get(propertyName) : control.getScriptingHandler();
1✔
789
        }
790

791
        /**
792
         * Sets the value of the named property. Will throw a runtime exception if the property does not exist or cannot
793
         * accept the specified value.
794
         *
795
         * @param propertyName
796
         *            - the name of the property
797
         * @param value
798
         *            - the new value
799
         **/
800
        @Override
801
        public void set(String propertyName, Object value) {
802
            if (propertyName.equals("target")) {
1✔
803
                setTargetAttribute(value.toString());
1✔
804
            } else if (propertyName.equals("action")) {
1✔
805
                setAction(value.toString());
1✔
806
            } else if (propertyName.equals("name")) {
1✔
807
                getElement().setAttribute("name", value.toString());
1✔
808
            } else if (value instanceof String) {
1!
809
                setParameterValue(propertyName, (String) value);
×
810
            } else if (value instanceof Number) {
1!
811
                setParameterValue(propertyName, HttpUnitUtils.trimmedValue((Number) value));
1✔
812
            } else {
813
                super.set(propertyName, value);
×
814
            }
815
        }
1✔
816

817
        @Override
818
        public void setParameterValue(String name, String value) {
819
            final Object scriptableObject = getParameter(name).getScriptableObject();
1✔
820
            if (scriptableObject instanceof ScriptableDelegate) {
1!
821
                ((ScriptableDelegate) scriptableObject).set("value", value);
1✔
822
            } else if (scriptableObject instanceof ScriptableDelegate[]) {
×
823
                ((ScriptableDelegate[]) scriptableObject)[0].set("value", value);
×
824
            }
825
        }
1✔
826

827
        public ScriptableDelegate[] getElementDelegates() {
828
            FormControl[] controls = getFormControls();
1✔
829
            ScriptableDelegate[] result = new ScriptableDelegate[controls.length];
1✔
830
            for (int i = 0; i < result.length; i++) {
1✔
831
                result[i] = (ScriptableDelegate) controls[i].getScriptingHandler();
1✔
832
            }
833
            return result;
1✔
834
        }
835

836
        public ScriptableDelegate[] getElementsByTagName(String name) throws SAXException {
837
            return getDelegates(getHTMLPage().getElementsByTagName(getElement(), name));
1✔
838
        }
839

840
        Scriptable() {
1✔
841
            super(WebForm.this);
1✔
842
        }
1✔
843
    }
844

845
    // ---------------------------------- package members
846
    // --------------------------------
847

848
    /**
849
     * Contructs a web form given the URL of its source page and the DOM extracted from that page.
850
     **/
851
    WebForm(WebResponse response, URL baseURL, Node node, FrameSelector frame, String defaultTarget,
852
            String characterSet, ElementRegistry registry) {
853
        super(response, node, baseURL, "action", frame, defaultTarget);
1✔
854
        _characterSet = characterSet;
1✔
855
        _registry = registry;
1✔
856
        _domElement = (HTMLFormElement) node;
1✔
857
    }
1✔
858

859
    /**
860
     * Returns the form control which is part of this form with the specified ID.
861
     */
862
    public FormControl getControlWithID(String id) {
863
        FormControl[] controls = getFormControls();
1✔
864
        for (FormControl control : controls) {
1✔
865
            if (control.getID().equals(id)) {
1✔
866
                return control;
1✔
867
            }
868
        }
869
        return null;
1✔
870
    }
871

872
    // ---------------------------------- private members
873
    // --------------------------------
874

875
    private SubmitButton getDefaultButton() {
876
        if (getSubmitButtons().length == 1) {
1✔
877
            return getSubmitButtons()[0];
1✔
878
        }
879
        return getSubmitButton("");
1✔
880
    }
881

882
    /**
883
     * get the Vector of submit buttons - will always contain at least one button - if the original vector has none a
884
     * faked submit button will be added
885
     *
886
     * @return a Vector with the submit buttons
887
     */
888
    private Vector getSubmitButtonVector() {
889
        if (_buttonVector == null) {
1✔
890
            _buttonVector = new Vector<>();
1✔
891
            FormControl[] controls = getFormControls();
1✔
892
            for (FormControl control : controls) {
1✔
893
                if (control instanceof SubmitButton) {
1✔
894
                    SubmitButton sb = (SubmitButton) control;
1✔
895
                    sb.rememberEnableState();
1✔
896
                    _buttonVector.add(sb);
1✔
897
                }
898
            }
899

900
            /**
901
             * make sure that there is always at least one submit button if none is in the Vector add a faked one
902
             */
903
            if (_buttonVector.isEmpty()) {
1✔
904
                _buttonVector.addElement(SubmitButton.createFakeSubmitButton(this));
1✔
905
            }
906
        }
907
        return _buttonVector;
1✔
908
    }
909

910
    private FormControl[] getPresetParameters() {
911
        if (_presetParameters == null) {
1✔
912
            _presets = new ArrayList<>();
1✔
913
            loadDestinationParameters();
1✔
914
            _presetParameters = (FormControl[]) _presets.toArray(new FormControl[_presets.size()]);
1✔
915
        }
916
        return _presetParameters;
1✔
917
    }
918

919
    FormControl newFormControl(Node child) {
920
        return FormControl.newFormParameter(this, child);
1✔
921
    }
922

923
    /**
924
     * Returns an array of form parameter attributes for this form.
925
     **/
926
    private FormControl[] getFormControls() {
927
        HTMLCollection controlElements = _domElement.getElements();
1✔
928
        FormControl[] controls = new FormControl[controlElements.getLength()];
1✔
929
        for (int i = 0; i < controls.length; i++) {
1✔
930
            controls[i] = getControlForNode(controlElements.item(i));
1✔
931
        }
932
        return controls;
1✔
933
    }
934

935
    private FormControl getControlForNode(Node node) {
936
        if (_registry.hasNode(node)) {
1!
937
            return (FormControl) _registry.getRegisteredElement(node);
1✔
938
        }
939
        return (FormControl) _registry.registerElement(node, newFormControl(node));
×
940
    }
941

942
    /**
943
     * get the form parameter with the given name
944
     *
945
     * @param name
946
     *
947
     * @return the form parameter with this name
948
     */
949
    public FormParameter getParameter(String name) {
950
        final FormParameter parameter = (FormParameter) getFormParameters().get(name);
1✔
951
        return parameter != null ? parameter : FormParameter.getUNKNOWN_PARAMETER();
1✔
952
    }
953

954
    /**
955
     * Returns a map of parameter name to form parameter objects. Each form parameter object represents the set of form
956
     * controls with a particular name. Unnamed parameters are ignored.
957
     */
958
    private Map getFormParameters() {
959
        Map formParameters = new HashMap<>();
1✔
960
        loadFormParameters(formParameters, getPresetParameters());
1✔
961
        loadFormParameters(formParameters, getFormControls());
1✔
962
        return formParameters;
1✔
963
    }
964

965
    private void loadFormParameters(Map formParameters, FormControl[] controls) {
966
        for (FormControl control : controls) {
1✔
967
            if (control.getName().isEmpty()) {
1✔
968
                continue;
1✔
969
            }
970
            FormParameter parameter = (FormParameter) formParameters.get(control.getName());
1✔
971
            if (parameter == null) {
1✔
972
                parameter = new FormParameter();
1✔
973
                formParameters.put(control.getName(), parameter);
1✔
974
            }
975
            parameter.addControl(control);
1✔
976
        }
977
    }
1✔
978

979
    static {
980
        MATCH_NAME = (htmlElement, criteria) -> HttpUnitUtils.matches(((WebForm) htmlElement).getName(),
1✔
981
                (String) criteria);
982

983
    }
1✔
984

985
    // ===========================---===== exception class
986
    // NoSuchParameterException =========================================
987

988
    /**
989
     * This exception is thrown on an attempt to set a file parameter to a non file type
990
     **/
991
    class InvalidFileParameterException extends IllegalRequestParameterException {
992

993
        private static final long serialVersionUID = 1L;
994

995
        /**
996
         * construct a new InvalidFileParameterException for the given parameter name and value list
997
         *
998
         * @param parameterName
999
         * @param values
1000
         */
1001
        InvalidFileParameterException(String parameterName, String[] values) {
1✔
1002
            _parameterName = parameterName;
1✔
1003
            _values = values;
1✔
1004
        }
1✔
1005

1006
        /**
1007
         * get the message for this exception
1008
         */
1009
        @Override
1010
        public String getMessage() {
1011
            StringBuilder valueList = new StringBuilder();
1✔
1012
            String delim = "";
1✔
1013
            for (String _value : _values) {
1✔
1014
                valueList.append(delim).append("'").append(_value).append("'");
1✔
1015
                delim = ", ";
1✔
1016
            }
1017
            return "The file parameter with the name '" + _parameterName
1✔
1018
                    + "' must have type File but the string values " + valueList.append(" where supplied").toString();
1✔
1019
        }
1020

1021
        private String _parameterName;
1022
        private String[] _values;
1023
    }
1024

1025
    /**
1026
     * This exception is thrown on an attempt to set a parameter to a value not permitted to it by the form.
1027
     **/
1028
    class NoSuchParameterException extends IllegalRequestParameterException {
1029

1030
        private static final long serialVersionUID = 1L;
1031

1032
        NoSuchParameterException(String parameterName) {
1✔
1033
            _parameterName = parameterName;
1✔
1034
        }
1✔
1035

1036
        @Override
1037
        public String getMessage() {
1038
            return "No parameter named '" + _parameterName + "' is defined in the form";
1✔
1039
        }
1040

1041
        private String _parameterName;
1042

1043
    }
1044

1045
    // ============================= exception class
1046
    // IllegalUnnamedSubmitButtonException
1047
    // ======================================
1048

1049
    /**
1050
     * This exception is thrown on an attempt to define a form request with a button not defined on that form.
1051
     **/
1052
    class IllegalUnnamedSubmitButtonException extends IllegalRequestParameterException {
1053

1054
        private static final long serialVersionUID = 1L;
1055

1056
        IllegalUnnamedSubmitButtonException() {
1✔
1057
        }
1✔
1058

1059
        @Override
1060
        public String getMessage() {
1061
            return "This form has more than one submit button, none unnamed. You must specify the button to be used.";
×
1062
        }
1063

1064
    }
1065

1066
    // ============================= exception class
1067
    // IllegalSubmitButtonException ======================================
1068

1069
    /**
1070
     * This exception is thrown on an attempt to define a form request with a button not defined on that form.
1071
     **/
1072
    class IllegalSubmitButtonException extends IllegalRequestParameterException {
1073

1074
        private static final long serialVersionUID = 1L;
1075

1076
        IllegalSubmitButtonException(SubmitButton button) {
1✔
1077
            _name = button.getName();
1✔
1078
            _value = button.getValue();
1✔
1079
        }
1✔
1080

1081
        IllegalSubmitButtonException(String name, String value) {
1✔
1082
            _name = name;
1✔
1083
            _value = value;
1✔
1084
        }
1✔
1085

1086
        @Override
1087
        public String getMessage() {
1088
            return "Specified submit button (name=\"" + _name + "\" value=\"" + _value + "\") not part of this form.";
×
1089
        }
1090

1091
        private String _name;
1092
        private String _value;
1093

1094
    }
1095

1096
    // ============================= exception class
1097
    // IllegalUnnamedSubmitButtonException
1098
    // ======================================
1099

1100
}
1101

1102
// ========================================== class PresetFormParameter
1103
// =================================================
1104

1105
class PresetFormParameter extends FormControl {
1106

1107
    PresetFormParameter(WebForm form, String name, String value) {
1108
        super(form);
1✔
1109
        _name = name;
1✔
1110
        _value = value;
1✔
1111
    }
1✔
1112

1113
    /**
1114
     * Returns the name of this control..
1115
     **/
1116
    @Override
1117
    public String getName() {
1118
        return _name;
1✔
1119
    }
1120

1121
    /**
1122
     * Returns true if this control is read-only.
1123
     **/
1124
    @Override
1125
    public boolean isReadOnly() {
1126
        return true;
×
1127
    }
1128

1129
    /**
1130
     * Returns true if this control accepts free-form text.
1131
     **/
1132
    @Override
1133
    public boolean isTextControl() {
1134
        return true;
×
1135
    }
1136

1137
    /**
1138
     * Remove any required values for this control from the list, throwing an exception if they are missing.
1139
     **/
1140
    @Override
1141
    void claimRequiredValues(List values) {
1142
        if (_value != null) {
×
1143
            claimValueIsRequired(values, _value);
×
1144
        }
1145
    }
×
1146

1147
    @Override
1148
    public String getType() {
1149
        return UNDEFINED_TYPE;
×
1150
    }
1151

1152
    /**
1153
     * Returns the current value(s) associated with this control. These values will be transmitted to the server if the
1154
     * control is 'successful'.
1155
     **/
1156
    @Override
1157
    public String[] getValues() {
1158
        if (_values == null) {
×
1159
            _values = new String[] { _value };
×
1160
        }
1161
        return _values;
×
1162
    }
1163

1164
    @Override
1165
    protected void addValues(ParameterProcessor processor, String characterSet) throws IOException {
1166
        processor.addParameter(_name, _value, characterSet);
1✔
1167
    }
1✔
1168

1169
    private String _name;
1170
    private String _value;
1171
    private String[] _values;
1172
}
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