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

hazendaz / httpunit / 646

06 Dec 2025 08:11PM UTC coverage: 80.526% (+0.02%) from 80.509%
646

push

github

hazendaz
Cleanup array usage

3213 of 4105 branches covered (78.27%)

Branch coverage included in aggregate %.

4 of 4 new or added lines in 3 files covered. (100.0%)

532 existing lines in 26 files now uncovered.

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

89.34
/src/main/java/com/meterware/httpunit/NodeUtils.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.parsing.HTMLParserFactory;
23

24
import java.util.Iterator;
25
import java.util.ListIterator;
26
import java.util.Stack;
27

28
import org.w3c.dom.Element;
29
import org.w3c.dom.NamedNodeMap;
30
import org.w3c.dom.Node;
31
import org.w3c.dom.NodeList;
32

33
/**
34
 * Some common utilities for manipulating DOM nodes.
35
 **/
36
public class NodeUtils {
×
37

38
    /**
39
     * get the attribute with the given name from the given node as an int value.
40
     *
41
     * @param node
42
     *            - the node to look in
43
     * @param attributeName
44
     *            - the attribute's name to look for
45
     * @param defaultValue
46
     *            the default value
47
     *
48
     * @return - the value - defaultValue as default
49
     */
50
    public static int getAttributeValue(Node node, String attributeName, int defaultValue) {
51
        NamedNodeMap nnm = node.getAttributes();
1✔
52
        Node attribute = nnm.getNamedItem(attributeName);
1✔
53
        if (attribute == null) {
1✔
54
            return defaultValue;
1✔
55
        }
56
        try {
57
            return Integer.parseInt(attribute.getNodeValue());
1✔
58
        } catch (NumberFormatException e) {
×
59
            return defaultValue;
×
60
        }
61
    }
62

63
    /**
64
     * get the attribute with the given name from the given node.
65
     *
66
     * @param node
67
     *            - the node to look in
68
     * @param attributeName
69
     *            - the attribute's name to look for
70
     *
71
     * @return - the value - "" as default
72
     */
73
    public static String getNodeAttribute(Node node, String attributeName) {
74
        return getNodeAttribute(node, attributeName, "");
1✔
75
    }
76

77
    /**
78
     * get the attribute with the given name from the given node.
79
     *
80
     * @param node
81
     *            - the node to look in
82
     * @param attributeName
83
     *            - the attribute's name to look for
84
     * @param defaultValue
85
     *            the default value
86
     *
87
     * @return - the value - defaultValue as default
88
     */
89
    public static String getNodeAttribute(Node node, String attributeName, String defaultValue) {
90
        NamedNodeMap attributes = node.getAttributes();
1✔
91
        if (attributes == null) {
1✔
92
            return defaultValue;
1✔
93
        }
94

95
        Node attribute = attributes.getNamedItem(attributeName);
1✔
96
        return attribute == null ? defaultValue : attribute.getNodeValue();
1✔
97
    }
98

99
    /**
100
     * set the attribute with the given attribute to the given value in the given node.
101
     *
102
     * @param node
103
     *            the node
104
     * @param attributeName
105
     *            - the attribute's name to look for
106
     * @param value
107
     *            - the value to set
108
     */
109
    static void setNodeAttribute(Node node, String attributeName, String value) {
110
        ((Element) node).setAttributeNS(null, attributeName, value);
1✔
111
    }
1✔
112

113
    /**
114
     * remove the given attribute from the given node based on the attribute's name.
115
     *
116
     * @param node
117
     *            the node
118
     * @param attributeName
119
     *            the attribute name
120
     */
121
    static void removeNodeAttribute(Node node, String attributeName) {
122
        ((Element) node).removeAttribute(attributeName);
×
123
    }
×
124

125
    /**
126
     * check whether the given Attribute in the Node is Present.
127
     *
128
     * @param node
129
     *            - the node to check
130
     * @param attributeName
131
     *            - the attribute name to check
132
     *
133
     * @return true if the attribute is present
134
     */
135
    public static boolean isNodeAttributePresent(Node node, final String attributeName) {
136
        return node.getAttributes().getNamedItem(attributeName) != null;
1✔
137
    }
138

139
    /**
140
     * common Node action methods.
141
     */
142
    interface NodeAction {
143

144
        /**
145
         * Does appropriate processing on specified element. Will return false if the subtree below the element should
146
         * be skipped.
147
         *
148
         * @param traversal
149
         *            the traversal
150
         * @param element
151
         *            the element
152
         *
153
         * @return true, if successful
154
         */
155
        boolean processElement(PreOrderTraversal traversal, Element element);
156

157
        /**
158
         * Processes a text node.
159
         *
160
         * @param traversal
161
         *            the traversal
162
         * @param textNode
163
         *            the text node
164
         */
165
        void processTextNode(PreOrderTraversal traversal, Node textNode);
166
    }
167

168
    /**
169
     * Converts the DOM trees rooted at the specified nodes to text, ignoring any HTML tags.
170
     *
171
     * @param rootNodes
172
     *            the root nodes
173
     *
174
     * @return the string
175
     */
176
    public static String asText(NodeList rootNodes) {
177
        final StringBuilder sb = new StringBuilder(HttpUnitUtils.DEFAULT_TEXT_BUFFER_SIZE);
1✔
178
        NodeAction action = new NodeAction() {
1✔
179
            @Override
180
            public boolean processElement(PreOrderTraversal traversal, Element node) {
181
                String nodeName = node.getNodeName();
1✔
182
                if (nodeName.equalsIgnoreCase("p") || nodeName.equalsIgnoreCase("br")
1!
183
                        || nodeName.equalsIgnoreCase("tr")) {
1✔
184
                    sb.append("\n");
1✔
185
                } else if (nodeName.equalsIgnoreCase("td") || nodeName.equalsIgnoreCase("th")) {
1!
186
                    sb.append(" | ");
1✔
187
                } else if (nodeName.equalsIgnoreCase("img") && HttpUnitOptions.getImagesTreatedAsAltText()) {
1✔
188
                    sb.append(getNodeAttribute(node, "alt"));
1✔
189
                }
190
                return true;
1✔
191
            }
192

193
            @Override
194
            public void processTextNode(PreOrderTraversal traversal, Node textNode) {
195
                sb.append(HTMLParserFactory.getHTMLParser().getCleanedText(textNode.getNodeValue()));
1✔
196
            }
1✔
197
        };
198
        new PreOrderTraversal(rootNodes).perform(action);
1✔
199
        return sb.toString();
1✔
200
    }
201

202
    /**
203
     * The Class PreOrderTraversal.
204
     */
205
    static class PreOrderTraversal {
206

207
        /** The pending nodes. */
208
        private Stack _pendingNodes = new Stack();
1✔
209

210
        /** The traversal context. */
211
        private Stack _traversalContext = new Stack();
1✔
212

213
        /** The Constant POP_CONTEXT. */
214
        private static final Object POP_CONTEXT = new Object();
1✔
215

216
        /**
217
         * Instantiates a new pre order traversal.
218
         *
219
         * @param rootNodes
220
         *            the root nodes
221
         */
222
        public PreOrderTraversal(NodeList rootNodes) {
1✔
223
            pushNodeList(rootNodes);
1✔
224
        }
1✔
225

226
        /**
227
         * Instantiates a new pre order traversal.
228
         *
229
         * @param rootNode
230
         *            the root node
231
         */
232
        public PreOrderTraversal(Node rootNode) {
1✔
233
            pushNodeList(rootNode.getLastChild());
1✔
234
        }
1✔
235

236
        /**
237
         * Push base context.
238
         *
239
         * @param context
240
         *            the context
241
         */
242
        public void pushBaseContext(Object context) {
243
            _traversalContext.push(context);
1✔
244
        }
1✔
245

246
        /**
247
         * Push context.
248
         *
249
         * @param context
250
         *            the context
251
         */
252
        public void pushContext(Object context) {
253
            _traversalContext.push(context);
1✔
254
            _pendingNodes.push(POP_CONTEXT);
1✔
255
        }
1✔
256

257
        /**
258
         * Gets the contexts.
259
         *
260
         * @return the contexts
261
         */
262
        public Iterator getContexts() {
263
            Stack stack = _traversalContext;
1✔
264
            return getTopDownIterator(stack);
1✔
265
        }
266

267
        /**
268
         * Gets the root context.
269
         *
270
         * @return the root context
271
         */
272
        public Object getRootContext() {
UNCOV
273
            return _traversalContext.firstElement();
×
274
        }
275

276
        /**
277
         * Gets the top down iterator.
278
         *
279
         * @param stack
280
         *            the stack
281
         *
282
         * @return the top down iterator
283
         */
284
        private Iterator getTopDownIterator(final Stack stack) {
285
            return new Iterator() {
1✔
286
                private ListIterator _forwardIterator = stack.listIterator(stack.size());
1✔
287

288
                @Override
289
                public boolean hasNext() {
290
                    return _forwardIterator.hasPrevious();
1✔
291
                }
292

293
                @Override
294
                public Object next() {
295
                    return _forwardIterator.previous();
1✔
296
                }
297

298
                @Override
299
                public void remove() {
300
                    _forwardIterator.remove();
×
UNCOV
301
                }
×
302
            };
303
        }
304

305
        /**
306
         * Returns the most recently pushed context which implements the specified class. Will return null if no
307
         * matching context is found.
308
         *
309
         * @param matchingClass
310
         *            the matching class
311
         *
312
         * @return the closest context
313
         */
314
        public Object getClosestContext(Class matchingClass) {
315
            for (int i = _traversalContext.size() - 1; i >= 0; i--) {
1✔
316
                Object o = _traversalContext.elementAt(i);
1✔
317
                if (matchingClass.isInstance(o)) {
1✔
318
                    return o;
1✔
319
                }
320
            }
321
            return null;
1✔
322
        }
323

324
        /**
325
         * Perform.
326
         *
327
         * @param action
328
         *            the action
329
         */
330
        public void perform(NodeAction action) {
331
            while (!_pendingNodes.empty()) {
1✔
332
                final Object object = _pendingNodes.pop();
1✔
333
                if (object == POP_CONTEXT) {
1✔
334
                    _traversalContext.pop();
1✔
335
                } else {
336
                    Node node = (Node) object;
1✔
337
                    if (node.getNodeType() == Node.TEXT_NODE) {
1✔
338
                        action.processTextNode(this, node);
1✔
339
                    } else if (node.getNodeType() != Node.ELEMENT_NODE) {
1!
UNCOV
340
                        continue;
×
341
                    } else {
342
                        action.processElement(this, (Element) node);
1✔
343
                    }
344
                    pushNodeList(node.getLastChild());
1✔
345
                }
346
            }
1✔
347
        }
1✔
348

349
        /**
350
         * Push node list.
351
         *
352
         * @param nl
353
         *            the nl
354
         */
355
        private void pushNodeList(NodeList nl) {
356
            if (nl != null) {
1!
357
                for (int i = nl.getLength() - 1; i >= 0; i--) {
1✔
358
                    _pendingNodes.push(nl.item(i));
1✔
359
                }
360
            }
361
        }
1✔
362

363
        /**
364
         * Push node list.
365
         *
366
         * @param lastChild
367
         *            the last child
368
         */
369
        private void pushNodeList(Node lastChild) {
370
            for (Node node = lastChild; node != null; node = node.getPreviousSibling()) {
1✔
371
                _pendingNodes.push(node);
1✔
372
            }
373
        }
1✔
374
    }
375

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