View Javadoc
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 <a href="mailto:mike@10gen.com">Mike Dirolf</a>
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 }