1 /*
2  * reserved comment block
3  * DO NOT REMOVE OR ALTER!
4  */
5 /*
6  * Licensed to the Apache Software Foundation (ASF) under one or more
7  * contributor license agreements.  See the NOTICE file distributed with
8  * this work for additional information regarding copyright ownership.
9  * The ASF licenses this file to You under the Apache License, Version 2.0
10  * (the "License"); you may not use this file except in compliance with
11  * the License.  You may obtain a copy of the License at
12  *
13  *      http://www.apache.org/licenses/LICENSE-2.0
14  *
15  * Unless required by applicable law or agreed to in writing, software
16  * distributed under the License is distributed on an "AS IS" BASIS,
17  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18  * See the License for the specific language governing permissions and
19  * limitations under the License.
20  */
21 
22 package com.sun.org.apache.xerces.internal.dom;
23 
24 import org.w3c.dom.DOMException;
25 import org.w3c.dom.Node;
26 import org.w3c.dom.traversal.NodeFilter;
27 import org.w3c.dom.traversal.NodeIterator;
28 
29 
30 /** DefaultNodeIterator implements a NodeIterator, which iterates a
31  *  DOM tree in the expected depth first way.
32  *
33  *  <p>The whatToShow and filter functionality is implemented as expected.
34  *
35  *  <p>This class also has method removeNode to enable iterator "fix-up"
36  *  on DOM remove. It is expected that the DOM implementation call removeNode
37  *  right before the actual DOM transformation. If not called by the DOM,
38  *  the client could call it before doing the removal.
39  *
40  * @xerces.internal
41  *
42  */
43 public class NodeIteratorImpl implements NodeIterator {
44 
45     //
46     // Data
47     //
48 
49     /** The DocumentImpl which created this iterator, so it can be detached. */
50     private DocumentImpl fDocument;
51     /** The root. */
52     private Node fRoot;
53     /** The whatToShow mask. */
54     private int fWhatToShow = NodeFilter.SHOW_ALL;
55     /** The NodeFilter reference. */
56     private NodeFilter fNodeFilter;
57     /** If detach is called, the fDetach flag is true, otherwise flase. */
58     private boolean fDetach = false;
59 
60     //
61     // Iterator state - current node and direction.
62     //
63     // Note: The current node and direction are sufficient to implement
64     // the desired behaviour of the current pointer being _between_
65     // two nodes. The fCurrentNode is actually the last node returned,
66     // and the
67     // direction is whether the pointer is in front or behind this node.
68     // (usually akin to whether the node was returned via nextNode())
69     // (eg fForward = true) or previousNode() (eg fForward = false).
70     // Note also, if removing a Node, the fCurrentNode
71     // can be placed on a Node which would not pass filters.
72 
73     /** The last Node returned. */
74     private Node fCurrentNode;
75 
76     /** The direction of the iterator on the fCurrentNode.
77      *  <pre>
78      *  nextNode()  ==      fForward = true;
79      *  previousNode() ==   fForward = false;
80      *  </pre>
81      */
82     private boolean fForward = true;
83 
84     /** When TRUE, the children of entites references are returned in the iterator. */
85     private boolean fEntityReferenceExpansion;
86 
87     //
88     // Constructor
89     //
90 
91     /** Public constructor */
NodeIteratorImpl( DocumentImpl document, Node root, int whatToShow, NodeFilter nodeFilter, boolean entityReferenceExpansion)92     public NodeIteratorImpl( DocumentImpl document,
93                              Node root,
94                              int whatToShow,
95                              NodeFilter nodeFilter,
96                              boolean entityReferenceExpansion) {
97         fDocument = document;
98         fRoot = root;
99         fCurrentNode = null;
100         fWhatToShow = whatToShow;
101         fNodeFilter = nodeFilter;
102         fEntityReferenceExpansion = entityReferenceExpansion;
103     }
104 
getRoot()105     public Node getRoot() {
106         return fRoot;
107     }
108 
109     // Implementation Note: Note that the iterator looks at whatToShow
110     // and filter values at each call, and therefore one _could_ add
111     // setters for these values and alter them while iterating!
112 
113     /** Return the whatToShow value */
getWhatToShow()114     public int                getWhatToShow() {
115         return fWhatToShow;
116     }
117 
118     /** Return the filter */
getFilter()119     public NodeFilter         getFilter() {
120         return fNodeFilter;
121     }
122 
123     /** Return whether children entity references are included in the iterator. */
getExpandEntityReferences()124     public boolean            getExpandEntityReferences() {
125         return fEntityReferenceExpansion;
126     }
127 
128     /** Return the next Node in the Iterator. The node is the next node in
129      *  depth-first order which also passes the filter, and whatToShow.
130      *  If there is no next node which passes these criteria, then return null.
131      */
nextNode()132     public Node               nextNode() {
133 
134         if( fDetach) {
135                 throw new DOMException(
136                 DOMException.INVALID_STATE_ERR,
137                 DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "INVALID_STATE_ERR", null));
138         }
139 
140         // if root is null there is no next node.
141         if (fRoot == null) return null;
142 
143         Node nextNode = fCurrentNode;
144         boolean accepted = false; // the next node has not been accepted.
145 
146         accepted_loop:
147         while (!accepted) {
148 
149             // if last direction is not forward, repeat node.
150             if (!fForward && nextNode!=null) {
151                 //System.out.println("nextNode():!fForward:"+fCurrentNode.getNodeName());
152                 nextNode = fCurrentNode;
153             } else {
154             // else get the next node via depth-first
155                 if (!fEntityReferenceExpansion
156                     && nextNode != null
157                     && nextNode.getNodeType() == Node.ENTITY_REFERENCE_NODE) {
158                     nextNode = nextNode(nextNode, false);
159                 } else {
160                     nextNode = nextNode(nextNode, true);
161                 }
162             }
163 
164             fForward = true; //REVIST: should direction be set forward before null check?
165 
166             // nothing in the list. return null.
167             if (nextNode == null) return null;
168 
169             // does node pass the filters and whatToShow?
170             accepted = acceptNode(nextNode);
171             if (accepted) {
172                 // if so, then the node is the current node.
173                 fCurrentNode = nextNode;
174                 return fCurrentNode;
175             } else
176                 continue accepted_loop;
177 
178         } // while (!accepted) {
179 
180         // no nodes, or no accepted nodes.
181         return null;
182 
183     }
184 
185     /** Return the previous Node in the Iterator. The node is the next node in
186      *  _backwards_ depth-first order which also passes the filter, and whatToShow.
187      */
previousNode()188     public Node               previousNode() {
189 
190         if( fDetach) {
191                 throw new DOMException(
192                 DOMException.INVALID_STATE_ERR,
193                 DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "INVALID_STATE_ERR", null));
194         }
195 
196         // if the root is null, or the current node is null, return null.
197         if (fRoot == null || fCurrentNode == null) return null;
198 
199         Node previousNode = fCurrentNode;
200         boolean accepted = false;
201 
202         accepted_loop:
203         while (!accepted) {
204 
205             if (fForward && previousNode != null) {
206                 //repeat last node.
207                 previousNode = fCurrentNode;
208             } else {
209                 // get previous node in backwards depth first order.
210                 previousNode = previousNode(previousNode);
211             }
212 
213             // we are going backwards
214             fForward = false;
215 
216             // if the new previous node is null, we're at head or past the root,
217             // so return null.
218             if (previousNode == null) return null;
219 
220             // check if node passes filters and whatToShow.
221             accepted = acceptNode(previousNode);
222             if (accepted) {
223                 // if accepted, update the current node, and return it.
224                 fCurrentNode = previousNode;
225                 return fCurrentNode;
226             } else
227                 continue accepted_loop;
228         }
229         // there are no nodes?
230         return null;
231     }
232 
233     /** The node is accepted if it passes the whatToShow and the filter. */
acceptNode(Node node)234     boolean acceptNode(Node node) {
235 
236         if (fNodeFilter == null) {
237             return ( fWhatToShow & (1 << node.getNodeType()-1)) != 0 ;
238         } else {
239             return ((fWhatToShow & (1 << node.getNodeType()-1)) != 0 )
240                 && fNodeFilter.acceptNode(node) == NodeFilter.FILTER_ACCEPT;
241         }
242     }
243 
244     /** Return node, if matches or any parent if matches. */
matchNodeOrParent(Node node)245     Node matchNodeOrParent(Node node) {
246         // Additions and removals in the underlying data structure may occur
247         // before any iterations, and in this case the reference_node is null.
248         if (fCurrentNode == null) return null;
249 
250         // check if the removed node is an _ancestor_ of the
251         // reference node
252         for (Node n = fCurrentNode; n != fRoot; n = n.getParentNode()) {
253             if (node == n) return n;
254         }
255         return null;
256     }
257 
258     /** The method nextNode(Node, boolean) returns the next node
259      *  from the actual DOM tree.
260      *
261      *  The boolean visitChildren determines whether to visit the children.
262      *  The result is the nextNode.
263      */
nextNode(Node node, boolean visitChildren)264     Node nextNode(Node node, boolean visitChildren) {
265 
266         if (node == null) return fRoot;
267 
268         Node result;
269         // only check children if we visit children.
270         if (visitChildren) {
271             //if hasChildren, return 1st child.
272             if (node.hasChildNodes()) {
273                 result = node.getFirstChild();
274                 return result;
275             }
276         }
277 
278         if (node == fRoot) { //if Root has no kids
279             return null;
280         }
281 
282         // if hasSibling, return sibling
283         result = node.getNextSibling();
284         if (result != null) return result;
285 
286 
287         // return parent's 1st sibling.
288         Node parent = node.getParentNode();
289         while (parent != null && parent != fRoot) {
290             result = parent.getNextSibling();
291             if (result != null) {
292                 return result;
293             } else {
294                 parent = parent.getParentNode();
295             }
296 
297         } // while (parent != null && parent != fRoot) {
298 
299         // end of list, return null
300         return null;
301     }
302 
303     /** The method previousNode(Node) returns the previous node
304      *  from the actual DOM tree.
305      */
previousNode(Node node)306     Node previousNode(Node node) {
307 
308         Node result;
309 
310         // if we're at the root, return null.
311         if (node == fRoot) return null;
312 
313         // get sibling
314         result = node.getPreviousSibling();
315         if (result == null) {
316             //if 1st sibling, return parent
317             result = node.getParentNode();
318             return result;
319         }
320 
321         // if sibling has children, keep getting last child of child.
322         if (result.hasChildNodes()
323             && !(!fEntityReferenceExpansion
324                 && result != null
325                 && result.getNodeType() == Node.ENTITY_REFERENCE_NODE))
326 
327         {
328             while (result.hasChildNodes()) {
329                 result = result.getLastChild();
330             }
331         }
332 
333         return result;
334     }
335 
336     /** Fix-up the iterator on a remove. Called by DOM or otherwise,
337      *  before an actual DOM remove.
338      */
removeNode(Node node)339     public void removeNode(Node node) {
340 
341         // Implementation note: Fix-up means setting the current node properly
342         // after a remove.
343 
344         if (node == null) return;
345 
346         Node deleted = matchNodeOrParent(node);
347 
348         if (deleted == null) return;
349 
350         if (fForward) {
351             fCurrentNode = previousNode(deleted);
352         } else
353         // if (!fForward)
354         {
355             Node next = nextNode(deleted, false);
356             if (next!=null) {
357                 // normal case: there _are_ nodes following this in the iterator.
358                 fCurrentNode = next;
359             } else {
360                 // the last node in the iterator is to be removed,
361                 // so we set the current node to be the previous one.
362                 fCurrentNode = previousNode(deleted);
363                 fForward = true;
364             }
365 
366         }
367 
368     }
369 
detach()370     public void               detach() {
371         fDetach = true;
372         fDocument.removeNodeIterator(this);
373     }
374 
375 }
376