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

hazendaz / httpunit / 656

06 Dec 2025 09:11PM UTC coverage: 80.452% (+0.02%) from 80.435%
656

push

github

hazendaz
[maven-release-plugin] prepare for next development iteration

3213 of 4105 branches covered (78.27%)

Branch coverage included in aggregate %.

8245 of 10137 relevant lines covered (81.34%)

0.81 hits per line

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

86.65
/src/main/java/com/meterware/httpunit/FormControl.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.controls.SelectionFormControl;
23
import com.meterware.httpunit.dom.HTMLControl;
24
import com.meterware.httpunit.dom.HTMLInputElementImpl;
25
import com.meterware.httpunit.dom.HTMLSelectElementImpl;
26
import com.meterware.httpunit.dom.HTMLTextAreaElementImpl;
27
import com.meterware.httpunit.protocol.ParameterProcessor;
28
import com.meterware.httpunit.protocol.UploadFileSpec;
29
import com.meterware.httpunit.scripting.Input;
30
import com.meterware.httpunit.scripting.ScriptableDelegate;
31

32
import java.io.IOException;
33
import java.util.Iterator;
34
import java.util.List;
35
import java.util.Set;
36

37
import org.w3c.dom.Node;
38
import org.xml.sax.SAXException;
39

40
/**
41
 * Represents a control in an HTML form.
42
 **/
