View Javadoc
1   /*
2    * Copyright (c) 2002-2026 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         return switch (node.getNodeType()) {
154             case Node.ELEMENT_NODE -> NodeFilter.SHOW_ELEMENT;
155             case Node.ATTRIBUTE_NODE -> NodeFilter.SHOW_ATTRIBUTE;
156             case Node.TEXT_NODE -> NodeFilter.SHOW_TEXT;
157             case Node.CDATA_SECTION_NODE -> NodeFilter.SHOW_CDATA_SECTION;
158             case Node.ENTITY_REFERENCE_NODE -> NodeFilter.SHOW_ENTITY_REFERENCE;
159             case Node.ENTITY_NODE -> NodeFilter.SHOW_ENTITY;
160             case Node.PROCESSING_INSTRUCTION_NODE -> NodeFilter.SHOW_PROCESSING_INSTRUCTION;
161             case Node.COMMENT_NODE -> NodeFilter.SHOW_COMMENT;
162             case Node.DOCUMENT_NODE -> NodeFilter.SHOW_DOCUMENT;
163             case Node.DOCUMENT_TYPE_NODE -> NodeFilter.SHOW_DOCUMENT_TYPE;
164             case Node.DOCUMENT_FRAGMENT_NODE -> NodeFilter.SHOW_DOCUMENT_FRAGMENT;
165             case Node.NOTATION_NODE -> NodeFilter.SHOW_NOTATION;
166             default -> 0;
167         };
168     }
169 
170     /**
171      * Moves to and returns the closest visible ancestor node of the current
172      * node. If the search for parentNode attempts to step upward from the
173      * TreeWalker's root node, or if it fails to find a visible ancestor node,
174      * this method retains the current position and returns null.
175      *
176      * @return The new parent node, or {@code null} if the current node
177      *          has no parent in the TreeWalker's logical view.
178      */
179     @JsxFunction
180     public Node parentNode() {
181         return getNodeOrNull(walker_.parentNode());
182     }
183 
184     private static Node getNodeOrNull(final DomNode domNode) {
185         if (domNode == null) {
186             return null;
187         }
188         return domNode.getScriptableObject();
189     }
190 
191     /**
192      * Moves the TreeWalker to the first visible child of the current node,
193      * and returns the new node. If the current node has no visible children,
194      * returns {@code null}, and retains the current node.
195      *
196      * @return The new node, or {@code null} if the current node has no
197      *          visible children in the TreeWalker's logical view.
198      */
199     @JsxFunction
200     public Node firstChild() {
201         return getNodeOrNull(walker_.firstChild());
202     }
203 
204     /**
205      * Moves the TreeWalker to the last 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 lastChild() {
214         return getNodeOrNull(walker_.lastChild());
215     }
216 
217     /**
218      * Moves the TreeWalker to the previous sibling of the current node, and
219      * returns the new node. If the current node has no visible previous
220      * sibling, returns {@code null}, and retains the current node.
221      *
222      * @return The new node, or {@code null} if the current node has no
223      *          previous sibling in the TreeWalker's logical view.
224      */
225     @JsxFunction
226     public Node previousSibling() {
227         return getNodeOrNull(walker_.previousSibling());
228     }
229 
230      /**
231       * Moves the TreeWalker to the next sibling of the current node, and
232       * returns the new node. If the current node has no visible next sibling,
233       * returns {@code null}, and retains the current node.
234       *
235       * @return The new node, or {@code null} if the current node has no
236       *          next sibling in the TreeWalker's logical view.
237       */
238     @JsxFunction
239     public Node nextSibling() {
240         return getNodeOrNull(walker_.nextSibling());
241     }
242 
243     /**
244      * Moves the TreeWalker to the previous visible node in document order
245      * relative to the current node, and returns the new node. If the current
246      * node has no previous node, or if the search for previousNode attempts
247      * to step upward from the TreeWalker's root node, returns
248      * {@code null}, and retains the current node.
249      *
250      * @return The new node, or {@code null} if the current node has no
251      *          previous node in the TreeWalker's logical view.
252      */
253     @JsxFunction
254     public Node previousNode() {
255         return getNodeOrNull(walker_.previousNode());
256     }
257 
258     /**
259      * Moves the TreeWalker to the next visible node in document order
260      * relative to the current node, and returns the new node. If the current
261      * node has no next node, or if the search for nextNode attempts to step
262      * upward from the TreeWalker's root node, returns {@code null}, and
263      * retains the current node.
264      *
265      * @return The new node, or {@code null} if the current node has no
266      *          next node in the TreeWalker's logical view.
267      */
268     @JsxFunction
269     public Node nextNode() {
270         return getNodeOrNull(walker_.nextNode());
271     }
272 
273 }