/* * 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. */ /* * $Id$ */ #include "DOMDeepNodeListImpl.hpp" #include "DOMElementImpl.hpp" #include "DOMDocumentImpl.hpp" #include "DOMCasts.hpp" #include "DOMNodeImpl.hpp" #include #include XERCES_CPP_NAMESPACE_BEGIN static const XMLCh kAstr[] = {chAsterisk, chNull}; DOMDeepNodeListImpl::DOMDeepNodeListImpl(const DOMNode *rootNode, const XMLCh *tagName) : fRootNode(rootNode) , fChanges(0) , fCurrentNode(0) , fCurrentIndexPlus1(0) , fNamespaceURI(0) , fMatchAllURI(false) , fMatchURIandTagname(false) { fTagName = ((DOMDocumentImpl *)(castToNodeImpl(rootNode)->getOwnerDocument()))->getPooledString(tagName); fMatchAll = XMLString::equals(fTagName, kAstr); } //DOM Level 2 DOMDeepNodeListImpl::DOMDeepNodeListImpl(const DOMNode *rootNode, const XMLCh *namespaceURI, const XMLCh *localName) : fRootNode(rootNode) , fChanges(0) , fCurrentNode(0) , fCurrentIndexPlus1(0) , fMatchAllURI(false) , fMatchURIandTagname(true) { DOMDocumentImpl* doc = (DOMDocumentImpl *)castToNodeImpl(rootNode)->getOwnerDocument(); fTagName = doc->getPooledString(localName); fMatchAll = XMLString::equals(fTagName, kAstr); fMatchAllURI = XMLString::equals(namespaceURI, kAstr); fNamespaceURI = doc->getPooledString(namespaceURI); } DOMDeepNodeListImpl::~DOMDeepNodeListImpl() { } XMLSize_t DOMDeepNodeListImpl::getLength() const { // Reset cache to beginning of list item(0); // Preload all matching elements. (Stops when we run out of subtree!) item(INT_MAX); return fCurrentIndexPlus1; } DOMNode *DOMDeepNodeListImpl::item(XMLSize_t index) const { return ((DOMDeepNodeListImpl*)this)->cacheItem(index); } // Start from the first child and count forward, 0-based. index>length-1 // should return 0. // // Attempts to do only work actually requested, cache work already // done, and to flush that cache when the tree has changed. // // LIMITATION: ????? Unable to tell relevant tree-changes from // irrelevant ones. Doing so in a really useful manner would seem // to involve a tree-walk in its own right, or maintaining our data // in a parallel tree. DOMNode *DOMDeepNodeListImpl::cacheItem(XMLSize_t index) { XMLSize_t currentIndexPlus1 = fCurrentIndexPlus1; DOMNode *currentNode = fCurrentNode; if (castToParentImpl(fRootNode)->changes() != fChanges) { // Tree changed. Do it all from scratch! currentIndexPlus1 = 0; currentNode = (DOMNode *)fRootNode; fChanges = castToParentImpl(fRootNode)->changes(); } else if (currentIndexPlus1 > index+1) { // Interested in something before cached node. Do it all from scratch! currentIndexPlus1 = 0; currentNode = (DOMNode *)fRootNode; } else if (index+1 == currentIndexPlus1) { // What luck! User is interested in cached node. return currentNode; } DOMNode *nextNode = 0; // revisit - ???? How efficient is this loop? ???? // Start at the place in the tree at which we're // currently pointing and count off nodes until we // reach the node of interest or the end of the tree. while (currentIndexPlus1 < index+1 && currentNode != 0) { nextNode = nextMatchingElementAfter(currentNode); if (nextNode == 0) break; currentNode = nextNode; currentIndexPlus1++; } fCurrentNode = currentNode; fCurrentIndexPlus1 = currentIndexPlus1; // If we found a node at the requested index, make that the current node if (nextNode != 0) { return currentNode; } // If we didn't find a node at the requested index, return 0 return 0; } /* 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(). */ DOMNode *DOMDeepNodeListImpl::nextMatchingElementAfter(DOMNode *current) { DOMNode *next; while (current != 0) { // Look down to first child. if (current->hasChildNodes()) { current = current->getFirstChild(); } // Look right to sibling (but not from root!) else { if (current != fRootNode && 0 != (next = current->getNextSibling())) { current = next; } // Look up and right (but not past root!) else { next = 0; for (; current != fRootNode; // Stop on return to starting point current = current->getParentNode()) { next = current->getNextSibling(); if (next != 0) break; } current = next; } } // Have we found an Element with the right tagName? // ("*" matches anything.) if (current != 0 && current != fRootNode && current->getNodeType() == DOMNode::ELEMENT_NODE) { DOMElement *currElement = (DOMElement *)current; if (!fMatchURIandTagname) { //DOM Level 1 if (fMatchAll || XMLString::equals(currElement->getTagName(), fTagName)) return current; } else { //DOM Level 2 if (!fMatchAllURI && !XMLString::equals(current->getNamespaceURI(), fNamespaceURI)) continue; if (fMatchAll || XMLString::equals(current->getLocalName(), fTagName)) return current; } } // Otherwise continue walking the tree } // Fell out of tree-walk; no more instances found return 0; } XERCES_CPP_NAMESPACE_END