43
public abstract class FormControl extends HTMLElementBase {
44

45
    /** The Constant NO_VALUE. */
46
    static final String[] NO_VALUE = {};
1✔
47

48
    /** The form. */
49
    private final WebForm _form;
50

51
    /** The control. */
52
    private HTMLControl _control;
53

54
    /** The Constant UNDEFINED_TYPE. */
55
    public static final String UNDEFINED_TYPE = "undefined";
56

57
    /** The Constant BUTTON_TYPE. */
58
    public static final String BUTTON_TYPE = "button";
59

60
    /** The Constant RESET_BUTTON_TYPE. */
61
    public static final String RESET_BUTTON_TYPE = "reset";
62

63
    /** The Constant SUBMIT_BUTTON_TYPE. */
64
    public static final String SUBMIT_BUTTON_TYPE = "submit";
65

66
    /** The Constant IMAGE_BUTTON_TYPE. */
67
    public static final String IMAGE_BUTTON_TYPE = "image";
68

69
    /** The Constant RADIO_BUTTON_TYPE. */
70
    public static final String RADIO_BUTTON_TYPE = "radio";
71

72
    /** The Constant CHECKBOX_TYPE. */
73
    public static final String CHECKBOX_TYPE = "checkbox";
74

75
    /** The Constant TEXT_TYPE. */
76
    public static final String TEXT_TYPE = "text";
77

78
    /** The Constant PASSWORD_TYPE. */
79
    public static final String PASSWORD_TYPE = "password";
80

81
    /** The Constant HIDDEN_TYPE. */
82
    public static final String HIDDEN_TYPE = "hidden";
83

84
    /** The Constant TEXTAREA_TYPE. */
85
    public static final String TEXTAREA_TYPE = "textarea";
86

87
    /** The Constant FILE_TYPE. */
88
    public static final String FILE_TYPE = "file";
89

90
    /** The Constant SINGLE_TYPE. */
91
    public static final String SINGLE_TYPE = "select-one";
92

93
    /** The Constant MULTIPLE_TYPE. */
94
    public static final String MULTIPLE_TYPE = "select-multiple";
95

96
    /**
97
     * Return the type of the control, as seen from JavaScript.
98
     *
99
     * @return the type
100
     */
101
    public abstract String getType();
102

103
    /**
104
     * New selection option.
105
     *
106
     * @return the scriptable delegate
107
     */
108
    static ScriptableDelegate newSelectionOption() {
109
        return new SelectionFormControl.Option();
1✔
110
    }
111

112
    /**
113
     * Instantiates a new form control.
114
     *
115
     * @param form
116
     *            the form
117
     */
118
    FormControl(WebForm form) {
119
        this(form, newEmptyControlElement(form));
1✔
120
    }
1✔
121

122
    /**
123
     * New empty control element.
124
     *
125
     * @param form
126
     *            the form
127
     *
128
     * @return the HTML control
129
     */
130
    private static HTMLControl newEmptyControlElement(WebForm form) {
131
        return (HTMLControl) form.getElement().getOwnerDocument().createElement("input");
1✔
132
    }
133

134
    /**
135
     * initialize the given form control from a Webform and a HTMLControl.
136
     *
137
     * @param form
138
     *            the form
139
     * @param control
140
     *            the control
141
     */
142
    protected FormControl(WebForm form, HTMLControl control) {
143
        super(control);
1✔
144
        _control = control;
1✔
145
        _form = form;
1✔
146
        supportAttribute("tabindex");
1✔
147
        supportAttribute("disabled");
1✔
148
        // Add all custom attributes
149
        Set customAttributes = HttpUnitOptions.getCustomAttributes();
1✔
150
        if (customAttributes != null) {
1✔
151
            for (Iterator iter = customAttributes.iterator(); iter.hasNext();) {
1✔
152
                supportAttribute((String) iter.next());
1✔
153
            }
154
        }
155
    }
1✔
156

157
    /**
158
     * Returns the current value(s) associated with this control. These values will be transmitted to the server if the
159
     * control is 'successful'.
160
     *
161
     * @return the values
162
     */
163
    protected abstract String[] getValues();
164

165
    /**
166
     * Returns either a single delegate object or potentially an array of delegates as needed, given the form control.
167
     * This default implementation returns the scriptable delegate for the control.
168
     *
169
     * @return the delegate
170
     */
171
    Object getDelegate() {
172
        return getScriptingHandler();
1✔
173
    }
174

175
    /**
176
     * Gets the form.
177
     *
178
     * @return the form
179
     */
180
    protected final WebForm getForm() {
181
        return _form;
1✔
182
    }
183

184
    @Override
185
    public ScriptableDelegate getParentDelegate() {
186
        return (ScriptableDelegate) getForm().getScriptingHandler();
1✔
187
    }
188

189
    /**
190
     * Returns the values permitted in this control. Does not apply to text or file controls.
191
     *
192
     * @return the option values
193
     */
194
    public String[] getOptionValues() {
195
        return NO_VALUE;
×
196
    }
197

198
    /**
199
     * Returns the list of values displayed by this control, if any.
200
     *
201
     * @return the displayed options
202
     */
203
    protected String[] getDisplayedOptions() {
204
        return NO_VALUE;
×
205
    }
206

207
    /**
208
     * Returns true if this control is read-only.
209
     *
210
     * @return true, if is read only
211
     */
212
    protected boolean isReadOnly() {
213
        return isDisabled() || _control.getReadOnly();
1✔
214
    }
215

216
    /**
217
     * Returns true if this control is hidden.
218
     *
219
     * @return true, if is hidden
220
     */
221
    public boolean isHidden() {
222
        return false;
1✔
223
    }
224

225
    /**
226
     * Sets the disabled.
227
     *
228
     * @param disabled
229
     *            the new disabled
230
     */
231
    void setDisabled(boolean disabled) {
232
        _control.setDisabled(disabled);
1✔
233
    }
1✔
234

235
    /**
236
     * Returns true if this control is disabled, meaning that it will not send a value to the server as part of a
237
     * request.
238
     *
239
     * @return true, if is disabled
240
     */
241
    public boolean isDisabled() {
242
        return _control.getDisabled();
1✔
243
    }
244

245
    /**
246
     * Returns true if this control accepts free-form text.
247
     *
248
     * @return true, if is text control
249
     */
250
    boolean isTextControl() {
251
        return false;
×
252
    }
253

254
    /**
255
     * Returns true if only one control of this kind with this name can have a value. This is true for radio buttons.
256
     *
257
     * @return true, if is exclusive
258
     */
259
    boolean isExclusive() {
260
        return false;
1✔
261
    }
262

263
    /**
264
     * Returns true if a single control can have multiple values.
265
     *
266
     * @return true, if is multi valued
267
     */
268
    protected boolean isMultiValued() {
269
        return false;
×
270
    }
271

272
    /**
273
     * Returns true if this control accepts a file for upload.
274
     *
275
     * @return true, if is file parameter
276
     */
277
    boolean isFileParameter() {
278
        return false;
1✔
279
    }
280

281
    /**
282
     * Adds the values.
283
     *
284
     * @param processor
285
     *            the processor
286
     * @param characterSet
287
     *            the character set
288
     *
289
     * @throws IOException
290
     *             Signals that an I/O exception has occurred.
291
     */
292
    protected abstract void addValues(ParameterProcessor processor, String characterSet) throws IOException;
293

294
    /**
295
     * Remove any required values for this control from the list, throwing an exception if they are missing.
296
     *
297
     * @param values
298
     *            the values
299
     */
300
    void claimRequiredValues(List values) {
301
    }
1✔
302

303
    /**
304
     * Sets this control to the next compatible value from the list, removing it from the list.
305
     *
306
     * @param values
307
     *            the values
308
     */
309
    void claimValue(List values) {
310
    }
1✔
311

312
    /**
313
     * Sets this control to the next compatible value from the list, removing it from the list.
314
     *
315
     * @param values
316
     *            the values
317
     */
318
    protected void claimUniqueValue(List values) {
319
    }
1✔
320

321
    /**
322
     * Specifies a file to be uploaded via this control.
323
     *
324
     * @param files
325
     *            the files
326
     */
327
    void claimUploadSpecification(List files) {
328
    }
×
329

330
    /**
331
     * Resets this control to its initial value.
332
     **/
333
    protected void reset() {
334
        _control.reset();
1✔
335
    }
1✔
336

337
    /**
338
     * Toggles the value of this control.
339
     */
340
    public void toggle() {
341
        throw new FormParameter.IllegalCheckboxParameterException(getName(), "toggleCheckbox");
1✔
342
    }
343

344
    /**
345
     * Sets the state of this boolean control.
346
     *
347
     * @param state
348
     *            the new state
349
     */
350
    public void setState(boolean state) {
351
        throw new FormParameter.IllegalCheckboxParameterException(getName(), "setCheckbox");
×
352
    }
353

354
    /**
355
     * Performs the 'onChange' event defined for this control.
356
     *
357
     * @deprecated since 1.7 use doOnChangeEvent instead
358
     */
359
    @Deprecated
360
    protected void sendOnChangeEvent() {
361
        doOnChangeEvent();
1✔
362
    }
1✔
363

364
    /**
365
     * Performs the 'onchange' event defined for this control.
366
     *
367
     * @return true, if successful
368
     */
369
    protected boolean doOnChangeEvent() {
370
        return handleEvent("onchange");
1✔
371
    }
372

373
    /**
374
     * Performs the 'onClick' event defined for this control.
375
     *
376
     * @deprecated since 1.7 use doOnClickEvent instead
377
     */
378
    @Deprecated
379
    protected void sendOnClickEvent() {
380
        doOnClickEvent();
1✔
381
    }
1✔
382

383
    /**
384
     * Performs the 'onClick' event defined for this control.
385
     *
386
     * @return true, if successful
387
     */
388
    protected boolean doOnClickEvent() {
389
        return handleEvent("onclick");
1✔
390
    }
391

392
    /**
393
     * Performs the 'onMouseUp' event defined for this control.
394
     *
395
     * @deprecated since 1.7 use doOnMouseUpEvent instead
396
     */
397
    @Deprecated
398
    protected void sendOnMouseUpEvent() {
399
        doOnMouseUpEvent();
×
400
    }
×
401

402
    /**
403
     * Performs the 'onMouseUp' event defined for this control.
404
     *
405
     * @return true, if successful
406
     */
407
    protected boolean doOnMouseUpEvent() {
408
        return handleEvent("onmouseup");
×
409
    }
410

411
    /**
412
     * Performs the 'onMouseDown' event defined for this control.
413
     *
414
     * @deprecated since 1.7 use doOnMouseDownEvent instead
415
     */
416
    @Deprecated
417
    protected void sendOnMouseDownEvent() {
418
        doOnMouseDownEvent();
×
419
    }
×
420

421
    /**
422
     * Performs the 'onMouseDown' event defined for this control.
423
     *
424
     * @return true, if successful
425
     */
426
    protected boolean doOnMouseDownEvent() {
427
        return handleEvent("onmousedown");
×
428
    }
429

430
    /**
431
     * Creates and returns a scriptable object for this control. Subclasses should override this if they use a different
432
     * implementation of Scriptable.
433
     */
434
    @Override
435
    public ScriptableDelegate newScriptable() {
436
        return new Scriptable();
×
437
    }
438

439
    /**
440
     * Returns the value of this control in the form. If no value is specified, defaults to the empty string.
441
     *
442
     * @return the value attribute
443
     */
444
    protected String getValueAttribute() {
445
        return "";
×
446
    }
447

448
    /**
449
     * Sets the value of this control in the form.
450
     *
451
     * @param value
452
     *            the new value attribute
453
     */
454
    protected void setValueAttribute(String value) {
455
    }
×
456

457
    /**
458
     * Removes the specified required value from the list of values, throwing an exception if it is missing.
459
     *
460
     * @param values
461
     *            the values
462
     * @param value
463
     *            the value
464
     */
465
    protected final void claimValueIsRequired(List values, final String value) {
466
        if (!values.contains(value)) {
1✔
467
            throw new MissingParameterValueException(getName(), value,
1✔
468
                    (String[]) values.toArray(new String[values.size()]));
1✔
469
        }
470
        values.remove(value);
1✔
471
    }
1✔
472

473
    /**
474
     * Gets the control element tags.
475
     *
476
     * @return the control element tags
477
     */
478
    static String[] getControlElementTags() {
479
        return new String[] { "textarea", "select", "button", "input" };
1✔
480
    }
481

482
    /**
483
     * return the FormControl for the given parameter node in a form.
484
     *
485
     * @param form
486
     *            - the form in which the parameter is defined
487
     * @param node
488
     *            - the node in which the parameter is defined
489
     *
490
     * @return the form control
491
     */
492
    static FormControl newFormParameter(WebForm form, Node node) {
493
        if (node.getNodeType() != Node.ELEMENT_NODE) {
1!
494
            return null;
×
495
        }
496
        if (node.getNodeName().equalsIgnoreCase("textarea")) {
1✔
497
            return new TextAreaFormControl(form, (HTMLTextAreaElementImpl) node);
1✔
498
        }
499
        if (node.getNodeName().equalsIgnoreCase("select")) {
1✔
500
            return new SelectionFormControl(form, (HTMLSelectElementImpl) node);
1✔
501
        }
502
        if (node.getNodeName().equalsIgnoreCase("button")) {
1✔
503
            HTMLControl control = (HTMLControl) node;
1✔
504
            final String type = control.getType();
1✔
505
            if (type.equalsIgnoreCase(SUBMIT_BUTTON_TYPE)) {
1✔
506
                return new SubmitButton(form, control);
1✔
507
            }
508
            if (type.equalsIgnoreCase(RESET_BUTTON_TYPE)) {
1✔
509
                return new ResetButton(form, control);
1✔
510
            }
511
            return new Button(form, control);
1✔
512
        }
513
        if (!node.getNodeName().equalsIgnoreCase("input")) {
1!
514
            return null;
×
515
        }
516
        HTMLInputElementImpl element = (HTMLInputElementImpl) node;
1✔
517
        final String type = element.getType();
1✔
518
        if (type.equalsIgnoreCase(TEXT_TYPE)) {
1✔
519
            return new TextFieldFormControl(form, element);
1✔
520
        }
521
        if (type.equalsIgnoreCase(PASSWORD_TYPE)) {
1✔
522
            return new PasswordFieldFormControl(form, element);
1✔
523
        }
524
        if (type.equalsIgnoreCase(HIDDEN_TYPE)) {
1✔
525
            return new HiddenFieldFormControl(form, element);
1✔
526
        }
527
        if (type.equalsIgnoreCase(RADIO_BUTTON_TYPE)) {
1✔
528
            return new RadioButtonFormControl(form, element);
1✔
529
        }
530
        if (type.equalsIgnoreCase(CHECKBOX_TYPE)) {
1✔
531
            return new CheckboxFormControl(form, element);
1✔
532
        }
533
        if (type.equalsIgnoreCase(SUBMIT_BUTTON_TYPE) || type.equalsIgnoreCase(IMAGE_BUTTON_TYPE)) {
1✔
534
            return new SubmitButton(form, element);
1✔
535
        }
536
        if (type.equalsIgnoreCase(BUTTON_TYPE)) {
1✔
537
            return new Button(form, (HTMLControl) node);
1✔
538
        }
539
        if (type.equalsIgnoreCase(RESET_BUTTON_TYPE)) {
1✔
540
            return new ResetButton(form, (HTMLControl) node);
1✔
541
        }
542
        if (type.equalsIgnoreCase(FILE_TYPE)) {
1✔
543
            return new FileSubmitFormControl(form, element);
1✔
544
        }
545
        return new TextFieldFormControl(form, element);
1✔
546
    }
547

548
    /**
549
     * Empty if null.
550
     *
551
     * @param value
552
     *            the value
553
     *
554
     * @return the string
555
     */
556
    protected String emptyIfNull(String value) {
557
        return value == null ? "" : value;
1✔
558
    }
559

560
    /**
561
     * implementation of Scriptable input elements.
562
     */
563
    public class Scriptable extends HTMLElementScriptable implements Input {
564

565
        /**
566
         * get my Name
567
         *
568
         * @return the name of this scriptable
569
         */
570
        @Override
571
        public String getName() {
572
            return FormControl.this.getName();
1✔
573
        }
574

575
        /**
576
         * get my ID
577
         *
578
         * @return the id of this scriptable
579
         */
580
        @Override
581
        public String getID() {
582
            return FormControl.this.getID();
1✔
583
        }
584

585
        /**
586
         * construct a Scriptable.
587
         */
588
        public Scriptable() {
1✔
589
            super(FormControl.this);
1✔
590
        }
1✔
591

592
        /**
593
         * get the given property
594
         *
595
         * @param propertyName
596
         *            - the name of the property to get
597
         */
598
        @Override
599
        public Object get(String propertyName) {
600
            if (propertyName.equalsIgnoreCase("name")) {
1✔
601
                return FormControl.this.getName();
1✔
602
            }
603
            if (propertyName.equalsIgnoreCase("type")) {
1✔
604
                return FormControl.this.getType();
1✔
605
            }
606
            return super.get(propertyName);
1✔
607
        }
608

609
        /**
610
         * set the given property to the given value
611
         *
612
         * @param propertyName
613
         *            - the property to set
614
         * @param value
615
         *            - the value to use
616
         */
617
        @Override
618
        public void set(String propertyName, Object value) {
619
            if (propertyName.equalsIgnoreCase("value")) {
1✔
620
                setValueAttribute(value.toString());
1✔
621
            } else if (propertyName.equalsIgnoreCase("disabled")) {
1!
622
                setDisabled(value instanceof Boolean && ((Boolean) value).booleanValue());
1!
623
            } else {
624
                super.set(propertyName, value);
×
625
            }
626
        }
1✔
627

628
        /**
629
         * set the given attribute to the given value
630
         *
631
         * @param attributeName
632
         *            - the name of the attribute to set
633
         * @param value
634
         *            - the value to use
635
         */
636
        @Override
637
        public void setAttribute(String attributeName, Object value) {
638
            // Value set by JavaScript, make sure attribute is supported
639
            supportAttribute(attributeName);
1✔
640
            super.setAttribute(attributeName, value);
1✔
641
        }
1✔
642

643
        /**
644
         * allow calling click for this control
645
         */
646
        @Override
647
        public void click() throws IOException, SAXException {
648
            // TODO check whether the empty body of this method was correct
649
            // call onclick event handler
650
            HTMLElement element = this.get_element();
×
651
            if (element instanceof FormControl) {
×
652
                FormControl control = (FormControl) element;
×
653
                control.sendOnClickEvent();
×
654
            }
655
        }
×
656

657
        /**
658
         * simulate blur.
659
         */
660
        public void blur() {
661
            handleEvent("onblur");
×
662
        }
×
663

664
        /**
665
         * simulate focus;.
666
         */
667
        public void focus() {
668
            handleEvent("onfocus");
×
669
        }
×
670

671
        /**
672
         * allow firing a sendOnChangeEvent
673
         */
674
        @Override
675
        public void sendOnChangeEvent() {
676
            // TODO check why the test for this does not work although
677
            // the javascript function call is done in the corresponding testcase
678
            // testCallOnChange()
679
            HTMLElement element = this.get_element();
1✔
680
            if (element instanceof FormControl) {
1!
681
                FormControl control = (FormControl) element;
1✔
682
                control.sendOnChangeEvent();
1✔
683
            }
684
        }
1✔
685

686
    }
687

688
}
689

