| /* |
| * reserved comment block |
| * DO NOT REMOVE OR ALTER! |
| */ |
| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.sun.org.apache.xerces.internal.dom; |
| |
| import org.w3c.dom.Node; |
| import org.w3c.dom.NodeList; |
| |
| import java.util.Vector; |
| |
| /** |
| * This class implements the DOM's NodeList behavior for |
| * Element.getElementsByTagName() |
| * <P> |
| * The DOM describes NodeList as follows: |
| * <P> |
| * 1) It may represent EITHER nodes scattered through a subtree (when |
| * returned by Element.getElementsByTagName), or just the immediate |
| * children (when returned by Node.getChildNodes). The latter is easy, |
| * but the former (which this class addresses) is more challenging. |
| * <P> |
| * 2) Its behavior is "live" -- that is, it always reflects the |
| * current state of the document tree. To put it another way, the |
| * NodeLists obtained before and after a series of insertions and |
| * deletions are effectively identical (as far as the user is |
| * concerned, the former has been dynamically updated as the changes |
| * have been made). |
| * <P> |
| * 3) Its API accesses individual nodes via an integer index, with the |
| * listed nodes numbered sequentially in the order that they were |
| * found during a preorder depth-first left-to-right search of the tree. |
| * (Of course in the case of getChildNodes, depth is not involved.) As |
| * nodes are inserted or deleted in the tree, and hence the NodeList, |
| * the numbering of nodes that follow them in the NodeList will |
| * change. |
| * <P> |
| * It is rather painful to support the latter two in the |
| * getElementsByTagName case. The current solution is for Nodes to |
| * maintain a change count (eventually that may be a Digest instead), |
| * which the NodeList tracks and uses to invalidate itself. |
| * <P> |
| * Unfortunately, this does _not_ respond efficiently in the case that |
| * the dynamic behavior was supposed to address: scanning a tree while |
| * it is being extended. That requires knowing which subtrees have |
| * changed, which can become an arbitrarily complex problem. |
| * <P> |
| * We save some work by filling the vector only as we access the |
| * item()s... but I suspect the same users who demanded index-based |
| * access will also start by doing a getLength() to control their loop, |
| * blowing this optimization out of the water. |
| * <P> |
| * NOTE: Level 2 of the DOM will probably _not_ use NodeList for its |
| * extended search mechanisms, partly for the reasons just discussed. |
| * |
| * @xerces.internal |
| * |
| * @since PR-DOM-Level-1-19980818. |
| */ |
| public class DeepNodeListImpl |
| implements NodeList { |
| |
| // |
| // Data |
| // |
| |
| protected NodeImpl rootNode; // Where the search started |
| protected String tagName; // Or "*" to mean all-tags-acceptable |
| protected int changes=0; |
| protected Vector nodes; |
| |
| protected String nsName; |
| protected boolean enableNS = false; |
| |
| // |
| // Constructors |
| // |
| |
| /** Constructor. */ |
| public DeepNodeListImpl(NodeImpl rootNode, String tagName) { |
| this.rootNode = rootNode; |
| this.tagName = tagName; |
| nodes = new Vector(); |
| } |
| |
| /** Constructor for Namespace support. */ |
| public DeepNodeListImpl(NodeImpl rootNode, |
| String nsName, String tagName) { |
| this(rootNode, tagName); |
| this.nsName = (nsName != null && !nsName.equals("")) ? nsName : null; |
| enableNS = true; |
| } |
| |
| // |
| // NodeList methods |
| // |
| |
| /** Returns the length of the node list. */ |
| public int getLength() { |
| // Preload all matching elements. (Stops when we run out of subtree!) |
| item(java.lang.Integer.MAX_VALUE); |
| return nodes.size(); |
| } |
| |
| /** Returns the node at the specified index. */ |
| public Node item(int index) { |
| Node thisNode; |
| |
| // Tree changed. Do it all from scratch! |
| if(rootNode.changes() != changes) { |
| nodes = new Vector(); |
| changes = rootNode.changes(); |
| } |
| |
| // In the cache |
| if (index < nodes.size()) |
| return (Node)nodes.elementAt(index); |
| |
| // Not yet seen |
| else { |
| |
| // Pick up where we left off (Which may be the beginning) |
| if (nodes.size() == 0) |
| thisNode = rootNode; |
| else |
| thisNode=(NodeImpl)(nodes.lastElement()); |
| |
| // Add nodes up to the one we're looking for |
| while(thisNode != null && index >= nodes.size()) { |
| thisNode=nextMatchingElementAfter(thisNode); |
| if (thisNode != null) |
| nodes.addElement(thisNode); |
| } |
| |
| // Either what we want, or null (not avail.) |
| return thisNode; |
| } |
| |
| } // item(int):Node |
| |
| // |
| // Protected methods (might be overridden by an extending DOM) |
| // |
| |
| /** |
| * Iterative tree-walker. When you have a Parent link, there's often no |
| * need to resort to recursion. NOTE THAT only Element nodes are matched |
| * since we're specifically supporting getElementsByTagName(). |
| */ |
| protected Node nextMatchingElementAfter(Node current) { |
| |
| Node next; |
| while (current != null) { |
| // Look down to first child. |
| if (current.hasChildNodes()) { |
| current = (current.getFirstChild()); |
| } |
| |
| // Look right to sibling (but not from root!) |
| else if (current != rootNode && null != (next = current.getNextSibling())) { |
| current = next; |
| } |
| |
| // Look up and right (but not past root!) |
| else { |
| next = null; |
| for (; current != rootNode; // Stop when we return to starting point |
| current = current.getParentNode()) { |
| |
| next = current.getNextSibling(); |
| if (next != null) |
| break; |
| } |
| current = next; |
| } |
| |
| // Have we found an Element with the right tagName? |
| // ("*" matches anything.) |
| if (current != rootNode |
| && current != null |
| && current.getNodeType() == Node.ELEMENT_NODE) { |
| if (!enableNS) { |
| if (tagName.equals("*") || |
| ((ElementImpl) current).getTagName().equals(tagName)) |
| { |
| return current; |
| } |
| } else { |
| // DOM2: Namespace logic. |
| if (tagName.equals("*")) { |
| if (nsName != null && nsName.equals("*")) { |
| return current; |
| } else { |
| ElementImpl el = (ElementImpl) current; |
| if ((nsName == null |
| && el.getNamespaceURI() == null) |
| || (nsName != null |
| && nsName.equals(el.getNamespaceURI()))) |
| { |
| return current; |
| } |
| } |
| } else { |
| ElementImpl el = (ElementImpl) current; |
| if (el.getLocalName() != null |
| && el.getLocalName().equals(tagName)) { |
| if (nsName != null && nsName.equals("*")) { |
| return current; |
| } else { |
| if ((nsName == null |
| && el.getNamespaceURI() == null) |
| || (nsName != null && |
| nsName.equals(el.getNamespaceURI()))) |
| { |
| return current; |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| // Otherwise continue walking the tree |
| } |
| |
| // Fell out of tree-walk; no more instances found |
| return null; |
| |
| } // nextMatchingElementAfter(int):Node |
| |
| } // class DeepNodeListImpl |