• 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

90.74
/src/main/java/com/meterware/httpunit/controls/SelectionFormControl.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.controls;
9

10
import com.meterware.httpunit.FormControl;
11
import com.meterware.httpunit.NodeUtils;
12
import com.meterware.httpunit.WebForm;
13
import com.meterware.httpunit.dom.HTMLSelectElementImpl;
14
import com.meterware.httpunit.protocol.ParameterProcessor;
15
import com.meterware.httpunit.scripting.ScriptableDelegate;
16
import com.meterware.httpunit.scripting.SelectionOption;
17
import com.meterware.httpunit.scripting.SelectionOptions;
18

19
import java.io.IOException;
20
import java.util.ArrayList;
21
import java.util.Hashtable;
22
import java.util.List;
23

24
import org.w3c.dom.Element;
25
import org.w3c.dom.NamedNodeMap;
26
import org.w3c.dom.Node;
27
import org.w3c.dom.NodeList;
28

29
/**
30
 * FormControl for "Select" moved here by wf for testability and visibility see bugreport [ 1124057 ] Out of Bounds
31
 * Exception should be avoided.
32
 */
33
public class SelectionFormControl extends FormControl {
34

35
    /** The multi select. */
36
    private final boolean _multiSelect;
37

38
    /** The list box. */
39
    private final boolean _listBox;
40

41
    /** The selection options. */
42
    private Options _selectionOptions;
43

44
    @Override
45
    public String getType() {
46
        return isMultiValued() ? MULTIPLE_TYPE : SINGLE_TYPE;
1✔
47
    }
48

49
    /**
50
     * Instantiates a new selection form control.
51
     *
52
     * @param form
53
     *            the form
54
     * @param element
55
     *            the element
56
     */
57
    public SelectionFormControl(WebForm form, HTMLSelectElementImpl element) {
58
        super(form, element);
1✔
59
        if (!element.getNodeName().equalsIgnoreCase("select")) {
1!
60
            throw new RuntimeException("Not a select element");
×
61
        }
62

63
        int size = NodeUtils.getAttributeValue(element, "size", 0);
1✔
64
        _multiSelect = NodeUtils.isNodeAttributePresent(element, "multiple");
1✔
65
        _listBox = size > 1 || _multiSelect && size != 1;
1✔
66

67
        _selectionOptions = _listBox ? (Options) new MultiSelectOptions(element)
1✔
68
                : (Options) new SingleSelectOptions(element);
1✔
69
    }
1✔
70

71
    @Override
72
    public String[] getValues() {
73
        return _selectionOptions.getSelectedValues();
1✔
74
    }
75

76
    @Override
77
    public String[] getOptionValues() {
78
        return _selectionOptions.getValues();
1✔
79
    }
80

81
    @Override
82
    public String[] getDisplayedOptions() {
83
        return _selectionOptions.getDisplayedText();
1✔
84
    }
85

86
    /**
87
     * Returns true if a single control can have multiple values.
88
     **/
89
    @Override
90
    public boolean isMultiValued() {
91
        return _multiSelect;
1✔
92
    }
93

94
    /**
95
     * The Class Scriptable.
96
     */
97
    class Scriptable extends FormControl.Scriptable {
1✔
98

99
        /**
100
         * get the Object with the given property name
101
         *
102
         * @param propertyName
103
         *            - the name of the property to get
104
         *
105
         * @return the Object for the property
106
         */
107
        @Override
108
        public Object get(String propertyName) {
109
            if (propertyName.equalsIgnoreCase("options")) {
1✔
110
                return _selectionOptions;
1✔
111
            }
112
            if (propertyName.equalsIgnoreCase("length")) {
1✔
113
                return Integer.valueOf(getOptionValues().length);
1✔
114
            }
115
            if (propertyName.equalsIgnoreCase("value")) {
1✔
116
                return getSelectedValue();
1✔
117
            }
118
            if (propertyName.equalsIgnoreCase("selectedIndex")) {
1✔
119
                return Integer.valueOf(_selectionOptions.getFirstSelectedIndex());
1✔
120
            }
121
            return super.get(propertyName);
1✔
122
        }
123

124
        /**
125
         * get the Object at the given index
126
         *
127
         * @param index
128
         *            - the index of the object to get
129
         *
130
         * @return the object at the given index
131
         */
132
        @Override
133
        public Object get(int index) {
134
            return _selectionOptions.get(index);
1✔
135
        }
136

137
        /**
138
         * Gets the selected value.
139
         *
140
         * @return the selected value
141
         */
142
        private String getSelectedValue() {
143
            String[] values = getValues();
1✔
144
            return values.length == 0 ? "" : values[0];
1!
145
        }
146

147
        /**
148
         * set the property with the given name to the given value
149
         *
150
         * @param propertyName
151
         *            - the name of the property to set
152
         * @param value
153
         *            - the value to assign to the property
154
         */
155
        @Override
156
        public void set(String propertyName, Object value) {
157
            if (propertyName.equalsIgnoreCase("value")) {
1✔
158
                ArrayList values = new ArrayList<>();
1✔
159
                values.add(value);
1✔
160
                _selectionOptions.claimUniqueValues(values);
1✔
161
            } else if (propertyName.equalsIgnoreCase("selectedIndex")) {
1✔
162
                if (!(value instanceof Number)) {
1!
163
                    throw new RuntimeException("selectedIndex must be set to an integer");
×
164
                }
165
                _selectionOptions.setSelectedIndex(((Number) value).intValue());
1✔
166
            } else if (propertyName.equalsIgnoreCase("length")) {
1!
167
                _selectionOptions.setLength(((Number) value).intValue());
1✔
168
            } else {
169
                super.set(propertyName, value);
×
170
            }
171
        }
1✔
172
    }
173

174
    @Override
175
    public ScriptableDelegate newScriptable() {
176
        return new Scriptable();
1✔
177
    }
178

179
    /**
180
     * Update required parameters.
181
     *
182
     * @param required
183
     *            the required
184
     */
185
    void updateRequiredParameters(Hashtable required) {
186
        if (isReadOnly()) {
×
187
            required.put(getName(), getValues());
×
188
        }
189
    }
×
190

191
    @Override
192
    protected void addValues(ParameterProcessor processor, String characterSet) throws IOException {
193
        if (isDisabled()) {
1!
194
            return;
×
195
        }
196
        for (int i = 0; i < getValues().length; i++) {
1✔
197
            processor.addParameter(getName(), getValues()[i], characterSet);
1✔
198
        }
199
    }
1✔
200

201
    @Override
202
    protected void claimUniqueValue(List values) {
203
        boolean changed = _selectionOptions.claimUniqueValues(values);
1✔
204
        if (changed) {
1✔
205
            sendOnChangeEvent();
1✔
206
        }
207
    }
1✔
208

209
    @Override
210
    protected void reset() {
211
        _selectionOptions.reset();
1✔
212
    }
1✔
213

214
    /**
215
     * The Class Option.
216
     */
217
    public static class Option extends ScriptableDelegate implements SelectionOption {
218

219
        /** The text. */
220
        private String _text = "";
1✔
221

222
        /** The value. */
223
        private String _value;
224

225
        /** The default selected. */
226
        private boolean _defaultSelected;
227

228
        /** The selected. */
229
        private boolean _selected;
230

231
        /** The index. */
232
        private int _index;
233

234
        /** The container. */
235
        private Options _container;
236

237
        /**
238
         * Instantiates a new option.
239
         */
240
        public Option() {
1✔
241
        }
1✔
242

243
        /**
244
         * Instantiates a new option.
245
         *
246
         * @param text
247
         *            the text
248
         * @param value
249
         *            the value
250
         * @param selected
251
         *            the selected
252
         */
253
        Option(String text, String value, boolean selected) {
1✔
254
            _text = text;
1✔
255
            _value = value;
1✔
256
            _defaultSelected = _selected = selected;
1✔
257
        }
1✔
258

259
        /**
260
         * Reset.
261
         */
262
        void reset() {
263
            _selected = _defaultSelected;
1✔
264
        }
1✔
265

266
        /**
267
         * Adds the value if selected.
268
         *
269
         * @param list
270
         *            the list
271
         */
272
        void addValueIfSelected(List list) {
273
            if (_selected) {
1✔
274
                list.add(_value);
1✔
275
            }
276
        }
1✔
277

278
        /**
279
         * Sets the index.
280
         *
281
         * @param container
282
         *            the container
283
         * @param index
284
         *            the index
285
         */
286
        void setIndex(Options container, int index) {
287
            _container = container;
1✔
288
            _index = index;
1✔
289
        }
1✔
290

291
        // ------------------------- SelectionOption methods ------------------------------
292

293
        @Override
294
        public void initialize(String text, String value, boolean defaultSelected, boolean selected) {
295
            _text = text;
1✔
296
            _value = value;
1✔
297
            _defaultSelected = defaultSelected;
1✔
298
            _selected = selected;
1✔
299
        }
1✔
300

301
        @Override
302
        public int getIndex() {
303
            return _index;
1✔
304
        }
305

306
        @Override
307
        public String getText() {
308
            return _text;
1✔
309
        }
310

311
        @Override
312
        public void setText(String text) {
313
            _text = text;
1✔
314
        }
1✔
315

316
        @Override
317
        public String getValue() {
318
            return _value;
1✔
319
        }
320

321
        @Override
322
        public void setValue(String value) {
323
            _value = value;
1✔
324
        }
1✔
325

326
        @Override
327
        public boolean isDefaultSelected() {
328
            return _defaultSelected;
×
329
        }
330

331
        @Override
332
        public void setSelected(boolean selected) {
333
            _selected = selected;
1✔
334
            if (selected) {
1✔
335
                _container.optionSet(_index);
1✔
336
            }
337
        }
1✔
338

339
        @Override
340
        public boolean isSelected() {
341
            return _selected;
1✔
342
        }
343
    }
344

345
    /**
346
     * The Class Options.
347
     */
348
    public abstract class Options extends ScriptableDelegate implements SelectionOptions {
349

350
        /** The options. */
351
        private Option[] _options;
352

353
        /**
354
         * Instantiates a new options.
355
         *
356
         * @param selectionNode
357
         *            the selection node
358
         */
359
        Options(Node selectionNode) {
1✔
360
            // BR [ 1843978 ] Accessing Options in a form is in lower case
361
            // calls for uppercase "option" here ... pending as of 2007-12-30
362
            NodeList nl = ((Element) selectionNode).getElementsByTagName("OPTION");
1✔
363

364
            _options = new Option[nl.getLength()];
1✔
365
            for (int i = 0; i < _options.length; i++) {
1✔
366
                final String displayedText = getValue(nl.item(i).getFirstChild()).trim();
1✔
367
                _options[i] = new Option(displayedText, getOptionValue(nl.item(i), displayedText),
1✔
368
                        nl.item(i).getAttributes().getNamedItem("selected") != null);
1✔
369
                _options[i].setIndex(this, i);
1✔
370
            }
371
        }
1✔
372

373
        /**
374
         * claim unique values from the given list of values.
375
         *
376
         * @param values
377
         *            - the list of values
378
         *
379
         * @return true, if successful
380
         */
381
        boolean claimUniqueValues(List values) {
382
            return claimUniqueValues(values, _options);
1✔
383
        }
384

385
        /**
386
         * Claim unique values.
387
         *
388
         * @param values
389
         *            the values
390
         * @param options
391
         *            the options
392
         *
393
         * @return true, if successful
394
         */
395
        protected abstract boolean claimUniqueValues(List values, Option[] options);
396

397
        /**
398
         * report if there are no matches be aware of [ 1100437 ] Patch for ClassCastException in FormControl TODO
399
         * implement patch if test get's available.
400
         *
401
         * @param values
402
         *            the values
403
         */
404
        protected final void reportNoMatches(List values) {
405
            if (!_listBox) {
1!
406
                throw new IllegalParameterValueException(getName(), values, getOptionValues());
1✔
407
            }
408
        }
×
409

410
        /**
411
         * Gets the selected values.
412
         *
413
         * @return the selected values
414
         */
415
        String[] getSelectedValues() {
416
            ArrayList list = new ArrayList<>();
1✔
417
            for (Option _option : _options) {
1✔
418
                _option.addValueIfSelected(list);
1✔
419
            }
420
            if (!_listBox && list.isEmpty() && _options.length > 0) {
1✔
421
                list.add(_options[0].getValue());
1✔
422
            }
423
            return (String[]) list.toArray(new String[list.size()]);
1✔
424
        }
425

426
        /**
427
         * Reset.
428
         */
429
        void reset() {
430
            for (Option _option : _options) {
1✔
431
                _option.reset();
1✔
432
            }
433
        }
1✔
434

435
        /**
436
         * Gets the displayed text.
437
         *
438
         * @return the displayed text
439
         */
440
        String[] getDisplayedText() {
441
            String[] displayedText = new String[_options.length];
1✔
442
            for (int i = 0; i < displayedText.length; i++) {
1✔
443
                displayedText[i] = _options[i].getText();
1✔
444
            }
445
            return displayedText;
1✔
446
        }
447

448
        /**
449
         * Gets the values.
450
         *
451
         * @return the values
452
         */
453
        String[] getValues() {
454
            String[] values = new String[_options.length];
1✔
455
            for (int i = 0; i < values.length; i++) {
1✔
456
                values[i] = _options[i].getValue();
1✔
457
            }
458
            return values;
1✔
459
        }
460

461
        /**
462
         * Selects the matching item and deselects the others.
463
         *
464
         * @param index
465
         *            the new selected index
466
         */
467
        void setSelectedIndex(int index) {
468
            for (int i = 0; i < _options.length; i++) {
1✔
469
                _options[i]._selected = i == index;
1✔
470
            }
471
        }
1✔
472

473
        /**
474
         * Returns the index of the first item selected, or -1 if none is selected.
475
         *
476
         * @return the first selected index
477
         */
478
        int getFirstSelectedIndex() {
479
            for (int i = 0; i < _options.length; i++) {
1✔
480
                if (_options[i].isSelected()) {
1✔
481
                    return i;
1✔
482
                }
483
            }
484
            return noOptionSelectedIndex();
1✔
485
        }
486

487
        /**
488
         * No option selected index.
489
         *
490
         * @return the int
491
         */
492
        protected abstract int noOptionSelectedIndex();
493

494
        @Override
495
        public int getLength() {
496
            return _options.length;
1✔
497
        }
498

499
        /**
500
         * Modified by gklopp - 12/19/2005 [ 1396835 ] Javascript : length of a select element cannot be increased Bug
501
         * corrected : The length can be greater than the original length
502
         */
503
        @Override
504
        public void setLength(int length) {
505
            if (length < 0) {
1!
506
                return;
×
507
            }
508
            Option[] newArray = new Option[length];
1✔
509
            System.arraycopy(_options, 0, newArray, 0, Math.min(length, _options.length));
1✔
510
            for (int i = _options.length; i < length; i++) {
1✔
511
                newArray[i] = new Option();
1✔
512
            }
513
            _options = newArray;
1✔
514
        }
1✔
515

516
        @Override
517
        public void put(int i, SelectionOption option) {
518
            if (i < 0) {
1!
519
                return;
×
520
            }
521

522
            if (option == null) {
1✔
523
                if (i >= _options.length) {
1!
524
                    return;
×
525
                }
526
                deleteOptionsEntry(i);
1✔
527
            } else {
528
                if (i >= _options.length) {
1✔
529
                    i = _options.length;
1✔
530
                    expandOptionsArray();
1✔
531
                }
532
                _options[i] = (Option) option;
1✔
533
                _options[i].setIndex(this, i);
1✔
534
                if (option.isSelected()) {
1!
535
                    ensureUniqueOption(_options, i);
×
536
                }
537
            }
538
        }
1✔
539

540
        /**
541
         * Ensure unique option.
542
         *
543
         * @param options
544
         *            the options
545
         * @param i
546
         *            the i
547
         */
548
        protected abstract void ensureUniqueOption(Option[] options, int i);
549

550
        /**
551
         * Delete options entry.
552
         *
553
         * @param i
554
         *            the i
555
         */
556
        private void deleteOptionsEntry(int i) {
557
            Option[] newArray = new Option[_options.length - 1];
1✔
558
            System.arraycopy(_options, 0, newArray, 0, i);
1✔
559
            System.arraycopy(_options, i + 1, newArray, i, newArray.length - i);
1✔
560
            _options = newArray;
1✔
561
        }
1✔
562

563
        /**
564
         * Expand options array.
565
         */
566
        private void expandOptionsArray() {
567
            Option[] newArray = new Option[_options.length + 1];
1✔
568
            System.arraycopy(_options, 0, newArray, 0, _options.length);
1✔
569
            _options = newArray;
1✔
570
        }
1✔
571

572
        /**
573
         * get the Object at the given index check that the index is not out of bounds
574
         *
575
         * @param index
576
         *            - the index of the object to get
577
         *
578
         * @throws RuntimeException
579
         *             if index is out of bounds
580
         */
581
        @Override
582
        public Object get(int index) {
583
            // if the index is out of bounds
584
            if (index < 0 || index >= _options.length) {
1!
585
                // create a user friendly error message
586
                StringBuilder msg = new StringBuilder("invalid index ").append(index).append(" for Options ");
1✔
587
                // by listing all possible options
588
                for (int i = 0; i < _options.length; i++) {
1✔
589
                    msg.append(_options[i]._text);
1✔
590
                    if (i < _options.length - 1) {
1✔
591
                        msg.append(",");
1✔
592
                    }
593
                } // for
594
                  // now throw a RunTimeException that would
595
                  // have happened anyways with a less friendly message
596
                throw new RuntimeException(msg.toString());
1✔
597
            } // if
598
            return _options[index];
1✔
599
        } // get
600

601
        /**
602
         * Invoked when an option is set true. *
603
         *
604
         * @param i
605
         *            the i
606
         */
607
        void optionSet(int i) {
608
            ensureUniqueOption(_options, i);
1✔
609
        }
1✔
610

611
        /**
612
         * Gets the option value.
613
         *
614
         * @param optionNode
615
         *            the option node
616
         * @param displayedText
617
         *            the displayed text
618
         *
619
         * @return the option value
620
         */
621
        private String getOptionValue(Node optionNode, String displayedText) {
622
            NamedNodeMap nnm = optionNode.getAttributes();
1✔
623
            if (nnm.getNamedItem("value") != null) {
1✔
624
                return getValue(nnm.getNamedItem("value"));
1✔
625
            }
626
            return displayedText;
1✔
627
        }
628

629
        /**
630
         * Gets the value.
631
         *
632
         * @param node
633
         *            the node
634
         *
635
         * @return the value
636
         */
637
        private String getValue(Node node) {
638
            return node == null ? "" : emptyIfNull(node.getNodeValue());
1!
639
        }
640
    }
641

642
    /**
643
     * The Class SingleSelectOptions.
644
     */
645
    class SingleSelectOptions extends Options {
646

647
        /**
648
         * Instantiates a new single select options.
649
         *
650
         * @param selectionNode
651
         *            the selection node
652
         */
653
        public SingleSelectOptions(Node selectionNode) {
1✔
654
            super(selectionNode);
1✔
655
        }
1✔
656

657
        @Override
658
        protected void ensureUniqueOption(Option[] options, int i) {
659
            for (int j = 0; j < options.length; j++) {
1✔
660
                options[j]._selected = i == j;
1✔
661
            }
662
        }
1✔
663

664
        @Override
665
        protected int noOptionSelectedIndex() {
666
            return 0;
1✔
667
        }
668

669
        /**
670
         * claim the values be aware of [ 1100437 ] Patch for ClassCastException in FormControl TODO implement patch if
671
         * test get's available - the (String) cast might fail
672
         */
673
        @Override
674
        protected boolean claimUniqueValues(List values, Option[] options) {
675
            boolean changed = false;
1✔
676
            for (int i = 0; i < values.size(); i++) {
1!
677
                String value = (String) values.get(i);
1✔
678
                for (int j = 0; j < options.length; j++) {
1✔
679
                    boolean selected = value.equals(options[j].getValue());
1✔
680
                    if (selected != options[j].isSelected()) {
1✔
681
                        changed = true;
1✔
682
                    }
683
                    options[j].setSelected(selected);
1✔
684
                    if (selected) {
1✔
685
                        values.remove(value);
1✔
686
                        for (++j; j < options.length; j++) {
1✔
687
                            options[j].setSelected(false);
1✔
688
                        }
689
                        return changed;
1✔
690
                    }
691
                }
692
            }
693
            reportNoMatches(values);
×
694
            return changed;
×
695
        }
696
    }
697

698
    /**
699
     * The Class MultiSelectOptions.
700
     */
701
    class MultiSelectOptions extends Options {
702

703
        /**
704
         * Instantiates a new multi select options.
705
         *
706
         * @param selectionNode
707
         *            the selection node
708
         */
709
        public MultiSelectOptions(Node selectionNode) {
1✔
710
            super(selectionNode);
1✔
711
        }
1✔
712

713
        @Override
714
        protected void ensureUniqueOption(Option[] options, int i) {
715
        }
1✔
716

717
        @Override
718
        protected int noOptionSelectedIndex() {
719
            return -1;
1✔
720
        }
721

722
        @Override
723
        protected boolean claimUniqueValues(List values, Option[] options) {
724
            boolean changed = false;
1✔
725
            for (Option option : options) {
1✔
726
                final boolean newValue = values.contains(option.getValue());
1✔
727
                if (newValue != option.isSelected()) {
1✔
728
                    changed = true;
1✔
729
                }
730
                option.setSelected(newValue);
1✔
731
                if (newValue) {
1✔
732
                    values.remove(option.getValue());
1✔
733
                }
734
            }
735
            return changed;
1✔
736
        }
737
    }
738

739
}
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