690
abstract class BooleanFormControl extends FormControl {
691

692
    private String[] _displayedValue;
693

694
    private HTMLInputElementImpl _element;
695

696
    @Override
697
    public ScriptableDelegate newScriptable() {
698
        return new Scriptable();
1✔
699
    }
700

701
    class Scriptable extends FormControl.Scriptable {
1✔
702

703
        @Override
704
        public Object get(String propertyName) {
705
            if (propertyName.equalsIgnoreCase("value")) {
1✔
706
                return getQueryValue();
1✔
707
            }
708
            if (propertyName.equalsIgnoreCase("checked")) {
1✔
709
                return isChecked() ? Boolean.TRUE : Boolean.FALSE;
1✔
710
            }
711
            if (propertyName.equalsIgnoreCase("defaultchecked")) {
1✔
712
                return _element.getDefaultChecked() ? Boolean.TRUE : Boolean.FALSE;
1✔
713
            }
714
            return super.get(propertyName);
1✔
715
        }
716

717
        @Override
718
        public void set(String propertyName, Object value) {
719
            if (propertyName.equalsIgnoreCase("checked")) {
1✔
720
                setChecked(value instanceof Boolean && ((Boolean) value).booleanValue());
1!
721
            } else {
722
                super.set(propertyName, value);
1✔
723
            }
724
        }
1✔
725
    }
726

727
    public BooleanFormControl(WebForm form, HTMLInputElementImpl element) {
728
        super(form, element);
1✔
729
        _element = element;
1✔
730
        _displayedValue = new String[] { readDisplayedValue(element) };
1✔
731
    }
1✔
732

733
    private String readDisplayedValue(Node node) {
734
        Node nextSibling = node.getNextSibling();
1✔
735
        while (nextSibling != null && nextSibling.getNodeType() != Node.TEXT_NODE
1✔
736
                && nextSibling.getNodeType() != Node.ELEMENT_NODE) {
1!
737
            nextSibling = nextSibling.getNextSibling();
×
738
        }
739
        if (nextSibling == null || nextSibling.getNodeType() != Node.TEXT_NODE) {
1✔
740
            return "";
1✔
741
        }
742
        return nextSibling.getNodeValue();
1✔
743
    }
744

745
    boolean isChecked() {
746
        return _element.getChecked();
1✔
747
    }
748

749
    @Override
750
    protected String getValueAttribute() {
751
        return emptyIfNull(_element.getValue());
1✔
752
    }
753

754
    @Override
755
    protected void setValueAttribute(String value) {
756
        _element.setValue(value);
1✔
757
    }
1✔
758

759
    /**
760
     * Sets the checked.
761
     *
762
     * @param checked
763
     *            the new checked
764
     */
765
    public void setChecked(boolean checked) {
766
        _element.setChecked(checked);
1✔
767
    }
1✔
768

769
    /**
770
     * Returns the current value(s) associated with this control. These values will be transmitted to the server if the
771
     * control is 'successful'.
772
     **/
773
    @Override
774
    public String[] getValues() {
775
        return isChecked() ? toArray(getQueryValue()) : NO_VALUE;
1✔
776
    }
777

778
    /**
779
     * Returns the values permitted in this control.
780
     **/
781
    @Override
782
    public String[] getOptionValues() {
783
        return isReadOnly() && !isChecked() ? NO_VALUE : toArray(getQueryValue());
1✔
784
    }
785

786
    @Override
787
    protected String[] getDisplayedOptions() {
788
        return _displayedValue;
1✔
789
    }
790

791
    @Override
792
    protected void addValues(ParameterProcessor processor, String characterSet) throws IOException {
793
        if (isChecked() && !isDisabled()) {
1✔
794
            processor.addParameter(getName(), getQueryValue(), characterSet);
1✔
795
        }
796
    }
1✔
797

798
    /**
799
     * Remove any required values for this control from the list, throwing an exception if they are missing.
800
     **/
801
    @Override
802
    void claimRequiredValues(List values) {
803
        if (isValueRequired()) {
1✔
804
            claimValueIsRequired(values, getQueryValue());
1✔
805
        }
806
    }
1✔
807

808
    /**
809
     * Checks if is value required.
810
     *
811
     * @return true, if is value required
812
     */
813
    protected boolean isValueRequired() {
814
        return isReadOnly() && isChecked();
1✔
815
    }
816

817
    abstract String getQueryValue();
818

819
    private String[] toArray(String value) {
820
        return new String[] { value };
1✔
821
    }
822
}
823

