View Javadoc
1   /*
2    * Copyright (c) 2002-2017 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    * http://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 com.gargoylesoftware.htmlunit.javascript.host.dom;
16  
17  import static com.gargoylesoftware.htmlunit.javascript.configuration.SupportedBrowser.CHROME;
18  import static com.gargoylesoftware.htmlunit.javascript.configuration.SupportedBrowser.EDGE;
19  import static com.gargoylesoftware.htmlunit.javascript.configuration.SupportedBrowser.FF;
20  import static com.gargoylesoftware.htmlunit.javascript.configuration.SupportedBrowser.IE;
21  
22  import org.w3c.dom.DOMException;
23  
24  import com.gargoylesoftware.htmlunit.SgmlPage;
25  import com.gargoylesoftware.htmlunit.html.DomNode;
26  import com.gargoylesoftware.htmlunit.html.DomTreeWalker;
27  import com.gargoylesoftware.htmlunit.javascript.SimpleScriptable;
28  import com.gargoylesoftware.htmlunit.javascript.configuration.JsxClass;
29  import com.gargoylesoftware.htmlunit.javascript.configuration.JsxConstructor;
30  import com.gargoylesoftware.htmlunit.javascript.configuration.JsxFunction;
31  import com.gargoylesoftware.htmlunit.javascript.configuration.JsxGetter;
32  import com.gargoylesoftware.htmlunit.javascript.configuration.JsxSetter;
33  
34  import net.sourceforge.htmlunit.corejs.javascript.Context;
35  
36  /**
37   * The JavaScript object that represents a {@code TreeWalker}.
38   *
39   * @see <a href="http://www.w3.org/TR/DOM-Level-2-Traversal-Range/traversal.html">
40   * DOM-Level-2-Traversal-Range</a>
41   * @author <a href="mailto:mike@10gen.com">Mike Dirolf</a>
42   * @author Frank Danek
43   * @author Ahmed Ashour
44   */
45  @JsxClass
46  public class TreeWalker extends SimpleScriptable {
47  
48      private DomTreeWalker walker_;
49  
50      /**
51       * Creates an instance.
52       */
53      @JsxConstructor({CHROME, FF, EDGE})
54      public TreeWalker() {
55      }
56  
57      /**
58       * Creates an instance.
59       *
60       * @param page the page
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       * @throws DOMException on attempt to create a TreeWalker with a root that
71       *          is {@code null}.
72       */
73      public TreeWalker(final SgmlPage page, final Node root,
74                        final int whatToShow,
75                        final org.w3c.dom.traversal.NodeFilter filter,
76                        final boolean expandEntityReferences) throws DOMException {
77          if (root == null) {
78              Context.throwAsScriptRuntimeEx(new DOMException(DOMException.NOT_SUPPORTED_ERR,
79                                     "root must not be null"));
80          }
81          walker_ = page.createTreeWalker(root.getDomNodeOrDie(), whatToShow, filter, expandEntityReferences);
82      }
83  
84      /**
85       * Gets the root node of the TreeWalker, as specified when it was created.
86       *
87       * @return the root node of the TreeWalker
88       */
89      @JsxGetter
90      public Node getRoot() {
91          return getNodeOrNull(walker_.getRoot());
92      }
93  
94      /**
95       * Gets the whatToShow attribute of the TreeWalker. This attribute
96       * determines which node types are presented via the TreeWalker. The set
97       * of available constants is defined in {@link NodeFilter}.
98       *
99       * @return the value of the whatToShow attribute of the TreeWalker
100      */
101     @JsxGetter
102     public long getWhatToShow() {
103         long whatToShow = walker_.getWhatToShow();
104         if (whatToShow == org.w3c.dom.traversal.NodeFilter.SHOW_ALL) {
105             whatToShow = 0xFFFFFFFFL;
106         }
107         return whatToShow;
108     }
109 
110     /**
111      * Gets the filter used to screen nodes.
112      *
113      * @return the filter used to screen nodes
114      */
115     @JsxGetter
116     public Object getFilter() {
117         //TODO: we should return the original filter
118         return walker_.getFilter();
119     }
120 
121     /**
122      * Gets the flag specifying whether or not to reject EntityReference nodes.
123      *
124      * @return the value of the expandEntityReferences flag
125      */
126     @JsxGetter(IE)
127     public boolean isExpandEntityReferences() {
128         return walker_.getExpandEntityReferences();
129     }
130 
131     /**
132      * Gets the node at which the TreeWalker is currently positioned.
133      *
134      * @return the currentNode
135      */
136     @JsxGetter
137     public Node getCurrentNode() {
138         return getNodeOrNull(walker_.getCurrentNode());
139     }
140 
141     /**
142      * Sets the node at which the TreeWalker is currently positioned.
143      *
144      * @param currentNode The node to be used as the current position of the
145      *          TreeWalker.
146      * @throws DOMException on attempt to set currentNode to
147      *          {@code null}.
148      */
149     @JsxSetter
150     public void setCurrentNode(final Node currentNode) throws DOMException {
151         if (currentNode == null) {
152             throw new DOMException(DOMException.NOT_SUPPORTED_ERR,
153                                    "currentNode cannot be set to null");
154         }
155         walker_.setCurrentNode(currentNode.getDomNodeOrDie());
156     }
157 
158     /**
159      * Given a {@link Node}, return the appropriate constant for whatToShow.
160      *
161      * @param node the node
162      * @return the whatToShow constant for the type of specified node
163      */
164     static int getFlagForNode(final Node node) {
165         switch (node.getNodeType()) {
166             case Node.ELEMENT_NODE:
167                 return NodeFilter.SHOW_ELEMENT;
168             case Node.ATTRIBUTE_NODE:
169                 return NodeFilter.SHOW_ATTRIBUTE;
170             case Node.TEXT_NODE:
171                 return NodeFilter.SHOW_TEXT;
172             case Node.CDATA_SECTION_NODE:
173                 return NodeFilter.SHOW_CDATA_SECTION;
174             case Node.ENTITY_REFERENCE_NODE:
175                 return NodeFilter.SHOW_ENTITY_REFERENCE;
176             case Node.ENTITY_NODE:
177                 return NodeFilter.SHOW_ENTITY;
178             case Node.PROCESSING_INSTRUCTION_NODE:
179                 return NodeFilter.SHOW_PROCESSING_INSTRUCTION;
180             case Node.COMMENT_NODE:
181                 return NodeFilter.SHOW_COMMENT;
182             case Node.DOCUMENT_NODE:
183                 return NodeFilter.SHOW_DOCUMENT;
184             case Node.DOCUMENT_TYPE_NODE:
185                 return NodeFilter.SHOW_DOCUMENT_TYPE;
186             case Node.DOCUMENT_FRAGMENT_NODE:
187                 return NodeFilter.SHOW_DOCUMENT_FRAGMENT;
188             case Node.NOTATION_NODE:
189                 return NodeFilter.SHOW_NOTATION;
190             default:
191                 return 0;
192         }
193     }
194 
195     /**
196      * Moves to and returns the closest visible ancestor node of the current
197      * node. If the search for parentNode attempts to step upward from the
198      * TreeWalker's root node, or if it fails to find a visible ancestor node,
199      * this method retains the current position and returns null.
200      *
201      * @return The new parent node, or {@code null} if the current node
202      *          has no parent in the TreeWalker's logical view.
203      */
204     @JsxFunction
205     public Node parentNode() {
206         return getNodeOrNull(walker_.parentNode());
207     }
208 
209     private static Node getNodeOrNull(final DomNode domNode) {
210         if (domNode == null) {
211             return null;
212         }
213         return (Node) domNode.getScriptableObject();
214     }
215 
216     /**
217      * Moves the TreeWalker to the first visible child of the current node,
218      * and returns the new node. If the current node has no visible children,
219      * returns {@code null}, and retains the current node.
220      *
221      * @return The new node, or {@code null} if the current node has no
222      *          visible children in the TreeWalker's logical view.
223      */
224     @JsxFunction
225     public Node firstChild() {
226         return getNodeOrNull(walker_.firstChild());
227     }
228 
229     /**
230      * Moves the TreeWalker to the last visible child of the current node,
231      * and returns the new node. If the current node has no visible children,
232      * returns {@code null}, and retains the current node.
233      *
234      * @return The new node, or {@code null} if the current node has no
235      *          visible children in the TreeWalker's logical view.
236      */
237     @JsxFunction
238     public Node lastChild() {
239         return getNodeOrNull(walker_.lastChild());
240     }
241 
242     /**
243       * Moves the TreeWalker to the previous sibling of the current node, and
244       * returns the new node. If the current node has no visible previous
245       * sibling, returns {@code null}, and retains the current node.
246       *
247       * @return The new node, or {@code null} if the current node has no
248       *          previous sibling in the TreeWalker's logical view.
249       */
250     @JsxFunction
251     public Node previousSibling() {
252         return getNodeOrNull(walker_.previousSibling());
253     }
254 
255      /**
256       * Moves the TreeWalker to the next sibling of the current node, and
257       * returns the new node. If the current node has no visible next sibling,
258       * returns {@code null}, and retains the current node.
259       *
260       * @return The new node, or {@code null} if the current node has no
261       *          next sibling in the TreeWalker's logical view.
262       */
263     @JsxFunction
264     public Node nextSibling() {
265         return getNodeOrNull(walker_.nextSibling());
266     }
267 
268     /**
269      * Moves the TreeWalker to the previous visible node in document order
270      * relative to the current node, and returns the new node. If the current
271      * node has no previous node, or if the search for previousNode attempts
272      * to step upward from the TreeWalker's root node, returns
273      * {@code null}, and retains the current node.
274      *
275      * @return The new node, or {@code null} if the current node has no
276      *          previous node in the TreeWalker's logical view.
277      */
278     @JsxFunction
279     public Node previousNode() {
280         return getNodeOrNull(walker_.previousNode());
281     }
282 
283     /**
284      * Moves the TreeWalker to the next visible node in document order
285      * relative to the current node, and returns the new node. If the current
286      * node has no next node, or if the search for nextNode attempts to step
287      * upward from the TreeWalker's root node, returns {@code null}, and
288      * retains the current node.
289      *
290      * @return The new node, or {@code null} if the current node has no
291      *          next node in the TreeWalker's logical view.
292      */
293     @JsxFunction
294     public Node nextNode() {
295         return getNodeOrNull(walker_.nextNode());
296     }
297 
298 }