1 /*
2 * Copyright (c) 2002-2025 Gargoyle Software Inc.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 * https://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15 package org.htmlunit.javascript.host.dom;
16
17 import org.htmlunit.html.DomNode;
18 import org.htmlunit.html.HtmlDomTreeWalker;
19 import org.htmlunit.javascript.HtmlUnitScriptable;
20 import org.htmlunit.javascript.JavaScriptEngine;
21 import org.htmlunit.javascript.configuration.JsxClass;
22 import org.htmlunit.javascript.configuration.JsxConstructor;
23 import org.htmlunit.javascript.configuration.JsxFunction;
24 import org.htmlunit.javascript.configuration.JsxGetter;
25 import org.htmlunit.javascript.configuration.JsxSetter;
26 import org.w3c.dom.DOMException;
27
28 /**
29 * The JavaScript object that represents a {@code TreeWalker}.
30 *
31 * @see <a href="http://www.w3.org/TR/DOM-Level-2-Traversal-Range/traversal.html">
32 * DOM-Level-2-Traversal-Range</a>
33 * @author Mike Dirolf
34 * @author Frank Danek
35 * @author Ahmed Ashour
36 * @author Ronald Brill
37 */
38 @JsxClass
39 public class TreeWalker extends HtmlUnitScriptable {
40
41 private HtmlDomTreeWalker walker_;
42
43 /**
44 * Creates an instance.
45 */
46 public TreeWalker() {
47 super();
48 }
49
50 /**
51 * Creates an instance.
52 */
53 @JsxConstructor
54 public void jsConstructor() {
55 throw JavaScriptEngine.typeErrorIllegalConstructor();
56 }
57
58 /**
59 * Creates an instance.
60 *
61 * @param root The root node of the TreeWalker. Must not be
62 * {@code null}.
63 * @param whatToShow Flag specifying which types of nodes appear in the
64 * logical view of the TreeWalker. See {@link NodeFilter} for the
65 * set of possible Show_ values.
66 * @param filter The {@link NodeFilter} to be used with this TreeWalker,
67 * or {@code null} to indicate no filter.
68 * @param expandEntityReferences If false, the contents of
69 * EntityReference nodes are not present in the logical view.
70 */
71 public TreeWalker(final Node root,
72 final int whatToShow,
73 final org.w3c.dom.traversal.NodeFilter filter,
74 final boolean expandEntityReferences) {
75 super();
76 if (root == null) {
77 throw JavaScriptEngine.typeError("root must not be null");
78 }
79 walker_ = new HtmlDomTreeWalker(root.getDomNodeOrDie(), whatToShow, filter, expandEntityReferences);
80 }
81
82 /**
83 * Gets the root node of the TreeWalker, as specified when it was created.
84 *
85 * @return the root node of the TreeWalker
86 */
87 @JsxGetter
88 public Node getRoot() {
89 return getNodeOrNull(walker_.getRoot());
90 }
91
92 /**
93 * Gets the whatToShow attribute of the TreeWalker. This attribute
94 * determines which node types are presented via the TreeWalker. The set
95 * of available constants is defined in {@link NodeFilter}.
96 *
97 * @return the value of the whatToShow attribute of the TreeWalker
98 */
99 @JsxGetter
100 public long getWhatToShow() {
101 long whatToShow = walker_.getWhatToShow();
102 if (whatToShow == org.w3c.dom.traversal.NodeFilter.SHOW_ALL) {
103 whatToShow = 0xFFFFFFFFL;
104 }
105 return whatToShow;
106 }
107
108 /**
109 * Gets the filter used to screen nodes.
110 *
111 * @return the filter used to screen nodes
112 */
113 @JsxGetter
114 public Object getFilter() {
115 //TODO: we should return the original filter
116 return walker_.getFilter();
117 }
118
119 /**
120 * Gets the node at which the TreeWalker is currently positioned.
121 *
122 * @return the currentNode
123 */
124 @JsxGetter
125 public Node getCurrentNode() {
126 return getNodeOrNull(walker_.getCurrentNode());
127 }
128
129 /**
130 * Sets the node at which the TreeWalker is currently positioned.
131 *
132 * @param currentNode The node to be used as the current position of the
133 * TreeWalker.
134 * @throws DOMException on attempt to set currentNode to
135 * {@code null}.
136 */
137 @JsxSetter
138 public void setCurrentNode(final Node currentNode) throws DOMException {
139 if (currentNode == null) {
140 throw new DOMException(DOMException.NOT_SUPPORTED_ERR,
141 "currentNode cannot be set to null");
142 }
143 walker_.setCurrentNode(currentNode.getDomNodeOrDie());
144 }
145
146 /**
147 * Given a {@link Node}, return the appropriate constant for whatToShow.
148 *
149 * @param node the node
150 * @return the whatToShow constant for the type of specified node
151 */
152 static int getFlagForNode(final Node node) {
153 switch (node.getNodeType()) {
154 case Node.ELEMENT_NODE:
155 return NodeFilter.SHOW_ELEMENT;
156 case Node.ATTRIBUTE_NODE:
157 return NodeFilter.SHOW_ATTRIBUTE;
158 case Node.TEXT_NODE:
159 return NodeFilter.SHOW_TEXT;
160 case Node.CDATA_SECTION_NODE:
161 return NodeFilter.SHOW_CDATA_SECTION;
162 case Node.ENTITY_REFERENCE_NODE:
163 return NodeFilter.SHOW_ENTITY_REFERENCE;
164 case Node.ENTITY_NODE:
165 return NodeFilter.SHOW_ENTITY;
166 case Node.PROCESSING_INSTRUCTION_NODE:
167 return NodeFilter.SHOW_PROCESSING_INSTRUCTION;
168 case Node.COMMENT_NODE:
169 return NodeFilter.SHOW_COMMENT;
170 case Node.DOCUMENT_NODE:
171 return NodeFilter.SHOW_DOCUMENT;
172 case Node.DOCUMENT_TYPE_NODE:
173 return NodeFilter.SHOW_DOCUMENT_TYPE;
174 case Node.DOCUMENT_FRAGMENT_NODE:
175 return NodeFilter.SHOW_DOCUMENT_FRAGMENT;
176 case Node.NOTATION_NODE:
177 return NodeFilter.SHOW_NOTATION;
178 default:
179 return 0;
180 }
181 }
182
183 /**
184 * Moves to and returns the closest visible ancestor node of the current
185 * node. If the search for parentNode attempts to step upward from the
186 * TreeWalker's root node, or if it fails to find a visible ancestor node,
187 * this method retains the current position and returns null.
188 *
189 * @return The new parent node, or {@code null} if the current node
190 * has no parent in the TreeWalker's logical view.
191 */
192 @JsxFunction
193 public Node parentNode() {
194 return getNodeOrNull(walker_.parentNode());
195 }
196
197 private static Node getNodeOrNull(final DomNode domNode) {
198 if (domNode == null) {
199 return null;
200 }
201 return domNode.getScriptableObject();
202 }
203
204 /**
205 * Moves the TreeWalker to the first visible child of the current node,
206 * and returns the new node. If the current node has no visible children,
207 * returns {@code null}, and retains the current node.
208 *
209 * @return The new node, or {@code null} if the current node has no
210 * visible children in the TreeWalker's logical view.
211 */
212 @JsxFunction
213 public Node firstChild() {
214 return getNodeOrNull(walker_.firstChild());
215 }
216
217 /**
218 * Moves the TreeWalker to the last visible child of the current node,
219 * and returns the new node. If the current node has no visible children,
220 * returns {@code null}, and retains the current node.
221 *
222 * @return The new node, or {@code null} if the current node has no
223 * visible children in the TreeWalker's logical view.
224 */
225 @JsxFunction
226 public Node lastChild() {
227 return getNodeOrNull(walker_.lastChild());
228 }
229
230 /**
231 * Moves the TreeWalker to the previous sibling of the current node, and
232 * returns the new node. If the current node has no visible previous
233 * sibling, returns {@code null}, and retains the current node.
234 *
235 * @return The new node, or {@code null} if the current node has no
236 * previous sibling in the TreeWalker's logical view.
237 */
238 @JsxFunction
239 public Node previousSibling() {
240 return getNodeOrNull(walker_.previousSibling());
241 }
242
243 /**
244 * Moves the TreeWalker to the next sibling of the current node, and
245 * returns the new node. If the current node has no visible next sibling,
246 * returns {@code null}, and retains the current node.
247 *
248 * @return The new node, or {@code null} if the current node has no
249 * next sibling in the TreeWalker's logical view.
250 */
251 @JsxFunction
252 public Node nextSibling() {
253 return getNodeOrNull(walker_.nextSibling());
254 }
255
256 /**
257 * Moves the TreeWalker to the previous visible node in document order
258 * relative to the current node, and returns the new node. If the current
259 * node has no previous node, or if the search for previousNode attempts
260 * to step upward from the TreeWalker's root node, returns
261 * {@code null}, and retains the current node.
262 *
263 * @return The new node, or {@code null} if the current node has no
264 * previous node in the TreeWalker's logical view.
265 */
266 @JsxFunction
267 public Node previousNode() {
268 return getNodeOrNull(walker_.previousNode());
269 }
270
271 /**
272 * Moves the TreeWalker to the next visible node in document order
273 * relative to the current node, and returns the new node. If the current
274 * node has no next node, or if the search for nextNode attempts to step
275 * upward from the TreeWalker's root node, returns {@code null}, and
276 * retains the current node.
277 *
278 * @return The new node, or {@code null} if the current node has no
279 * next node in the TreeWalker's logical view.
280 */
281 @JsxFunction
282 public Node nextNode() {
283 return getNodeOrNull(walker_.nextNode());
284 }
285
286 }