824
class CheckboxFormControl extends BooleanFormControl {
825

826
    @Override
827
    public String getType() {
828
        return CHECKBOX_TYPE;
1✔
829
    }
830

831
    public CheckboxFormControl(WebForm form, HTMLInputElementImpl element) {
832
        super(form, element);
1✔
833
    }
1✔
834

835
    @Override
836
    protected void claimUniqueValue(List values) {
837
        if (isValueRequired()) {
1✔
838
            return;
1✔
839
        }
840
        setState(values.contains(getQueryValue()));
1✔
841
        if (isChecked()) {
1✔
842
            values.remove(getQueryValue());
1✔
843
        }
844
    }
1✔
845

846
    @Override
847
    String getQueryValue() {
848
        final String value = getValueAttribute();
1✔
849
        return value.isEmpty() ? "on" : value;
1✔
850
    }
851

852
    /**
853
     * Toggles the value of this control.
854
     */
855
    @Override
856
    public void toggle() {
857
        setState(!isChecked());
1!
858
    }
1✔
859

860
    /**
861
     * Sets the state of this boolean control. Triggers the 'onclick' event if the state has changed.
862
     */
863
    @Override
864
    public void setState(boolean state) {
865
        boolean wasChecked = isChecked();
1✔
866
        setChecked(state);
1✔
867
        if (isChecked() != wasChecked) {
1✔
868
            sendOnClickEvent();
1✔
869
        }
870
    }
1✔
871
}
872

