• 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

89.49
/src/main/java/com/meterware/httpunit/dom/NodeImpl.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.dom;
21

22
import java.util.ArrayList;
23
import java.util.Hashtable;
24
import java.util.Iterator;
25
import java.util.List;
26

27
import org.w3c.dom.DOMException;
28
import org.w3c.dom.Document;
29
import org.w3c.dom.Element;
30
import org.w3c.dom.NamedNodeMap;
31
import org.w3c.dom.Node;
32
import org.w3c.dom.NodeList;
33
import org.w3c.dom.UserDataHandler;
34
import org.w3c.dom.html.HTMLIFrameElement;
35

36
/**
37
 * The Class NodeImpl.
38
 */
39
public abstract class NodeImpl extends AbstractDomComponent implements Node {
1✔
40

41
    /** The Constant serialVersionUID. */
42
    private static final long serialVersionUID = 1L;
43

44
    /** The owner document. */
45
    private DocumentImpl _ownerDocument;
46

47
    /** The parent node. */
48
    private NodeImpl _parentNode;
49

50
    /** The first child. */
51
    private NodeImpl _firstChild;
52

53
    /** The next sibling. */
54
    private NodeImpl _nextSibling;
55

56
    /** The previous sibling. */
57
    private NodeImpl _previousSibling;
58

59
    /** The user data. */
60
    private Hashtable _userData = new Hashtable<>();
1✔
61

62
    /** The skip iframes. */
63
    static IteratorMask SKIP_IFRAMES = subtreeRoot -> subtreeRoot instanceof HTMLIFrameElement;
1✔
64

65
    /**
66
     * Initialize.
67
     *
68
     * @param ownerDocument
69
     *            the owner document
70
     */
71
    protected void initialize(DocumentImpl ownerDocument) {
72
        if (_ownerDocument != null) {
1!
73
            throw new IllegalStateException("NodeImpl already initialized");
×
74
        }
75
        if (ownerDocument == null) {
1!
76
            throw new IllegalArgumentException("No owner document specified");
×
77
        }
78
        _ownerDocument = ownerDocument;
1✔
79
    }
1✔
80

81
    // ------------------------------------------ ScriptableObject methods
82
    // --------------------------------------------------
83

84
    // ------------------------------------------ ScriptingEngine methods
85
    // --------------------------------------------------
86

87
    // ----------------------------------------------- Node methods
88
    // ---------------------------------------------------------
89

90
    @Override
91
    public Node getParentNode() {
92
        return _parentNode;
1✔
93
    }
94

95
    @Override
96
    public NodeList getChildNodes() {
97
        ArrayList list = new ArrayList<>();
1✔
98
        for (NodeImpl child = _firstChild; child != null; child = child._nextSibling) {
1✔
99
            list.add(child);
1✔
100
        }
101
        return new NodeListImpl(list);
1✔
102
    }
103

104
    @Override
105
    public Node getFirstChild() {
106
        return _firstChild;
1✔
107
    }
108

109
    @Override
110
    public Node getLastChild() {
111
        if (_firstChild == null) {
1✔
112
            return null;
1✔
113
        }
114

115
        Node child = _firstChild;
1✔
116
        while (child.getNextSibling() != null) {
1✔
117
            child = child.getNextSibling();
1✔
118
        }
119
        return child;
1✔
120
    }
121

122
    @Override
123
    public Node getPreviousSibling() {
124
        return _previousSibling;
1✔
125
    }
126

127
    @Override
128
    public Node getNextSibling() {
129
        return _nextSibling;
1✔
130
    }
131

132
    @Override
133
    public NamedNodeMap getAttributes() {
134
        return null;
1✔
135
    }
136

137
    @Override
138
    public Document getOwnerDocument() {
139
        return _ownerDocument;
1✔
140
    }
141

142
    @Override
143
    public Node insertBefore(Node newChild, Node refChild) throws DOMException {
144
        NodeImpl refChildNode = (NodeImpl) refChild;
1✔
145
        if (refChildNode.getParentNode() != this) {
1✔
146
            throw new DOMException(DOMException.NOT_FOUND_ERR, "Must specify an existing child as the reference");
1✔
147
        }
148
        NodeImpl newChildNode = getChildIfPermitted(newChild);
1✔
149
        removeFromTree(newChildNode);
1✔
150
        newChildNode._parentNode = this;
1✔
151
        if (refChildNode._previousSibling == null) {
1✔
152
            _firstChild = newChildNode;
1✔
153
        } else {
154
            refChildNode._previousSibling.setNextSibling(newChildNode);
1✔
155
        }
156
        newChildNode.setNextSibling(refChildNode);
1✔
157
        return newChildNode;
1✔
158
    }
159

160
    /**
161
     * Removes the from tree.
162
     *
163
     * @param childNode
164
     *            the child node
165
     */
166
    private void removeFromTree(NodeImpl childNode) {
167
        if (childNode._parentNode != null) {
1✔
168
            if (childNode._previousSibling != null) {
1✔
169
                childNode._previousSibling.setNextSibling(childNode._nextSibling);
1✔
170
            } else {
171
                childNode._parentNode._firstChild = childNode._nextSibling;
1✔
172
                childNode._nextSibling._previousSibling = null;
1✔
173
            }
174
            childNode._parentNode = null;
1✔
175
        }
176
    }
1✔
177

178
    @Override
179
    public Node replaceChild(Node newChild, Node oldChild) throws DOMException {
180
        insertBefore(newChild, oldChild);
1✔
181
        return removeChild(oldChild);
1✔
182
    }
183

184
    @Override
185
    public Node removeChild(Node oldChild) throws DOMException {
186
        if (oldChild.getParentNode() != this) {
1✔
187
            throw new DOMException(DOMException.NOT_FOUND_ERR, "May only remove a node from its own parent");
1✔
188
        }
189
        removeFromTree((NodeImpl) oldChild);
1✔
190
        return oldChild;
1✔
191
    }
192

193
    @Override
194
    public Node appendChild(Node newChild) throws DOMException {
195
        if (newChild == null) {
1!
196
            throw new IllegalArgumentException("child to append may not be null");
×
197
        }
198

199
        NodeImpl childNode = getChildIfPermitted(newChild);
1✔
200
        removeFromTree(childNode);
1✔
201
        childNode._parentNode = this;
1✔
202
        if (_firstChild == null) {
1✔
203
            _firstChild = childNode;
1✔
204
        } else {
205
            ((NodeImpl) getLastChild()).setNextSibling(childNode);
1✔
206
        }
207
        return newChild;
1✔
208
    }
209

210
    /**
211
     * Gets the child if permitted.
212
     *
213
     * @param proposedChild
214
     *            the proposed child
215
     *
216
     * @return the child if permitted
217
     */
218
    protected NodeImpl getChildIfPermitted(Node proposedChild) {
219
        if (!(proposedChild instanceof NodeImpl)) {
1!
220
            throw new DOMException(DOMException.WRONG_DOCUMENT_ERR,
×
221
                    "Specified node is from a different DOM implementation");
222
        }
223
        NodeImpl childNode = (NodeImpl) proposedChild;
1✔
224
        if (getOwnerDocument() != childNode._ownerDocument) {
1✔
225
            throw new DOMException(DOMException.WRONG_DOCUMENT_ERR, "Specified node is from a different document");
1✔
226
        }
227
        for (Node parent = this; parent != null; parent = parent.getParentNode()) {
1✔
228
            if (proposedChild == parent) {
1✔
229
                throw new DOMException(DOMException.HIERARCHY_REQUEST_ERR, "May not add node as its own descendant");
1✔
230
            }
231
        }
232

233
        return childNode;
1✔
234
    }
235

236
    /**
237
     * Sets the next sibling.
238
     *
239
     * @param sibling
240
     *            the new next sibling
241
     */
242
    private void setNextSibling(NodeImpl sibling) {
243
        _nextSibling = sibling;
1✔
244
        if (sibling != null) {
1✔
245
            sibling._previousSibling = this;
1✔
246
        }
247
    }
1✔
248

249
    @Override
250
    public boolean hasChildNodes() {
251
        return _firstChild != null;
1✔
252
    }
253

254
    @Override
255
    public Node cloneNode(boolean deep) {
256
        return getOwnerDocument().importNode(this, deep);
1✔
257
    }
258

259
    @Override
260
    public void normalize() {
261
    }
×
262

263
    @Override
264
    public boolean isSupported(String feature, String version) {
265
        return false;
×
266
    }
267

268
    @Override
269
    public String getNamespaceURI() {
270
        return null;
1✔
271
    }
272

273
    @Override
274
    public String getPrefix() {
275
        return null;
×
276
    }
277

278
    @Override
279
    public void setPrefix(String prefix) throws DOMException {
280
    }
×
281

282
    @Override
283
    public String getLocalName() {
284
        return null;
1✔
285
    }
286

287
    @Override
288
    public boolean hasAttributes() {
289
        return false;
×
290
    }
291

292
    // ------------------------------------ DOM level 3 methods
293
    // -------------------------------------------------------------
294

295
    @Override
296
    public Object setUserData(String key, Object data, UserDataHandler handler) {
297
        return _userData.put(key, data);
×
298
    }
299

300
    @Override
301
    public Object getUserData(String key) {
302
        return _userData.get(key);
×
303
    }
304

305
    @Override
306
    public Object getFeature(String feature, String version) {
307
        return null;
×
308
    }
309

310
    @Override
311
    public boolean isEqualNode(Node arg) {
312
        return false; // To change body of implemented methods use File | Settings | File Templates.
×
313
    }
314

315
    @Override
316
    public String lookupNamespaceURI(String prefix) {
317
        return null; // To change body of implemented methods use File | Settings | File Templates.
×
318
    }
319

320
    @Override
321
    public String getBaseURI() {
322
        return null; // To change body of implemented methods use File | Settings | File Templates.
×
323
    }
324

325
    @Override
326
    public short compareDocumentPosition(Node other) throws DOMException {
327
        return 0; // To change body of implemented methods use File | Settings | File Templates.
×
328
    }
329

330
    @Override
331
    public String getTextContent() throws DOMException {
332
        return null; // To change body of implemented methods use File | Settings | File Templates.
×
333
    }
334

335
    @Override
336
    public void setTextContent(String textContent) throws DOMException {
337
        // To change body of implemented methods use File | Settings | File Templates.
338
    }
×
339

340
    @Override
341
    public boolean isSameNode(Node other) {
342
        return this == other;
1✔
343
    }
344

345
    @Override
346
    public String lookupPrefix(String namespaceURI) {
347
        return null; // To change body of implemented methods use File | Settings | File Templates.
×
348
    }
349

350
    @Override
351
    public boolean isDefaultNamespace(String namespaceURI) {
352
        return false; // To change body of implemented methods use File | Settings | File Templates.
×
353
    }
354

355
    // ----------------------------------------- implementation internals
356
    // ---------------------------------------------------
357

358
    /**
359
     * Gets the elements by tag name.
360
     *
361
     * @param name
362
     *            the name
363
     *
364
     * @return the elements by tag name
365
     */
366
    public NodeList getElementsByTagName(String name) {
367
        List matchingElements = new ArrayList<>();
1✔
368
        appendElementsWithTag(name, matchingElements);
1✔
369
        return new NodeListImpl(matchingElements);
1✔
370
    }
371

372
    /**
373
     * Append elements with tag.
374
     *
375
     * @param name
376
     *            the name
377
     * @param matchingElements
378
     *            the matching elements
379
     */
380
    private void appendElementsWithTag(String name, List matchingElements) {
381
        for (Node child = getFirstChild(); child != null; child = child.getNextSibling()) {
1✔
382
            if (child.getNodeType() != ELEMENT_NODE) {
1✔
383
                continue;
1✔
384
            }
385
            if (name.equals("*") || ((Element) child).getTagName().equalsIgnoreCase(name)) {
1✔
386
                matchingElements.add(child);
1✔
387
            }
388
            ((NodeImpl) child).appendElementsWithTag(name, matchingElements);
1✔
389
        }
390
    }
1✔
391

392
    /**
393
     * Gets the elements by tag names.
394
     *
395
     * @param names
396
     *            the names
397
     *
398
     * @return the elements by tag names
399
     */
400
    protected NodeList getElementsByTagNames(String[] names) {
401
        List matchingElements = new ArrayList<>();
1✔
402
        appendElementsWithTags(names, matchingElements);
1✔
403
        return new NodeListImpl(matchingElements);
1✔
404
    }
405

406
    /**
407
     * Append elements with tags.
408
     *
409
     * @param names
410
     *            the names
411
     * @param matchingElements
412
     *            the matching elements
413
     */
414
    void appendElementsWithTags(String[] names, List matchingElements) {
415
        for (Node child = getFirstChild(); child != null; child = child.getNextSibling()) {
1✔
416
            if (child.getNodeType() != ELEMENT_NODE) {
1✔
417
                continue;
1✔
418
            }
419
            String tagName = ((Element) child).getTagName();
1✔
420
            for (String name : names) {
1✔
421
                if (tagName.equalsIgnoreCase(name)) {
1✔
422
                    matchingElements.add(child);
1✔
423
                }
424
            }
425
            ((NodeImpl) child).appendElementsWithTags(names, matchingElements);
1✔
426
        }
427
    }
1✔
428

429
    /**
430
     * As text.
431
     *
432
     * @return the string
433
     */
434
    String asText() {
435
        StringBuilder sb = new StringBuilder();
1✔
436
        appendContents(sb);
1✔
437
        return sb.toString();
1✔
438
    }
439

440
    /**
441
     * Append contents.
442
     *
443
     * @param sb
444
     *            the sb
445
     */
446
    void appendContents(StringBuilder sb) {
447
        NodeList nl = getChildNodes();
1✔
448
        for (int i = 0; i < nl.getLength(); i++) {
1✔
449
            ((NodeImpl) nl.item(i)).appendContents(sb);
1✔
450
        }
451
    }
1✔
452

453
    /**
454
     * Pre order iterator.
455
     *
456
     * @return the iterator
457
     */
458
    public Iterator preOrderIterator() {
459
        return new PreOrderIterator(this);
1✔
460
    }
461

462
    /**
463
     * Pre order iterator.
464
     *
465
     * @param mask
466
     *            the mask
467
     *
468
     * @return the iterator
469
     */
470
    public Iterator preOrderIterator(IteratorMask mask) {
471
        return new PreOrderIterator(this, mask);
1✔
472
    }
473

474
    /**
475
     * Pre order iterator after node.
476
     *
477
     * @return the iterator
478
     */
479
    public Iterator preOrderIteratorAfterNode() {
480
        return new PreOrderIterator(PreOrderIterator.nextNode(this));
1✔
481
    }
482

483
    /**
484
     * Pre order iterator within node.
485
     *
486
     * @return the iterator
487
     */
488
    public Iterator preOrderIteratorWithinNode() {
489
        PreOrderIterator result = new PreOrderIterator(PreOrderIterator.nextNode(this));
1✔
490
        result.setDoNotLeaveNode(this);
1✔
491
        return result;
1✔
492
    }
493

494
    /**
495
     * Pre order iterator within node.
496
     *
497
     * @param mask
498
     *            the mask
499
     *
500
     * @return the iterator
501
     */
502
    public Iterator preOrderIteratorWithinNode(IteratorMask mask) {
503
        PreOrderIterator result = new PreOrderIterator(PreOrderIterator.nextNode(this), mask);
1✔
504
        result.setDoNotLeaveNode(this);
1✔
505
        return result;
1✔
506
    }
507

508
    /**
509
     * Pre order iterator after node.
510
     *
511
     * @param mask
512
     *            the mask
513
     *
514
     * @return the iterator
515
     */
516
    public Iterator preOrderIteratorAfterNode(IteratorMask mask) {
517
        return new PreOrderIterator(PreOrderIterator.nextNode(this), mask);
×
518
    }
519

520
    @Override
521
    protected String getJavaPropertyName(String propertyName) {
522
        if (propertyName.equals("document")) {
1✔
523
            return "ownerDocument";
1✔
524
        }
525
        return propertyName;
1✔
526
    }
527

528
    /**
529
     * allow masking of the iteration.
530
     */
531
    interface IteratorMask {
532

533
        /**
534
         * Skip subtree.
535
         *
536
         * @param subtreeRoot
537
         *            the subtree root
538
         *
539
         * @return true, if successful
540
         */
541
        // skip a given subtree
542
        boolean skipSubtree(Node subtreeRoot);
543
    }
544

545
    /**
546
     * iterator for Nodetrees that can be influenced with an Iterator mask to skip specific parts.
547
     */
548
    static class PreOrderIterator implements Iterator {
549

550
        /** The next node. */
551
        private NodeImpl _nextNode;
552

553
        /** The mask. */
554
        private IteratorMask _mask;
555

556
        /** The do not leave node. */
557
        private NodeImpl _doNotLeaveNode = null;
1✔
558

559
        /**
560
         * get the limit node.
561
         *
562
         * @return the do not leave node
563
         */
564
        public NodeImpl getDoNotLeaveNode() {
565
            return _doNotLeaveNode;
×
566
        }
567

568
        /**
569
         * limit the PreOrderIterator not to leave the given node.
570
         *
571
         * @param doNotLeaveNode
572
         *            the new do not leave node
573
         */
574
        public void setDoNotLeaveNode(NodeImpl doNotLeaveNode) {
575
            _doNotLeaveNode = doNotLeaveNode;
1✔
576
        }
1✔
577

578
        /**
579
         * check whether the node is a child of the doNotLeaveNode (if one is set).
580
         *
581
         * @param node
582
         *            the node
583
         *
584
         * @return true, if is child
585
         */
586
        private boolean isChild(Node node) {
587
            if (node == null) {
1✔
588
                return false;
1✔
589
            }
590
            if (_doNotLeaveNode == null) {
1✔
591
                return true;
1✔
592
            }
593
            Node parent = node.getParentNode();
1✔
594
            if (parent == null) {
1✔
595
                return false;
1✔
596
            }
597
            if (parent.isSameNode(_doNotLeaveNode)) {
1✔
598
                return true;
1✔
599
            }
600
            return isChild(parent);
1✔
601
        }
602

603
        /**
604
         * create a PreOrderIterator starting at a given currentNode.
605
         *
606
         * @param currentNode
607
         *            the current node
608
         */
609
        PreOrderIterator(NodeImpl currentNode) {
1✔
610
            _nextNode = currentNode;
1✔
611
        }
1✔
612

613
        /**
614
         * create a PreOrderIterator starting at a given currentNode and setting the iterator mask to the given mask.
615
         *
616
         * @param currentNode
617
         *            the current node
618
         * @param mask
619
         *            the mask
620
         */
621
        PreOrderIterator(NodeImpl currentNode, IteratorMask mask) {
622
            this(currentNode);
1✔
623
            _mask = mask;
1✔
624
        }
1✔
625

626
        /**
627
         * is there still a next node?
628
         */
629
        @Override
630
        public boolean hasNext() {
631
            return null != _nextNode;
1✔
632
        }
633

634
        /**
635
         * move one step in the tree
636
         */
637
        @Override
638
        public Object next() {
639
            NodeImpl currentNode = _nextNode;
1✔
640
            _nextNode = nextNode(_nextNode);
1✔
641
            while (_mask != null && _nextNode != null && _mask.skipSubtree(_nextNode)) {
1✔
642
                _nextNode = nextSubtree(_nextNode);
1✔
643
            }
644
            // check that we fit the doNotLeaveNode condition in case there is one
645
            if (!isChild(_nextNode)) {
1✔
646
                _nextNode = null;
1✔
647
            }
648
            return currentNode;
1✔
649
        }
650

651
        @Override
652
        public void remove() {
653
            throw new java.lang.UnsupportedOperationException();
×
654
        }
655

656
        /**
657
         * Next node.
658
         *
659
         * @param node
660
         *            the node
661
         *
662
         * @return the node impl
663
         */
664
        static NodeImpl nextNode(NodeImpl node) {
665
            if (node._firstChild != null) {
1✔
666
                return node._firstChild;
1✔
667
            }
668
            return nextSubtree(node);
1✔
669
        }
670

671
        /**
672
         * Next subtree.
673
         *
674
         * @param node
675
         *            the node
676
         *
677
         * @return the node impl
678
         */
679
        private static NodeImpl nextSubtree(NodeImpl node) {
680
            if (node._nextSibling != null) {
1✔
681
                return node._nextSibling;
1✔
682
            }
683
            while (node._parentNode != null) {
1✔
684
                node = node._parentNode;
1✔
685
                if (node._nextSibling != null) {
1✔
686
                    return node._nextSibling;
1✔
687
                }
688
            }
689
            return null;
1✔
690
        }
691
    }
692

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