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