873
abstract class TextFormControl extends FormControl {
874

875
    public TextFormControl(WebForm form, HTMLControl control) {
876
        super(form, control);
1✔
877
    }
1✔
878

879
    /**
880
     * Returns the current value(s) associated with this control. These values will be transmitted to the server if the
881
     * control is 'successful'.
882
     **/
883
    @Override
884
    public String[] getValues() {
885
        return new String[] { getValue() };
1✔
886
    }
887

888
    abstract protected String getDefaultValue();
889

890
    abstract protected String getValue();
891

892
    abstract protected void setValue(String value);
893

894
    /**
895
     * Returns true to indicate that this control accepts free-form text.
896
     **/
897
    @Override
898
    public boolean isTextControl() {
899
        return true;
1✔
900
    }
901

902
    @Override
903
    public ScriptableDelegate newScriptable() {
904
        return new Scriptable();
1✔
905
    }
906

907
    @Override
908
    protected void addValues(ParameterProcessor processor, String characterSet) throws IOException {
909
        if (!isDisabled() && getName().length() > 0) {
1✔
910
            processor.addParameter(getName(), getValues()[0], characterSet);
1✔
911
        }
912
    }
1✔
913

914
    /**
915
     * claim values and fire onChange Event if a change occured
916
     *
917
     * @param values
918
     *            - the list of values
919
     */
920
    @Override
921
    void claimValue(List values) {
922
        if (isReadOnly()) {
1✔
923
            return;
1✔
924
        }
925

926
        String oldValue = getValue();
1✔
927
        if (values.isEmpty()) {
1✔
928
            setValue("");
1✔
929
        } else {
930
            setValue((String) values.get(0));
1✔
931
            values.remove(0);
1✔
932
        }
933
        boolean same = oldValue == null && getValue() == null;
1!
934
        if (oldValue != null) {
1!
935
            same = oldValue.equals(getValue());
1✔
936
        }
937
        if (!same) {
1✔
938
            sendOnChangeEvent();
1✔
939
        }
940
    }
1✔
941

942
    @Override
943
    void claimRequiredValues(List values) {
944
        if (isReadOnly()) {
1✔
945
            claimValueIsRequired(values);
1✔
946
        }
947
    }
1✔
948

949
    protected void claimValueIsRequired(List values) {
950
        claimValueIsRequired(values, getDefaultValue());
1✔
951
    }
1✔
952

953
    class Scriptable extends FormControl.Scriptable {
1✔
954

955
        @Override
956
        public Object get(String propertyName) {
957
            if (propertyName.equalsIgnoreCase("value")) {
1✔
958
                return getValue();
1✔
959
            }
960
            if (propertyName.equalsIgnoreCase("defaultValue")) {
1✔
961
                return getDefaultValue();
1✔
962
            }
963
            return super.get(propertyName);
1✔
964
        }
965

966
        @Override
967
        public void set(String propertyName, Object value) {
968
            if (!propertyName.equalsIgnoreCase("value")) {
1!
969
                super.set(propertyName, value);
×
970
            } else if (value instanceof Number) {
1✔
971
                setValue(HttpUnitUtils.trimmedValue((Number) value));
1✔
972
            } else {
973
                setValue(value == null ? null : value.toString());
1✔
974
            }
975
        }
1✔
976
    }
977
}
978

