• 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

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

22
import com.meterware.httpunit.FormControl;
23
import com.meterware.httpunit.NodeUtils;
24
import com.meterware.httpunit.WebForm;
25
import com.meterware.httpunit.dom.HTMLSelectElementImpl;
26
import com.meterware.httpunit.protocol.ParameterProcessor;
27
import com.meterware.httpunit.scripting.ScriptableDelegate;
28
import com.meterware.httpunit.scripting.SelectionOption;
29
import com.meterware.httpunit.scripting.SelectionOptions;
30

31
import java.io.IOException;
32
import java.util.ArrayList;
33
import java.util.Hashtable;
34
import java.util.List;
35

36
import org.w3c.dom.Element;
37
import org.w3c.dom.NamedNodeMap;
38
import org.w3c.dom.Node;
39
import org.w3c.dom.NodeList;
40

41
/**
42
 * FormControl for "Select" moved here by wf for testability and visibility see bugreport [ 1124057 ] Out of Bounds
43
 * Exception should be avoided
44
 */
45
public class SelectionFormControl extends FormControl {
46

47
    private final boolean _multiSelect;
48
    private final boolean _listBox;
49

50
    private Options _selectionOptions;
51

52
    @Override
53
    public String getType() {
54
        return isMultiValued() ? MULTIPLE_TYPE : SINGLE_TYPE;
1✔
55
    }
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
    class Scriptable extends FormControl.Scriptable {
1✔
95

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

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

134
        private String getSelectedValue() {
135
            String[] values = getValues();
1✔
136
            return values.length == 0 ? "" : values[0];
1!
137
        }
138

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

166
    @Override
167
    public ScriptableDelegate newScriptable() {
168
        return new Scriptable();
1✔
169
    }
170

171
    void updateRequiredParameters(Hashtable required) {
172
        if (isReadOnly()) {
×
173
            required.put(getName(), getValues());
×
174
        }
175
    }
×
176

177
    @Override
178
    protected void addValues(ParameterProcessor processor, String characterSet) throws IOException {
179
        if (isDisabled()) {
1!
180
            return;
×
181
        }
182
        for (int i = 0; i < getValues().length; i++) {
1✔
183
            processor.addParameter(getName(), getValues()[i], characterSet);
1✔
184
        }
185
    }
1✔
186

187
    @Override
188
    protected void claimUniqueValue(List values) {
189
        boolean changed = _selectionOptions.claimUniqueValues(values);
1✔
190
        if (changed) {
1✔
191
            sendOnChangeEvent();
1✔
192
        }
193
    }
1✔
194

195
    @Override
196
    protected void reset() {
197
        _selectionOptions.reset();
1✔
198
    }
1✔
199

200
    public static class Option extends ScriptableDelegate implements SelectionOption {
201

202
        private String _text = "";
1✔
203
        private String _value;
204
        private boolean _defaultSelected;
205
        private boolean _selected;
206
        private int _index;
207
        private Options _container;
208

209
        public Option() {
1✔
210
        }
1✔
211

212
        Option(String text, String value, boolean selected) {
1✔
213
            _text = text;
1✔
214
            _value = value;
1✔
215
            _defaultSelected = _selected = selected;
1✔
216
        }
1✔
217

218
        void reset() {
219
            _selected = _defaultSelected;
1✔
220
        }
1✔
221

222
        void addValueIfSelected(List list) {
223
            if (_selected) {
1✔
224
                list.add(_value);
1✔
225
            }
226
        }
1✔
227

228
        void setIndex(Options container, int index) {
229
            _container = container;
1✔
230
            _index = index;
1✔
231
        }
1✔
232

233
        // ------------------------- SelectionOption methods ------------------------------
234

235
        @Override
236
        public void initialize(String text, String value, boolean defaultSelected, boolean selected) {
237
            _text = text;
1✔
238
            _value = value;
1✔
239
            _defaultSelected = defaultSelected;
1✔
240
            _selected = selected;
1✔
241
        }
1✔
242

243
        @Override
244
        public int getIndex() {
245
            return _index;
1✔
246
        }
247

248
        @Override
249
        public String getText() {
250
            return _text;
1✔
251
        }
252

253
        @Override
254
        public void setText(String text) {
255
            _text = text;
1✔
256
        }
1✔
257

258
        @Override
259
        public String getValue() {
260
            return _value;
1✔
261
        }
262

263
        @Override
264
        public void setValue(String value) {
265
            _value = value;
1✔
266
        }
1✔
267

268
        @Override
269
        public boolean isDefaultSelected() {
270
            return _defaultSelected;
×
271
        }
272

273
        @Override
274
        public void setSelected(boolean selected) {
275
            _selected = selected;
1✔
276
            if (selected) {
1✔
277
                _container.optionSet(_index);
1✔
278
            }
279
        }
1✔
280

281
        @Override
282
        public boolean isSelected() {
283
            return _selected;
1✔
284
        }
285
    }
286

287
    public abstract class Options extends ScriptableDelegate implements SelectionOptions {
288

289
        private Option[] _options;
290

291
        Options(Node selectionNode) {
1✔
292
            // BR [ 1843978 ] Accessing Options in a form is in lower case
293
            // calls for uppercase "option" here ... pending as of 2007-12-30
294
            NodeList nl = ((Element) selectionNode).getElementsByTagName("OPTION");
1✔
295

296
            _options = new Option[nl.getLength()];
1✔
297
            for (int i = 0; i < _options.length; i++) {
1✔
298
                final String displayedText = getValue(nl.item(i).getFirstChild()).trim();
1✔
299
                _options[i] = new Option(displayedText, getOptionValue(nl.item(i), displayedText),
1✔
300
                        nl.item(i).getAttributes().getNamedItem("selected") != null);
1✔
301
                _options[i].setIndex(this, i);
1✔
302
            }
303
        }
1✔
304

305
        /**
306
         * claim unique values from the given list of values
307
         *
308
         * @param values
309
         *            - the list of values
310
         *
311
         * @return
312
         */
313
        boolean claimUniqueValues(List values) {
314
            return claimUniqueValues(values, _options);
1✔
315
        }
316

317
        protected abstract boolean claimUniqueValues(List values, Option[] options);
318

319
        /**
320
         * report if there are no matches be aware of [ 1100437 ] Patch for ClassCastException in FormControl TODO
321
         * implement patch if test get's available
322
         *
323
         * @param values
324
         */
325
        protected final void reportNoMatches(List values) {
326
            if (!_listBox) {
1!
327
                throw new IllegalParameterValueException(getName(), values, getOptionValues());
1✔
328
            }
329
        }
×
330

331
        String[] getSelectedValues() {
332
            ArrayList list = new ArrayList<>();
1✔
333
            for (Option _option : _options) {
1✔
334
                _option.addValueIfSelected(list);
1✔
335
            }
336
            if (!_listBox && list.isEmpty() && _options.length > 0) {
1✔
337
                list.add(_options[0].getValue());
1✔
338
            }
339
            return (String[]) list.toArray(new String[list.size()]);
1✔
340
        }
341

342
        void reset() {
343
            for (Option _option : _options) {
1✔
344
                _option.reset();
1✔
345
            }
346
        }
1✔
347

348
        String[] getDisplayedText() {
349
            String[] displayedText = new String[_options.length];
1✔
350
            for (int i = 0; i < displayedText.length; i++) {
1✔
351
                displayedText[i] = _options[i].getText();
1✔
352
            }
353
            return displayedText;
1✔
354
        }
355

356
        String[] getValues() {
357
            String[] values = new String[_options.length];
1✔
358
            for (int i = 0; i < values.length; i++) {
1✔
359
                values[i] = _options[i].getValue();
1✔
360
            }
361
            return values;
1✔
362
        }
363

364
        /**
365
         * Selects the matching item and deselects the others.
366
         **/
367
        void setSelectedIndex(int index) {
368
            for (int i = 0; i < _options.length; i++) {
1✔
369
                _options[i]._selected = i == index;
1✔
370
            }
371
        }
1✔
372

373
        /**
374
         * Returns the index of the first item selected, or -1 if none is selected.
375
         */
376
        int getFirstSelectedIndex() {
377
            for (int i = 0; i < _options.length; i++) {
1✔
378
                if (_options[i].isSelected()) {
1✔
379
                    return i;
1✔
380
                }
381
            }
382
            return noOptionSelectedIndex();
1✔
383
        }
384

385
        protected abstract int noOptionSelectedIndex();
386

387
        @Override
388
        public int getLength() {
389
            return _options.length;
1✔
390
        }
391

392
        /**
393
         * Modified by gklopp - 12/19/2005 [ 1396835 ] Javascript : length of a select element cannot be increased Bug
394
         * corrected : The length can be greater than the original length
395
         */
396
        @Override
397
        public void setLength(int length) {
398
            if (length < 0) {
1!
399
                return;
×
400
            }
401
            Option[] newArray = new Option[length];
1✔
402
            System.arraycopy(_options, 0, newArray, 0, Math.min(length, _options.length));
1✔
403
            for (int i = _options.length; i < length; i++) {
1✔
404
                newArray[i] = new Option();
1✔
405
            }
406
            _options = newArray;
1✔
407
        }
1✔
408

409
        @Override
410
        public void put(int i, SelectionOption option) {
411
            if (i < 0) {
1!
412
                return;
×
413
            }
414

415
            if (option == null) {
1✔
416
                if (i >= _options.length) {
1!
417
                    return;
×
418
                }
419
                deleteOptionsEntry(i);
1✔
420
            } else {
421
                if (i >= _options.length) {
1✔
422
                    i = _options.length;
1✔
423
                    expandOptionsArray();
1✔
424
                }
425
                _options[i] = (Option) option;
1✔
426
                _options[i].setIndex(this, i);
1✔
427
                if (option.isSelected()) {
1!
428
                    ensureUniqueOption(_options, i);
×
429
                }
430
            }
431
        }
1✔
432

433
        protected abstract void ensureUniqueOption(Option[] options, int i);
434

435
        private void deleteOptionsEntry(int i) {
436
            Option[] newArray = new Option[_options.length - 1];
1✔
437
            System.arraycopy(_options, 0, newArray, 0, i);
1✔
438
            System.arraycopy(_options, i + 1, newArray, i, newArray.length - i);
1✔
439
            _options = newArray;
1✔
440
        }
1✔
441

442
        private void expandOptionsArray() {
443
            Option[] newArray = new Option[_options.length + 1];
1✔
444
            System.arraycopy(_options, 0, newArray, 0, _options.length);
1✔
445
            _options = newArray;
1✔
446
        }
1✔
447

448
        /**
449
         * get the Object at the given index check that the index is not out of bounds
450
         *
451
         * @param index
452
         *            - the index of the object to get
453
         *
454
         * @throws RuntimeException
455
         *             if index is out of bounds
456
         */
457
        @Override
458
        public Object get(int index) {
459
            // if the index is out of bounds
460
            if (index < 0 || index >= _options.length) {
1!
461
                // create a user friendly error message
462
                StringBuilder msg = new StringBuilder("invalid index ").append(index).append(" for Options ");
1✔
463
                // by listing all possible options
464
                for (int i = 0; i < _options.length; i++) {
1✔
465
                    msg.append(_options[i]._text);
1✔
466
                    if (i < _options.length - 1) {
1✔
467
                        msg.append(",");
1✔
468
                    }
469
                } // for
470
                  // now throw a RunTimeException that would
471
                  // have happened anyways with a less friendly message
472
                throw new RuntimeException(msg.toString());
1✔
473
            } // if
474
            return _options[index];
1✔
475
        } // get
476

477
        /** Invoked when an option is set true. **/
478
        void optionSet(int i) {
479
            ensureUniqueOption(_options, i);
1✔
480
        }
1✔
481

482
        private String getOptionValue(Node optionNode, String displayedText) {
483
            NamedNodeMap nnm = optionNode.getAttributes();
1✔
484
            if (nnm.getNamedItem("value") != null) {
1✔
485
                return getValue(nnm.getNamedItem("value"));
1✔
486
            }
487
            return displayedText;
1✔
488
        }
489

490
        private String getValue(Node node) {
491
            return node == null ? "" : emptyIfNull(node.getNodeValue());
1!
492
        }
493
    }
494

495
    class SingleSelectOptions extends Options {
496

497
        public SingleSelectOptions(Node selectionNode) {
1✔
498
            super(selectionNode);
1✔
499
        }
1✔
500

501
        @Override
502
        protected void ensureUniqueOption(Option[] options, int i) {
503
            for (int j = 0; j < options.length; j++) {
1✔
504
                options[j]._selected = i == j;
1✔
505
            }
506
        }
1✔
507

508
        @Override
509
        protected int noOptionSelectedIndex() {
510
            return 0;
1✔
511
        }
512

513
        /**
514
         * claim the values be aware of [ 1100437 ] Patch for ClassCastException in FormControl TODO implement patch if
515
         * test get's available - the (String) cast might fail
516
         */
517
        @Override
518
        protected boolean claimUniqueValues(List values, Option[] options) {
519
            boolean changed = false;
1✔
520
            for (int i = 0; i < values.size(); i++) {
1!
521
                String value = (String) values.get(i);
1✔
522
                for (int j = 0; j < options.length; j++) {
1✔
523
                    boolean selected = value.equals(options[j].getValue());
1✔
524
                    if (selected != options[j].isSelected()) {
1✔
525
                        changed = true;
1✔
526
                    }
527
                    options[j].setSelected(selected);
1✔
528
                    if (selected) {
1✔
529
                        values.remove(value);
1✔
530
                        for (++j; j < options.length; j++) {
1✔
531
                            options[j].setSelected(false);
1✔
532
                        }
533
                        return changed;
1✔
534
                    }
535
                }
536
            }
537
            reportNoMatches(values);
×
538
            return changed;
×
539
        }
540
    }
541

542
    class MultiSelectOptions extends Options {
543

544
        public MultiSelectOptions(Node selectionNode) {
1✔
545
            super(selectionNode);
1✔
546
        }
1✔
547

548
        @Override
549
        protected void ensureUniqueOption(Option[] options, int i) {
550
        }
1✔
551

552
        @Override
553
        protected int noOptionSelectedIndex() {
554
            return -1;
1✔
555
        }
556

557
        @Override
558
        protected boolean claimUniqueValues(List values, Option[] options) {
559
            boolean changed = false;
1✔
560
            for (Option option : options) {
1✔
561
                final boolean newValue = values.contains(option.getValue());
1✔
562
                if (newValue != option.isSelected()) {
1✔
563
                    changed = true;
1✔
564
                }
565
                option.setSelected(newValue);
1✔
566
                if (newValue) {
1✔
567
                    values.remove(option.getValue());
1✔
568
                }
569
            }
570
            return changed;
1✔
571
        }
572
    }
573

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