1 /* 2 * reserved comment block 3 * DO NOT REMOVE OR ALTER! 4 */ 5 /* 6 * Copyright 1999-2002,2004 The Apache Software Foundation. 7 * 8 * Licensed under the Apache License, Version 2.0 (the "License"); 9 * you may not use this file except in compliance with the License. 10 * You may obtain a copy of the License at 11 * 12 * http://www.apache.org/licenses/LICENSE-2.0 13 * 14 * Unless required by applicable law or agreed to in writing, software 15 * distributed under the License is distributed on an "AS IS" BASIS, 16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 * See the License for the specific language governing permissions and 18 * limitations under the License. 19 */ 20 21 package com.sun.org.apache.xerces.internal.dom; 22 23 import org.w3c.dom.Node; 24 import org.w3c.dom.NodeList; 25 26 import java.util.Vector; 27 28 /** 29 * This class implements the DOM's NodeList behavior for 30 * Element.getElementsByTagName() 31 * <P> 32 * The DOM describes NodeList as follows: 33 * <P> 34 * 1) It may represent EITHER nodes scattered through a subtree (when 35 * returned by Element.getElementsByTagName), or just the immediate 36 * children (when returned by Node.getChildNodes). The latter is easy, 37 * but the former (which this class addresses) is more challenging. 38 * <P> 39 * 2) Its behavior is "live" -- that is, it always reflects the 40 * current state of the document tree. To put it another way, the 41 * NodeLists obtained before and after a series of insertions and 42 * deletions are effectively identical (as far as the user is 43 * concerned, the former has been dynamically updated as the changes 44 * have been made). 45 * <P> 46 * 3) Its API accesses individual nodes via an integer index, with the 47 * listed nodes numbered sequentially in the order that they were 48 * found during a preorder depth-first left-to-right search of the tree. 49 * (Of course in the case of getChildNodes, depth is not involved.) As 50 * nodes are inserted or deleted in the tree, and hence the NodeList, 51 * the numbering of nodes that follow them in the NodeList will 52 * change. 53 * <P> 54 * It is rather painful to support the latter two in the 55 * getElementsByTagName case. The current solution is for Nodes to 56 * maintain a change count (eventually that may be a Digest instead), 57 * which the NodeList tracks and uses to invalidate itself. 58 * <P> 59 * Unfortunately, this does _not_ respond efficiently in the case that 60 * the dynamic behavior was supposed to address: scanning a tree while 61 * it is being extended. That requires knowing which subtrees have 62 * changed, which can become an arbitrarily complex problem. 63 * <P> 64 * We save some work by filling the vector only as we access the 65 * item()s... but I suspect the same users who demanded index-based 66 * access will also start by doing a getLength() to control their loop, 67 * blowing this optimization out of the water. 68 * <P> 69 * NOTE: Level 2 of the DOM will probably _not_ use NodeList for its 70 * extended search mechanisms, partly for the reasons just discussed. 71 * 72 * @xerces.internal 73 * 74 * @since PR-DOM-Level-1-19980818. 75 */ 76 public class DeepNodeListImpl 77 implements NodeList { 78 79 // 80 // Data 81 // 82 83 protected NodeImpl rootNode; // Where the search started 84 protected String tagName; // Or "*" to mean all-tags-acceptable 85 protected int changes=0; 86 protected Vector nodes; 87 88 protected String nsName; 89 protected boolean enableNS = false; 90 91 // 92 // Constructors 93 // 94 95 /** Constructor. */ 96 public DeepNodeListImpl(NodeImpl rootNode, String tagName) { 97 this.rootNode = rootNode; 98 this.tagName = tagName; 99 nodes = new Vector(); 100 } 101 102 /** Constructor for Namespace support. */ 103 public DeepNodeListImpl(NodeImpl rootNode, 104 String nsName, String tagName) { 105 this(rootNode, tagName); 106 this.nsName = (nsName != null && !nsName.equals("")) ? nsName : null; 107 enableNS = true; 108 } 109 110 // 111 // NodeList methods 112 // 113 114 /** Returns the length of the node list. */ 115 public int getLength() { 116 // Preload all matching elements. (Stops when we run out of subtree!) 117 item(java.lang.Integer.MAX_VALUE); 118 return nodes.size(); 119 } 120 121 /** Returns the node at the specified index. */ 122 public Node item(int index) { 123 Node thisNode; 124 125 // Tree changed. Do it all from scratch! 126 if(rootNode.changes() != changes) { 127 nodes = new Vector(); 128 changes = rootNode.changes(); 129 } 130 131 // In the cache 132 if (index < nodes.size()) 133 return (Node)nodes.elementAt(index); 134 135 // Not yet seen 136 else { 137 138 // Pick up where we left off (Which may be the beginning) 139 if (nodes.size() == 0) 140 thisNode = rootNode; 141 else 142 thisNode=(NodeImpl)(nodes.lastElement()); 143 144 // Add nodes up to the one we're looking for 145 while(thisNode != null && index >= nodes.size()) { 146 thisNode=nextMatchingElementAfter(thisNode); 147 if (thisNode != null) 148 nodes.addElement(thisNode); 149 } 150 151 // Either what we want, or null (not avail.) 152 return thisNode; 153 } 154 155 } // item(int):Node 156 157 // 158 // Protected methods (might be overridden by an extending DOM) 159 // 160 161 /** 162 * Iterative tree-walker. When you have a Parent link, there's often no 163 * need to resort to recursion. NOTE THAT only Element nodes are matched 164 * since we're specifically supporting getElementsByTagName(). 165 */ 166 protected Node nextMatchingElementAfter(Node current) { 167 168 Node next; 169 while (current != null) { 170 // Look down to first child. 171 if (current.hasChildNodes()) { 172 current = (current.getFirstChild()); 173 } 174 175 // Look right to sibling (but not from root!) 176 else if (current != rootNode && null != (next = current.getNextSibling())) { 177 current = next; 178 } 179 180 // Look up and right (but not past root!) 181 else { 182 next = null; 183 for (; current != rootNode; // Stop when we return to starting point 184 current = current.getParentNode()) { 185 186 next = current.getNextSibling(); 187 if (next != null) 188 break; 189 } 190 current = next; 191 } 192 193 // Have we found an Element with the right tagName? 194 // ("*" matches anything.) 195 if (current != rootNode 196 && current != null 197 && current.getNodeType() == Node.ELEMENT_NODE) { 198 if (!enableNS) { 199 if (tagName.equals("*") || 200 ((ElementImpl) current).getTagName().equals(tagName)) 201 { 202 return current; 203 } 204 } else { 205 // DOM2: Namespace logic. 206 if (tagName.equals("*")) { 207 if (nsName != null && nsName.equals("*")) { 208 return current; 209 } else { 210 ElementImpl el = (ElementImpl) current; 211 if ((nsName == null 212 && el.getNamespaceURI() == null) 213 || (nsName != null 214 && nsName.equals(el.getNamespaceURI()))) 215 { 216 return current; 217 } 218 } 219 } else { 220 ElementImpl el = (ElementImpl) current; 221 if (el.getLocalName() != null 222 && el.getLocalName().equals(tagName)) { 223 if (nsName != null && nsName.equals("*")) { 224 return current; 225 } else { 226 if ((nsName == null 227 && el.getNamespaceURI() == null) 228 || (nsName != null && 229 nsName.equals(el.getNamespaceURI()))) 230 { 231 return current; 232 } 233 } 234 } 235 } 236 } 237 } 238 239 // Otherwise continue walking the tree 240 } 241 242 // Fell out of tree-walk; no more instances found 243 return null; 244 245 } // nextMatchingElementAfter(int):Node 246 247 } // class DeepNodeListImpl