979
class TextFieldFormControl extends TextFormControl {
980

981
    private HTMLInputElementImpl _element;
982

983
    @Override
984
    public String getType() {
985
        return TEXT_TYPE;
1✔
986
    }
987

988
    public TextFieldFormControl(WebForm form, HTMLInputElementImpl element) {
989
        super(form, element);
1✔
990
        _element = element;
1✔
991
        supportAttribute("maxlength");
1✔
992
    }
1✔
993

994
    @Override
995
    protected String getDefaultValue() {
996
        return _element.getDefaultValue();
1✔
997
    }
998

999
    @Override
1000
    protected String getValue() {
1001
        return emptyIfNull(_element.getValue());
1✔
1002
    }
1003

1004
    @Override
1005
    protected void setValue(String value) {
1006
        _element.setValue(value);
1✔
1007
    }
1✔
1008
}
1009

1010
class PasswordFieldFormControl extends TextFieldFormControl {
1011

1012
    @Override
1013
    public String getType() {
1014
        return PASSWORD_TYPE;
1✔
1015
    }
1016

1017
    public PasswordFieldFormControl(WebForm form, HTMLInputElementImpl element) {
1018
        super(form, element);
1✔
1019
    }
1✔
1020
}
1021

1022
/**
1023
 * a hidden text field
1024
 */
