GME  13
DOMDeepNodeListImpl.cpp
Go to the documentation of this file.
00001 /*
00002  * Licensed to the Apache Software Foundation (ASF) under one or more
00003  * contributor license agreements.  See the NOTICE file distributed with
00004  * this work for additional information regarding copyright ownership.
00005  * The ASF licenses this file to You under the Apache License, Version 2.0
00006  * (the "License"); you may not use this file except in compliance with
00007  * the License.  You may obtain a copy of the License at
00008  *
00009  *      http://www.apache.org/licenses/LICENSE-2.0
00010  *
00011  * Unless required by applicable law or agreed to in writing, software
00012  * distributed under the License is distributed on an "AS IS" BASIS,
00013  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00014  * See the License for the specific language governing permissions and
00015  * limitations under the License.
00016  */
00017 
00018 /*
00019  * $Id: DOMDeepNodeListImpl.cpp 678381 2008-07-21 10:15:01Z borisk $
00020  */
00021 
00022 #include "DOMDeepNodeListImpl.hpp"
00023 #include "DOMElementImpl.hpp"
00024 #include "DOMDocumentImpl.hpp"
00025 #include "DOMCasts.hpp"
00026 #include "DOMNodeImpl.hpp"
00027 #include <xercesc/util/XMLUniDefs.hpp>
00028 #include <limits.h>
00029 
00030 XERCES_CPP_NAMESPACE_BEGIN
00031 
00032 
00033 static const XMLCh kAstr[] = {chAsterisk, chNull};
00034 
00035 DOMDeepNodeListImpl::DOMDeepNodeListImpl(const DOMNode *rootNode,
00036                                        const XMLCh *tagName)
00037     : fRootNode(rootNode)
00038     , fChanges(0)
00039     , fCurrentNode(0)
00040     , fCurrentIndexPlus1(0)
00041     , fNamespaceURI(0)
00042     , fMatchAllURI(false)
00043     , fMatchURIandTagname(false)
00044 {
00045     fTagName = ((DOMDocumentImpl *)(castToNodeImpl(rootNode)->getOwnerDocument()))->getPooledString(tagName);
00046     fMatchAll = XMLString::equals(fTagName, kAstr);
00047 }
00048 
00049 
00050 //DOM Level 2
00051 DOMDeepNodeListImpl::DOMDeepNodeListImpl(const DOMNode *rootNode,
00052                                        const XMLCh *namespaceURI,
00053                                        const XMLCh *localName)
00054     : fRootNode(rootNode)
00055     , fChanges(0)
00056     , fCurrentNode(0)
00057     , fCurrentIndexPlus1(0)
00058     , fMatchAllURI(false)
00059     , fMatchURIandTagname(true)
00060 {
00061     DOMDocumentImpl* doc = (DOMDocumentImpl *)castToNodeImpl(rootNode)->getOwnerDocument();
00062 
00063     fTagName = doc->getPooledString(localName);
00064     fMatchAll = XMLString::equals(fTagName, kAstr);
00065     fMatchAllURI = XMLString::equals(namespaceURI, kAstr);
00066     fNamespaceURI = doc->getPooledString(namespaceURI);
00067 }
00068 
00069 
00070 DOMDeepNodeListImpl::~DOMDeepNodeListImpl()
00071 {
00072 }
00073 
00074 XMLSize_t DOMDeepNodeListImpl::getLength() const
00075 {
00076     // Reset cache to beginning of list
00077     item(0);
00078 
00079     // Preload all matching elements. (Stops when we run out of subtree!)
00080     item(INT_MAX);
00081     return fCurrentIndexPlus1;
00082 }
00083 
00084 
00085 DOMNode *DOMDeepNodeListImpl::item(XMLSize_t index) const
00086 {
00087     return ((DOMDeepNodeListImpl*)this)->cacheItem(index);
00088 }
00089 
00090 // Start from the first child and count forward, 0-based. index>length-1
00091 // should return 0.
00092 //
00093 // Attempts to do only work actually requested, cache work already
00094 // done, and to flush that cache when the tree has changed.
00095 //
00096 // LIMITATION: ????? Unable to tell relevant tree-changes from
00097 // irrelevant ones.  Doing so in a really useful manner would seem
00098 // to involve a tree-walk in its own right, or maintaining our data
00099 // in a parallel tree.
00100 DOMNode *DOMDeepNodeListImpl::cacheItem(XMLSize_t index)
00101 {
00102     XMLSize_t currentIndexPlus1 = fCurrentIndexPlus1;
00103     DOMNode *currentNode = fCurrentNode;
00104 
00105     if (castToParentImpl(fRootNode)->changes() != fChanges)
00106     {
00107         // Tree changed. Do it all from scratch!
00108         currentIndexPlus1 = 0;
00109         currentNode = (DOMNode *)fRootNode;
00110         fChanges = castToParentImpl(fRootNode)->changes();
00111     }
00112     else if (currentIndexPlus1 > index+1)
00113     {
00114         // Interested in something before cached node.  Do it all from scratch!
00115         currentIndexPlus1 = 0;
00116         currentNode = (DOMNode *)fRootNode;
00117     }
00118     else if (index+1 == currentIndexPlus1)
00119     {
00120         // What luck!  User is interested in cached node.
00121         return currentNode;
00122     }
00123 
00124     DOMNode *nextNode = 0;
00125 
00126 // revisit - ???? How efficient is this loop? ????
00127 
00128     // Start at the place in the tree at which we're
00129     // currently pointing and count off nodes until we
00130     // reach the node of interest or the end of the tree.
00131     while (currentIndexPlus1 < index+1 && currentNode != 0)
00132     {
00133         nextNode = nextMatchingElementAfter(currentNode);
00134         if (nextNode == 0)
00135             break;
00136         currentNode = nextNode;
00137         currentIndexPlus1++;
00138     }
00139 
00140     fCurrentNode = currentNode;
00141     fCurrentIndexPlus1 = currentIndexPlus1;
00142 
00143     // If we found a node at the requested index, make that the current node
00144     if (nextNode != 0)
00145     {
00146         return currentNode;
00147     }
00148 
00149     // If we didn't find a node at the requested index, return 0
00150     return 0;
00151 }
00152 
00153 
00154 
00155 /* Iterative tree-walker. When you have a Parent link, there's often no
00156 need to resort to recursion. NOTE THAT only Element nodes are matched
00157 since we're specifically supporting getElementsByTagName().
00158 */
00159 DOMNode *DOMDeepNodeListImpl::nextMatchingElementAfter(DOMNode *current)
00160 {
00161     DOMNode *next;
00162     while (current != 0)
00163     {
00164         // Look down to first child.
00165         if (current->hasChildNodes())
00166         {
00167             current = current->getFirstChild();
00168         }
00169         // Look right to sibling (but not from root!)
00170         else
00171         {
00172             if (current != fRootNode && 0 != (next = current->getNextSibling()))
00173             {
00174                 current = next;
00175             }
00176             // Look up and right (but not past root!)
00177             else
00178             {
00179                 next = 0;
00180                 for (;
00181                      current != fRootNode; // Stop on return to starting point
00182                      current = current->getParentNode())
00183                 {
00184                     next = current->getNextSibling();
00185                     if (next != 0)
00186                         break;
00187                 }
00188                 current = next;
00189             }
00190         }
00191 
00192         // Have we found an Element with the right tagName?
00193         // ("*" matches anything.)
00194         if (current != 0 && current != fRootNode &&
00195             current->getNodeType() == DOMNode::ELEMENT_NODE) {
00196             DOMElement *currElement = (DOMElement *)current;
00197 
00198             if (!fMatchURIandTagname) {        //DOM Level 1
00199                 if (fMatchAll ||
00200                     XMLString::equals(currElement->getTagName(), fTagName))
00201                     return current;
00202             } else {        //DOM Level 2
00203                 if (!fMatchAllURI &&
00204                     !XMLString::equals(current->getNamespaceURI(), fNamespaceURI))
00205                     continue;
00206 
00207                 if (fMatchAll ||
00208                     XMLString::equals(current->getLocalName(), fTagName))
00209                     return current;
00210             }
00211         }
00212 
00213         // Otherwise continue walking the tree
00214     }
00215     // Fell out of tree-walk; no more instances found
00216     return 0;
00217 }
00218 
00219 XERCES_CPP_NAMESPACE_END