1025
class HiddenFieldFormControl extends TextFieldFormControl {
1026

1027
    @Override
1028
    public String getType() {
1029
        return HIDDEN_TYPE;
1✔
1030
    }
1031

1032
    public HiddenFieldFormControl(WebForm form, HTMLInputElementImpl element) {
1033
        super(form, element);
1✔
1034
    }
1✔
1035

1036
    @Override
1037
    void claimRequiredValues(List values) {
1038
        claimValueIsRequired(values);
1✔
1039
    }
1✔
1040

1041
    @Override
1042
    void claimValue(List values) {
1043
    }
1✔
1044

1045
    @Override
1046
    public boolean isHidden() {
1047
        return true;
1✔
1048
    }
1049
}
1050

1051
class TextAreaFormControl extends TextFormControl {
1052

1053
    private HTMLTextAreaElementImpl _element;
1054

1055
    public TextAreaFormControl(WebForm form, HTMLTextAreaElementImpl element) {
1056
        super(form, element);
1✔
1057
        _element = element;
1✔
1058
    }
1✔
1059

1060
    @Override
1061
    public String getType() {
1062
        return TEXTAREA_TYPE;
1✔
1063
    }
1064

1065
    @Override
1066
    protected String getDefaultValue() {
1067
        return _element.getDefaultValue();
1✔
1068
    }
1069

1070
    @Override
1071
    protected String getValue() {
1072
        return _element.getValue();
1✔
1073
    }
1074

1075
    @Override
1076
    protected void setValue(String value) {
1077
        _element.setValue(value);
1✔
1078
    }
1✔
1079

1080
}
1081

1082
/**
1083
 * a control for File submit
1084
 */
1085
class FileSubmitFormControl extends FormControl {
1086

1087
    /**
1088
     * accessor for the type
1089
     *
1090
     * @return the constant FILE_TYPE
1091
     */
1092
    @Override
1093
    public String getType() {
1094
        return FILE_TYPE;
1✔
1095
    }
1096

1097
    private UploadFileSpec _fileToUpload;
1098

1099
    @Override
1100
    public ScriptableDelegate newScriptable() {
1101
        return new Scriptable();
1✔
1102
    }
1103

1104
    class Scriptable extends FormControl.Scriptable {
1✔
1105

1106
        @Override
1107
        public Object get(String propertyName) {
1108
            if (propertyName.equalsIgnoreCase("value")) {
1✔
1109
                return getSelectedName();
1✔
1110
            }
1111
            return super.get(propertyName);
1✔
1112
        }
1113

1114
    }
1115

1116
    public FileSubmitFormControl(WebForm form, HTMLInputElementImpl node) {
1117
        super(form, node);
1✔
1118
    }
1✔
1119

1120
    /**
1121
     * Returns true if this control accepts a file for upload.
1122
     **/
1123
    @Override
1124
    public boolean isFileParameter() {
1125
        return true;
1✔
1126
    }
1127

1128
    /**
1129
     * Returns the name of the selected file, if any.
1130
     */
1131
    @Override
1132
    public String[] getValues() {
1133
        return new String[] { getSelectedName() };
1✔
1134
    }
1135

1136
    private String getSelectedName() {
1137
        return _fileToUpload == null ? "" : _fileToUpload.getFileName();
1✔
1138
    }
1139

1140
    /**
1141
     * Specifies a number of file upload specifications for this control.
1142
     **/
1143
    @Override
1144
    void claimUploadSpecification(List files) {
1145
        if (files.isEmpty()) {
1✔
1146
            _fileToUpload = null;
1✔
1147
        } else {
1148
            _fileToUpload = (UploadFileSpec) files.get(0);
1✔
1149
            files.remove(0);
1✔
1150
        }
1151
    }
1✔
1152

1153
    @Override
1154
    protected void addValues(ParameterProcessor processor, String characterSet) throws IOException {
1155
        if (!isDisabled() && _fileToUpload != null) {
1!
1156
            processor.addFile(getName(), _fileToUpload);
1✔
1157
        }
1158
    }
1✔
1159
}
1160

1161
// ============================= exception class MissingParameterValueException ======================================
1162

1163
/**
1164
 * This exception is thrown on an attempt to remove a required value from a form parameter.
1165
 **/
1166
class MissingParameterValueException extends IllegalRequestParameterException {
1167

1168
    private static final long serialVersionUID = 1L;
1169

1170
    MissingParameterValueException(String parameterName, String missingValue, String[] proposed) {
1✔
1171
        _parameterName = parameterName;
1✔
1172
        _missingValue = missingValue;
1✔
1173
        _proposedValues = proposed;
1✔
1174
    }
1✔
1175

1176
    @Override
1177
    public String getMessage() {
1178
        StringBuilder sb = new StringBuilder(HttpUnitUtils.DEFAULT_TEXT_BUFFER_SIZE);
×
1179
        sb.append("Parameter '").append(_parameterName).append("' must have the value '");
×
1180
        sb.append(_missingValue).append("'. Attempted to set it to: { ");
×
1181
        for (int i = 0; i < _proposedValues.length; i++) {
×
1182
            if (i != 0) {
×
1183
                sb.append(", ");
×
1184
            }
1185
            sb.append(_proposedValues[i]);
×
1186
        }
1187
        sb.append(" }");
×
1188
        return sb.toString();
×
1189
    }
1190

1191
    private String _parameterName;
1192
    private String _missingValue;
1193
    private String[] _proposedValues;
1194
}
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