/* * 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 "DOMRangeImpl.hpp" #include "DOMDocumentImpl.hpp" #include "DOMDocumentFragmentImpl.hpp" #include "DOMCommentImpl.hpp" #include "DOMProcessingInstructionImpl.hpp" #include "DOMCasts.hpp" #include #include #include #include #include #include #include XERCES_CPP_NAMESPACE_BEGIN //--------------------- // C'tor and D'tor //--------------------- DOMRangeImpl::DOMRangeImpl(DOMDocument* doc, MemoryManager* const manager) : fStartContainer(doc), fStartOffset(0), fEndContainer(doc), fEndOffset(0), fCollapsed(true), fDocument(doc), fDetached(false), fRemoveChild(0), fMemoryManager(manager) { } DOMRangeImpl::DOMRangeImpl(const DOMRangeImpl& other) : DOMRange(other), fStartContainer(other.fStartContainer), fStartOffset(other.fStartOffset), fEndContainer(other.fEndContainer), fEndOffset(other.fEndOffset), fCollapsed(other.fCollapsed), fDocument(other.fDocument), fDetached(other.fDetached), fRemoveChild(other.fRemoveChild), fMemoryManager(other.fMemoryManager) { } DOMRangeImpl::~DOMRangeImpl() { } //------------------------------- // Public getter functions //------------------------------- DOMNode* DOMRangeImpl::getStartContainer() const { if (fDetached) { throw DOMException( DOMException::INVALID_STATE_ERR, 0, fMemoryManager); } return fStartContainer; } XMLSize_t DOMRangeImpl::getStartOffset() const { if (fDetached) { throw DOMException( DOMException::INVALID_STATE_ERR, 0, fMemoryManager); } return fStartOffset; } DOMNode* DOMRangeImpl::getEndContainer() const { if (fDetached) { throw DOMException( DOMException::INVALID_STATE_ERR, 0, fMemoryManager); } return fEndContainer; } XMLSize_t DOMRangeImpl::getEndOffset() const { if (fDetached) { throw DOMException( DOMException::INVALID_STATE_ERR, 0, fMemoryManager); } return fEndOffset; } bool DOMRangeImpl::getCollapsed() const { if (fDetached) { throw DOMException( DOMException::INVALID_STATE_ERR, 0, fMemoryManager); } return ((fStartContainer == fEndContainer) && (fStartOffset == fEndOffset)); } //------------------------------- // Public setter functions //------------------------------- void DOMRangeImpl::setStartContainer(const DOMNode* node) { if (fDetached) { throw DOMException( DOMException::INVALID_STATE_ERR, 0, fMemoryManager); } fStartContainer = (DOMNode*) node; } void DOMRangeImpl::setStartOffset(XMLSize_t offset) { if (fDetached) { throw DOMException( DOMException::INVALID_STATE_ERR, 0, fMemoryManager); } fStartOffset = offset; } void DOMRangeImpl::setEndContainer(const DOMNode* node) { if (fDetached) { throw DOMException( DOMException::INVALID_STATE_ERR, 0, fMemoryManager); } fEndContainer = (DOMNode*) node; } void DOMRangeImpl::setEndOffset(XMLSize_t offset) { if (fDetached) { throw DOMException( DOMException::INVALID_STATE_ERR, 0, fMemoryManager); } fEndOffset = offset; } void DOMRangeImpl::setStart(const DOMNode* refNode, XMLSize_t offset) { validateNode(refNode); checkIndex(refNode, offset); // error if not the same owner document if (fDocument != refNode->getOwnerDocument()) { if ( refNode != fDocument ) { collapse(true); //collapse the range positions to start fCollapsed = true; throw DOMException( DOMException::WRONG_DOCUMENT_ERR, 0, fMemoryManager); } } fStartContainer = (DOMNode*) refNode; fStartOffset = offset; // they may be of same document, but not same root container // collapse if not the same root container if (!commonAncestorOf(refNode, fEndContainer)) collapse(true); //compare the start and end boundary point //collapse if start point is after the end point if(compareBoundaryPoints(DOMRange::END_TO_START, this) == 1) collapse(true); //collapse the range positions to start else fCollapsed = false; } void DOMRangeImpl::setEnd(const DOMNode* refNode, XMLSize_t offset) { validateNode(refNode); checkIndex(refNode, offset); // error if not the same owner document if (fDocument != refNode->getOwnerDocument()) { if ( refNode != fDocument ) { collapse(false); //collapse the range positions to end fCollapsed = true; throw DOMException( DOMException::WRONG_DOCUMENT_ERR, 0, fMemoryManager); } } fEndContainer = (DOMNode*) refNode; fEndOffset = offset; // they may be of same document, but not same root container // collapse if not the same root container if (!commonAncestorOf(refNode, fStartContainer)) collapse(false); //compare the start and end boundary point //collapse if start point is after the end point if(compareBoundaryPoints(DOMRange::END_TO_START, this) == 1) collapse(false); //collapse the range positions to end else fCollapsed = false; } void DOMRangeImpl::setStartBefore(const DOMNode* refNode) { if( fDetached) { throw DOMException( DOMException::INVALID_STATE_ERR, 0, fMemoryManager); } if ( !hasLegalRootContainer(refNode) || !isLegalContainedNode(refNode)) { throw DOMRangeException( DOMRangeException::INVALID_NODE_TYPE_ERR, 0, fMemoryManager); } // error if not the same owner document if (fDocument != refNode->getOwnerDocument()) { if ( refNode != fDocument ) { collapse(true); //collapse the range positions to start fCollapsed = true; throw DOMException( DOMException::WRONG_DOCUMENT_ERR, 0, fMemoryManager); } } fStartContainer = refNode->getParentNode(); XMLSize_t i = 0; for (DOMNode* n = (DOMNode*) refNode; n!=0; n = n->getPreviousSibling()) { i++; } if (i == 0) fStartOffset = 0; else fStartOffset = i-1; // they may be of same document, but not same root container // collapse if not the same root container if (!commonAncestorOf(refNode, fEndContainer)) collapse(true); //compare the start and end boundary point //collapse if start point is after the end point if(compareBoundaryPoints(DOMRange::END_TO_START, this) == 1) collapse(true); //collapse the range positions to start else fCollapsed = false; } void DOMRangeImpl::setStartAfter(const DOMNode* refNode) { if( fDetached) { throw DOMException( DOMException::INVALID_STATE_ERR, 0, fMemoryManager); } if ( !hasLegalRootContainer(refNode) || !isLegalContainedNode(refNode)) { throw DOMRangeException( DOMRangeException::INVALID_NODE_TYPE_ERR, 0, fMemoryManager); } // error if not the same owner document if (fDocument != refNode->getOwnerDocument()) { if ( refNode != fDocument ) { collapse(true); //collapse the range positions to start fCollapsed = true; throw DOMException( DOMException::WRONG_DOCUMENT_ERR, 0, fMemoryManager); } } fStartContainer = refNode->getParentNode(); XMLSize_t i = 0; for (DOMNode* n = (DOMNode*) refNode; n!=0; n = n->getPreviousSibling()) { i++; } fStartOffset = i; // they may be of same document, but not same root container // collapse if not the same root container if (!commonAncestorOf(refNode, fEndContainer)) collapse(true); //compare the start and end boundary point //collapse if start point is after the end point if(compareBoundaryPoints(DOMRange::END_TO_START, this) == 1) collapse(true); //collapse the range positions to start else fCollapsed = false; } void DOMRangeImpl::setEndBefore(const DOMNode* refNode) { if( fDetached) { throw DOMException( DOMException::INVALID_STATE_ERR, 0, fMemoryManager); } if ( !hasLegalRootContainer(refNode) || !isLegalContainedNode(refNode)) { throw DOMRangeException( DOMRangeException::INVALID_NODE_TYPE_ERR, 0, fMemoryManager); } // error if not the same owner document if (fDocument != refNode->getOwnerDocument()) { if ( refNode != fDocument ) { collapse(false); //collapse the range positions to end fCollapsed = true; throw DOMException( DOMException::WRONG_DOCUMENT_ERR, 0, fMemoryManager); } } fEndContainer = refNode->getParentNode(); XMLSize_t i = 0; for (DOMNode* n = (DOMNode*) refNode; n!=0; n = n->getPreviousSibling(), i++) ; if (i< 1) fEndOffset = 0; else fEndOffset = i-1; // they may be of same document, but not same root container // collapse if not the same root container if (!commonAncestorOf(refNode, fStartContainer)) collapse(false); //compare the start and end boundary point //collapse if start point is after the end point if(compareBoundaryPoints(DOMRange::END_TO_START, this) == 1) collapse(false); //collapse the range positions to end else fCollapsed = false; } void DOMRangeImpl::setEndAfter(const DOMNode* refNode) { if( fDetached) { throw DOMException( DOMException::INVALID_STATE_ERR, 0, fMemoryManager); } if ( !hasLegalRootContainer(refNode) || !isLegalContainedNode(refNode)) { throw DOMRangeException( DOMRangeException::INVALID_NODE_TYPE_ERR, 0, fMemoryManager); } // error if not the same owner document if (fDocument != refNode->getOwnerDocument()) { if ( refNode != fDocument ) { collapse(false); //collapse the range positions to end fCollapsed = true; throw DOMException( DOMException::WRONG_DOCUMENT_ERR, 0, fMemoryManager); } } fEndContainer = refNode->getParentNode(); XMLSize_t i = 0; for (DOMNode* n = (DOMNode*) refNode; n!=0; n = n->getPreviousSibling(), i++) ; if (i ==0) fEndOffset = 0; else fEndOffset = i; // they may be of same document, but not same root container // collapse if not the same root container if (!commonAncestorOf(refNode, fStartContainer)) collapse(false); //compare the start and end boundary point //collapse if start point is after the end point if(compareBoundaryPoints(DOMRange::END_TO_START, this) == 1) collapse(false); //collapse the range positions to end else fCollapsed = false; } //------------------------------- // Public Misc. functions //------------------------------- void DOMRangeImpl::detach() { if( fDetached) { throw DOMException( DOMException::INVALID_STATE_ERR, 0, fMemoryManager); } ((DOMDocumentImpl *)fDocument)->removeRange(this); fDetached = true; //0ify nodes fStartContainer = 0; fStartOffset = 0; fEndContainer = 0; fEndOffset = 0; fCollapsed = true; fRemoveChild = 0; } void DOMRangeImpl::collapse(bool toStart) { if( fDetached) { throw DOMException( DOMException::INVALID_STATE_ERR, 0, fMemoryManager); } if (toStart) { fEndContainer = fStartContainer; fEndOffset = fStartOffset; } else { fStartContainer = fEndContainer; fStartOffset = fEndOffset; } fCollapsed = true; } void DOMRangeImpl::selectNode(const DOMNode* refNode) { validateNode(refNode); if ( !isLegalContainedNode(refNode)) { throw DOMRangeException( DOMRangeException::INVALID_NODE_TYPE_ERR, 0, fMemoryManager); } //First check for the text type node short type = refNode->getNodeType(); if((type == DOMNode::TEXT_NODE || type == DOMNode::CDATA_SECTION_NODE || type == DOMNode::COMMENT_NODE || type == DOMNode::PROCESSING_INSTRUCTION_NODE)) { //The node itself is the container. fStartContainer = (DOMNode*) refNode; fEndContainer = (DOMNode*) refNode; //Select all the contents of the node fStartOffset = 0; if (type == DOMNode::PROCESSING_INSTRUCTION_NODE) fEndOffset = XMLString::stringLen(((DOMProcessingInstruction*)refNode)->getData()); else fEndOffset = ((DOMText *)refNode)->getLength(); return; } DOMNode* parent = refNode->getParentNode(); if (parent != 0 ) // REVIST: what to do if it IS 0? { fStartContainer = parent; fEndContainer = parent; XMLSize_t i = 0; for (DOMNode* n = parent->getFirstChild(); n!=0 && n!=refNode; n = n->getNextSibling()) { i++; } fStartOffset = i; fEndOffset = fStartOffset+1; } } void DOMRangeImpl::selectNodeContents(const DOMNode* node) { validateNode(node); fStartContainer = (DOMNode*) node; fEndContainer = (DOMNode*) node; fStartOffset = 0; short type = node->getNodeType(); if((type == DOMNode::TEXT_NODE || type == DOMNode::CDATA_SECTION_NODE || type == DOMNode::COMMENT_NODE)) { fEndOffset = ((DOMText *)node)->getLength(); return; } if (type == DOMNode::PROCESSING_INSTRUCTION_NODE) { fEndOffset = XMLString::stringLen(((DOMProcessingInstruction*)node)->getData()); return; } DOMNode* first = node->getFirstChild(); if (first == 0) { fEndOffset = 0; return; } XMLSize_t i = 0; for (DOMNode* n = first; n!=0; n = n->getNextSibling()) { i++; } fEndOffset = i; } void DOMRangeImpl::surroundContents(DOMNode* newParent) { if (newParent==0) return; //check for elimination criteria if( fDetached) { throw DOMException( DOMException::INVALID_STATE_ERR, 0, fMemoryManager); } if (newParent->getOwnerDocument() !=fDocument) { throw DOMException( DOMException::WRONG_DOCUMENT_ERR, 0, fMemoryManager); } int type = newParent->getNodeType(); if ( !isLegalContainedNode(newParent) || type == DOMNode::DOCUMENT_TYPE_NODE) { throw DOMRangeException( DOMRangeException::INVALID_NODE_TYPE_ERR, 0, fMemoryManager); } DOMNode* realStart = fStartContainer; DOMNode* realEnd = fEndContainer; type = fStartContainer->getNodeType(); if((type == DOMNode::TEXT_NODE || type == DOMNode::CDATA_SECTION_NODE || type == DOMNode::COMMENT_NODE || type == DOMNode::PROCESSING_INSTRUCTION_NODE)) { realStart = fStartContainer->getParentNode(); } type = fEndContainer->getNodeType(); if((type == DOMNode::TEXT_NODE || type == DOMNode::CDATA_SECTION_NODE || type == DOMNode::COMMENT_NODE || type == DOMNode::PROCESSING_INSTRUCTION_NODE)) { realEnd = fEndContainer->getParentNode(); } if (realStart != realEnd) { throw DOMRangeException( DOMRangeException::BAD_BOUNDARYPOINTS_ERR, 0, fMemoryManager); } DOMDocumentFragment* frag = (DOMDocumentFragment*) extractContents(); insertNode(newParent); newParent->appendChild(frag); selectNode(newParent); } short DOMRangeImpl::compareBoundaryPoints(DOMRange::CompareHow how, const DOMRange* srcRange) const { if (fDocument != ((DOMRangeImpl*)srcRange)->fDocument) { throw DOMException( DOMException::WRONG_DOCUMENT_ERR, 0, fMemoryManager); } if( fDetached) { throw DOMException( DOMException::INVALID_STATE_ERR, 0, fMemoryManager); } DOMNode* pointA; DOMNode* pointB; XMLSize_t offsetA, offsetB; switch (how) { case (DOMRange::START_TO_START) : pointB = srcRange->getStartContainer(); pointA = fStartContainer; offsetB = srcRange->getStartOffset(); offsetA = fStartOffset; break; case (DOMRange::START_TO_END) : pointB = srcRange->getStartContainer(); pointA = fEndContainer; offsetB = srcRange->getStartOffset(); offsetA = fEndOffset; break; case (DOMRange::END_TO_START) : pointB = srcRange->getEndContainer(); pointA = fStartContainer; offsetB = srcRange->getEndOffset(); offsetA = fStartOffset; break; case (DOMRange::END_TO_END) : pointB = srcRange->getEndContainer(); pointA = fEndContainer; offsetB = srcRange->getEndOffset(); offsetA = fEndOffset; break; default: throw DOMException( DOMException::INVALID_STATE_ERR, 0, fMemoryManager); } // case 1: same container if (pointA == pointB) { if (offsetA < offsetB) return -1; //A before B if (offsetA == offsetB) return 0; //A equal to B return 1; // A after B } // case 2: Child C of container A is ancestor of B for (DOMNode* node = pointA->getFirstChild(); node != 0; node=node->getNextSibling()) { if (isAncestorOf(node, pointB)) { XMLSize_t index = indexOf(node, pointA); if (offsetA <= index) return -1; return 1; } } // case 3: Child C of container B is ancestor of A for (DOMNode* nd = pointB->getFirstChild(); nd != 0; nd=nd->getNextSibling()) { if (isAncestorOf(nd, pointA)) { XMLSize_t index = indexOf(nd, pointB); if (index < offsetB ) return -1; return 1; //B strictly before A } } // case 4: preorder traversal of context tree. // Instead of literally walking the context tree in pre-order, // we use relative node depth walking which is usually faster int depthDiff = 0; DOMNode* n = 0; for ( n = pointB; n != 0; n = n->getParentNode() ) depthDiff++; for ( n = pointA; n != 0; n = n->getParentNode() ) depthDiff--; while (depthDiff > 0) { pointB = pointB->getParentNode(); depthDiff--; } while (depthDiff < 0) { pointA = pointA->getParentNode(); depthDiff++; } for (DOMNode* pB = pointB->getParentNode(), *pA = pointA->getParentNode(); pB != pA; pB = pB->getParentNode(), pA = pA->getParentNode() ) { pointB = pB; pointA = pA; } for ( n = pointB->getNextSibling(); n != 0; n = n->getNextSibling() ) { if (n == pointA) { return 1; } } return -1; } void DOMRangeImpl:: deleteContents() { traverseContents(DELETE_CONTENTS); } DOMDocumentFragment* DOMRangeImpl::extractContents() { checkReadOnly(fStartContainer, fEndContainer, fStartOffset, fEndOffset); return traverseContents(EXTRACT_CONTENTS); } DOMDocumentFragment* DOMRangeImpl::cloneContents() const { // cast off const. return ((DOMRangeImpl *)this)->traverseContents(CLONE_CONTENTS); } void DOMRangeImpl::insertNode(DOMNode* newNode) { if (newNode == 0) return; //don't have to do anything if( fDetached) { throw DOMException( DOMException::INVALID_STATE_ERR, 0, fMemoryManager); } int type = newNode->getNodeType(); if (type == DOMNode::ATTRIBUTE_NODE || type == DOMNode::ENTITY_NODE || type == DOMNode::NOTATION_NODE || type == DOMNode::DOCUMENT_NODE) { throw DOMRangeException( DOMRangeException::INVALID_NODE_TYPE_ERR, 0, fMemoryManager); } // Prevent cycles in the tree. //isKidOK() is not checked here as its taken care by insertBefore() function if (isAncestorOf( newNode, fStartContainer)) { throw DOMException( DOMException::HIERARCHY_REQUEST_ERR, 0, fMemoryManager); } for (DOMNode* aNode = fStartContainer; aNode!=0; aNode = aNode->getParentNode()) { if (castToNodeImpl(newNode)->isReadOnly()) { throw DOMException( DOMException::NO_MODIFICATION_ALLOWED_ERR, 0, fMemoryManager); } } if (fDocument != newNode->getOwnerDocument()) { throw DOMException( DOMException::WRONG_DOCUMENT_ERR, 0, fMemoryManager); } DOMNode* parent; DOMNode* next; type = fStartContainer->getNodeType(); if((type == DOMNode::TEXT_NODE || type == DOMNode::CDATA_SECTION_NODE || type == DOMNode::COMMENT_NODE || type == DOMNode::PROCESSING_INSTRUCTION_NODE)) { //set 'parent' and 'next' here parent = fStartContainer->getParentNode(); //split the text nodes if (fStartOffset > 0) { if (type == DOMNode::COMMENT_NODE) ((DOMCommentImpl*)fStartContainer)->splitText(fStartOffset); else if (type == DOMNode::PROCESSING_INSTRUCTION_NODE) ((DOMProcessingInstructionImpl*)fStartContainer)->splitText(fStartOffset); else ((DOMText*)fStartContainer)->splitText(fStartOffset); } //update the new start information later. After inserting the first newNode if (fStartOffset == 0) next = fStartContainer; else next = fStartContainer->getNextSibling(); } // end of text handling else { parent = fStartContainer; next = fStartContainer->getFirstChild(); for(XMLSize_t i = 0; (i < fStartOffset) && (next != 0); i++) { next=next->getNextSibling(); } } if (parent != 0) { if (next != 0) parent->insertBefore(newNode, next); else parent->appendChild(newNode); } } DOMRange* DOMRangeImpl::cloneRange() const { if( fDetached) { throw DOMException( DOMException::INVALID_STATE_ERR, 0, fMemoryManager); } DOMRange* range = fDocument->createRange(); range->setStart(fStartContainer, fStartOffset); range->setEnd(fEndContainer, fEndOffset); return range; } const XMLCh* DOMRangeImpl::toString() const { if( fDetached) { throw DOMException( DOMException::INVALID_STATE_ERR, 0, fMemoryManager); } if ((fStartContainer == fEndContainer) && (fEndOffset == fStartOffset)) return XMLUni::fgZeroLenString; DOMNode* node = fStartContainer; DOMNode* stopNode = fEndContainer; XMLBuffer retStringBuf(1023, ((DOMDocumentImpl *)fDocument)->getMemoryManager()); short type = fStartContainer->getNodeType(); if((type == DOMNode::TEXT_NODE || type == DOMNode::CDATA_SECTION_NODE || type == DOMNode::COMMENT_NODE || type == DOMNode::PROCESSING_INSTRUCTION_NODE)) { if (fStartContainer == fEndContainer) { XMLCh* tempString; XMLCh temp[4000]; if ((fEndOffset-fStartOffset) >= 3999) tempString = (XMLCh*) fMemoryManager->allocate ( (fEndOffset - fStartOffset + 1) * sizeof(XMLCh) );//new XMLCh[fEndOffset-fStartOffset+1]; else tempString = temp; XMLString::subString(tempString, fStartContainer->getNodeValue(), fStartOffset, fEndOffset, ((DOMDocumentImpl *)fDocument)->getMemoryManager()); const XMLCh* retString = ((DOMDocumentImpl *)fDocument)->getPooledString(tempString); if ((fEndOffset-fStartOffset) >= 3999) fMemoryManager->deallocate(tempString);//delete[] tempString; return retString; } else { XMLSize_t length = XMLString::stringLen(fStartContainer->getNodeValue()); if (length != fStartOffset) { XMLCh* tempString; XMLCh temp[4000]; if ((length - fStartOffset) >= 3999) tempString = (XMLCh*) fMemoryManager->allocate ( (length - fStartOffset + 1) * sizeof(XMLCh) );//new XMLCh[length - fStartOffset+1]; else tempString = temp; XMLString::subString(tempString, fStartContainer->getNodeValue(), fStartOffset, length, ((DOMDocumentImpl *)fDocument)->getMemoryManager()); retStringBuf.append(tempString); if ((length - fStartOffset) >= 3999) fMemoryManager->deallocate(tempString);//delete[] tempString; } node = nextNode(node, true); } }else { //fStartContainer is not a TextNode node=node->getFirstChild(); if (fStartOffset>0) { //find a first node within a range, specified by fStartOffset XMLSize_t counter = 0; while (countergetNextSibling(); counter++; } } if (node == 0) { node = nextNode(fStartContainer,false); } } type = fEndContainer->getNodeType(); if((type != DOMNode::TEXT_NODE && type != DOMNode::CDATA_SECTION_NODE && type != DOMNode::COMMENT_NODE && type != DOMNode::PROCESSING_INSTRUCTION_NODE)) { int i=(int)fEndOffset; stopNode = fEndContainer->getFirstChild(); while( i>0 && stopNode!=0 ){ --i; stopNode = stopNode->getNextSibling(); } if ( stopNode == 0 ) stopNode = nextNode( fEndContainer, false ); } while (node != stopNode) { //look into all kids of the Range if (node == 0) break; type = node->getNodeType(); if((type == DOMNode::TEXT_NODE || type == DOMNode::CDATA_SECTION_NODE || type == DOMNode::COMMENT_NODE || type == DOMNode::PROCESSING_INSTRUCTION_NODE)) { retStringBuf.append(node->getNodeValue()); } node = nextNode(node, true); } type = fEndContainer->getNodeType(); if((type == DOMNode::TEXT_NODE || type == DOMNode::CDATA_SECTION_NODE || type == DOMNode::COMMENT_NODE || type == DOMNode::PROCESSING_INSTRUCTION_NODE)) { if (fEndOffset != 0) { XMLCh* tempString; XMLCh temp[4000]; if (fEndOffset >= 3999) tempString = (XMLCh*) fMemoryManager->allocate ( (fEndOffset+1) * sizeof(XMLCh) );//new XMLCh[fEndOffset+1]; else tempString = temp; XMLString::subString(tempString, fEndContainer->getNodeValue(), 0, fEndOffset, ((DOMDocumentImpl *)fDocument)->getMemoryManager()); retStringBuf.append(tempString); if (fEndOffset >= 3999) fMemoryManager->deallocate(tempString);//delete[] tempString; } } return ((DOMDocumentImpl *)fDocument)->getPooledString(retStringBuf.getRawBuffer()); } DOMDocument* DOMRangeImpl::getDocument() { return fDocument; } const DOMNode* DOMRangeImpl::getCommonAncestorContainer() const { return commonAncestorOf(fStartContainer, fEndContainer); } void DOMRangeImpl::release() { detach(); // for performance reason, do not recycle pointer // chance that this is allocated again and again is not usual } //--------------------- //private functions //--------------------- bool DOMRangeImpl::isValidAncestorType(const DOMNode* node) const { for (DOMNode* aNode = (DOMNode*) node; aNode!=0; aNode = aNode->getParentNode()) { short type = aNode->getNodeType(); if ( type == DOMNode::ENTITY_NODE || type == DOMNode::NOTATION_NODE || type == DOMNode::DOCUMENT_TYPE_NODE) return false; } return true; } bool DOMRangeImpl::isAncestorOf(const DOMNode* a, const DOMNode* b) { for (DOMNode* node = (DOMNode*) b; node != 0; node=node->getParentNode()) { if (node == a) return true; } return false; } bool DOMRangeImpl::hasLegalRootContainer(const DOMNode* node) const { if ( node==0 ) return false; DOMNode* rootContainer = (DOMNode*)node; for (; rootContainer->getParentNode()!=0; rootContainer = rootContainer->getParentNode()) ; switch( rootContainer->getNodeType() ) { case DOMNode::ATTRIBUTE_NODE: case DOMNode::DOCUMENT_NODE: case DOMNode::DOCUMENT_FRAGMENT_NODE: return true; default: return false; } } bool DOMRangeImpl::isLegalContainedNode(const DOMNode* node ) const { if ( node==0 ) return false; switch( node->getNodeType() ) { case DOMNode::DOCUMENT_NODE: case DOMNode::DOCUMENT_FRAGMENT_NODE: case DOMNode::ATTRIBUTE_NODE: case DOMNode::ENTITY_NODE: case DOMNode::NOTATION_NODE: return false; default: return true; } } XMLSize_t DOMRangeImpl::indexOf(const DOMNode* child, const DOMNode* parent) const { XMLSize_t i = 0; if (child->getParentNode() != parent) return (XMLSize_t)-1; for(DOMNode* node = child->getPreviousSibling(); node!= 0; node=node->getPreviousSibling()) { i++; } return i; } void DOMRangeImpl::validateNode(const DOMNode* node) const { if( fDetached) { throw DOMException( DOMException::INVALID_STATE_ERR, 0, fMemoryManager); } if ( !isValidAncestorType(node)) { throw DOMRangeException(DOMRangeException::INVALID_NODE_TYPE_ERR, 0, fMemoryManager); } } const DOMNode* DOMRangeImpl::commonAncestorOf(const DOMNode* pointA, const DOMNode* pointB) const { if (fDetached) throw DOMException(DOMException::INVALID_STATE_ERR, 0, fMemoryManager); //if the containers are same then it itself is its common ancestor. if (pointA == pointB) return pointA; typedef RefVectorOf VectorNodes; VectorNodes startV(1, false, ((DOMDocumentImpl *)fDocument)->getMemoryManager()); DOMNode* node; for (node=(DOMNode*)pointA; node != 0; node=node->getParentNode()) { startV.addElement(node); } VectorNodes endV(1, false, ((DOMDocumentImpl *)fDocument)->getMemoryManager()); for (node=(DOMNode*)pointB; node != 0; node=node->getParentNode()) { endV.addElement(node); } XMLSize_t s = startV.size(); XMLSize_t e = endV.size(); DOMNode* commonAncestor = 0; while (s>0 && e>0) { if (startV.elementAt(s-1) == endV.elementAt(e-1)) { commonAncestor = startV.elementAt(s-1); } else break; --s; --e; } return commonAncestor; } void DOMRangeImpl::checkIndex(const DOMNode* node, XMLSize_t offset) const { short type = node->getNodeType(); if((type == DOMNode::TEXT_NODE || type == DOMNode::CDATA_SECTION_NODE || type == DOMNode::COMMENT_NODE || type == DOMNode::PROCESSING_INSTRUCTION_NODE)) { if (offset > XMLString::stringLen(node->getNodeValue())) throw DOMException( DOMException::INDEX_SIZE_ERR, 0, fMemoryManager ); else return; } DOMNode* child = node->getFirstChild(); XMLSize_t i = 0; for (; child != 0; i++) { child = child->getNextSibling(); } if (i < offset) { throw DOMException( DOMException::INDEX_SIZE_ERR, 0, fMemoryManager ); } } DOMNode* DOMRangeImpl::nextNode(const DOMNode* node, bool visitChildren) const { if (node == 0) return 0; DOMNode* result; if (visitChildren) { result = node->getFirstChild(); if (result != 0) { return result; } } // if hasSibling, return sibling result = node->getNextSibling(); if (result != 0) { return result; } // return parent's 1st sibling. DOMNode* parent = node->getParentNode(); while ( (parent != 0) && (parent != fDocument) ) { result = parent->getNextSibling(); if (result != 0) { return result; } else { parent = parent->getParentNode(); } } // end of list, return 0 return 0; } /** This is the master routine invoked to visit the nodes * selected by this range. For each such node, different * actions are taken depending on the value of the TraversalType argument. */ DOMDocumentFragment* DOMRangeImpl::traverseContents(TraversalType how) { if (fDetached) throw DOMException(DOMException::INVALID_STATE_ERR, 0, fMemoryManager); if (fStartContainer == 0 || fEndContainer == 0) { return 0; // REVIST: Throw exception? } /* Traversal is accomplished by first determining the relationship between the endpoints of the range. For each of four significant relationships, we will delegate the traversal call to a method that can make appropriate assumptions. */ // case 1: same container if ( fStartContainer == fEndContainer ) return traverseSameContainer( how ); // case 2: Child C of start container is ancestor of end container // This can be quickly tested by walking the parent chain of // end container int endContainerDepth = 0; for ( DOMNode* c = fEndContainer, *p = c->getParentNode(); p != 0; c = p, p = p->getParentNode()) { if (p == fStartContainer) return traverseCommonStartContainer( c, how ); ++endContainerDepth; } // case 3: Child C of end container is ancestor of start container // This can be quickly tested by walking the parent chain of A int startContainerDepth = 0; for ( DOMNode* c2 = fStartContainer, *p2 = c2->getParentNode(); p2 != 0; c2 = p2, p2 = p2->getParentNode()) { if (p2 == fEndContainer) return traverseCommonEndContainer( c2, how ); ++startContainerDepth; } // case 4: There is a common ancestor container. Find the // ancestor siblings that are children of that container. int depthDiff = startContainerDepth - endContainerDepth; DOMNode* startNode = fStartContainer; while (depthDiff > 0) { startNode = startNode->getParentNode(); depthDiff--; } DOMNode* endNode = fEndContainer; while (depthDiff < 0) { endNode = endNode->getParentNode(); depthDiff++; } // ascend the ancestor hierarchy until we have a common parent. for( DOMNode* sp = startNode->getParentNode(), *ep = endNode->getParentNode(); sp!=ep; sp = sp->getParentNode(), ep = ep->getParentNode() ) { startNode = sp; endNode = ep; } return traverseCommonAncestors( startNode, endNode, how ); } /** * Visits the nodes selected by this range when we know * a-priori that the start and end containers are the same. * */ DOMDocumentFragment* DOMRangeImpl::traverseSameContainer( int how ) { DOMDocumentFragment* frag = 0; if ( how!=DELETE_CONTENTS) frag = fDocument->createDocumentFragment(); // If selection is empty, just return the fragment if ( fStartOffset==fEndOffset ) return frag; DOMNode* cloneCurrent = 0; // Text node needs special case handling short type = fStartContainer->getNodeType(); if((type == DOMNode::TEXT_NODE || type == DOMNode::CDATA_SECTION_NODE || type == DOMNode::COMMENT_NODE || type == DOMNode::PROCESSING_INSTRUCTION_NODE)) { cloneCurrent = fStartContainer->cloneNode(false); if (fEndOffset == fStartOffset) { cloneCurrent->setNodeValue(XMLUni::fgZeroLenString); } else { XMLCh* tempString; XMLCh temp[4000]; if (fEndOffset >= 3999) tempString = (XMLCh*) fMemoryManager->allocate ( (fEndOffset+1) * sizeof(XMLCh) );//new XMLCh[fEndOffset+1]; else tempString = temp; XMLString::subString(tempString, cloneCurrent->getNodeValue(), fStartOffset, fEndOffset, ((DOMDocumentImpl *)fDocument)->getMemoryManager()); cloneCurrent->setNodeValue(((DOMDocumentImpl *)fDocument)->getPooledString(tempString)); if (fEndOffset >= 3999) fMemoryManager->deallocate(tempString);//delete[] tempString; } // set the original text node to its new value if ( how != CLONE_CONTENTS ) { if(type == DOMNode::PROCESSING_INSTRUCTION_NODE) { ((DOMProcessingInstructionImpl*)fStartContainer)->deleteData(fStartOffset, fEndOffset-fStartOffset); } else ((DOMCharacterData*)fStartContainer)->deleteData(fStartOffset, fEndOffset-fStartOffset); } if ( how != DELETE_CONTENTS) frag->appendChild(cloneCurrent); } else { // Copy nodes between the start/end offsets. DOMNode* n = getSelectedNode( fStartContainer, (int)fStartOffset ); int cnt = (int)fEndOffset - (int)fStartOffset; while( cnt > 0 && n) { DOMNode* sibling = n->getNextSibling(); DOMNode* xferNode = traverseFullySelected( n, how ); if ( frag!=0 ) frag->appendChild( xferNode ); --cnt; n = sibling; } } // Nothing is partially selected, so collapse to start point if ( how != CLONE_CONTENTS ) collapse(true); return frag; } /** * Visits the nodes selected by this range when we know * a-priori that the start and end containers are not the * same, but the start container is an ancestor of the end container * */ DOMDocumentFragment* DOMRangeImpl::traverseCommonStartContainer( DOMNode*endAncestor, int how ) { DOMDocumentFragment* frag = 0; if ( how!=DELETE_CONTENTS) frag = fDocument->createDocumentFragment(); DOMNode*n = traverseRightBoundary( endAncestor, how ); if ( frag!=0 ) frag->appendChild( n ); XMLSize_t endIdx = indexOf( endAncestor, fStartContainer ); if ( endIdx <= fStartOffset ) { // Collapse to just before the endAncestor, which // is partially selected. if ( how != CLONE_CONTENTS ) { setEndBefore( endAncestor ); collapse( false ); } return frag; } n = endAncestor->getPreviousSibling(); int cnt = (int)endIdx - (int)fStartOffset; while( cnt > 0 ) { DOMNode* sibling = n->getPreviousSibling(); DOMNode* xferNode = traverseFullySelected( n, how ); if ( frag!=0 ) frag->insertBefore( xferNode, frag->getFirstChild() ); --cnt; n = sibling; } // Collapse to just before the endAncestor, which // is partially selected. if ( how != CLONE_CONTENTS ) { setEndBefore( endAncestor ); collapse( false ); } return frag; } /** * Visits the nodes selected by this range when we know * a-priori that the start and end containers are not the * same, but the end container is an ancestor of the start container * */ DOMDocumentFragment* DOMRangeImpl::traverseCommonEndContainer( DOMNode*startAncestor, int how ) { DOMDocumentFragment* frag = 0; if ( how!=DELETE_CONTENTS) frag = fDocument->createDocumentFragment(); DOMNode* n = traverseLeftBoundary( startAncestor, how ); if ( frag!=0 ) frag->appendChild( n ); XMLSize_t startIdx = indexOf( startAncestor, fEndContainer ); ++startIdx; // Because we already traversed it.... int cnt = (int)fEndOffset - (int)startIdx; n = startAncestor->getNextSibling(); while( cnt > 0 ) { DOMNode* sibling = n->getNextSibling(); DOMNode* xferNode = traverseFullySelected( n, how ); if ( frag!=0 ) frag->appendChild( xferNode ); --cnt; n = sibling; } if ( how != CLONE_CONTENTS ) { setStartAfter( startAncestor ); collapse( true ); } return frag; } /** * Visits the nodes selected by this range when we know * a-priori that the start and end containers are not * the same, and we also know that neither the start * nor end container is an ancestor of the other. */ DOMDocumentFragment* DOMRangeImpl::traverseCommonAncestors( DOMNode*startAncestor, DOMNode*endAncestor, int how ) { DOMDocumentFragment* frag = 0; if ( how!=DELETE_CONTENTS) frag = fDocument->createDocumentFragment(); DOMNode*n = traverseLeftBoundary( startAncestor, how ); if ( frag!=0 ) frag->appendChild( n ); DOMNode*commonParent = startAncestor->getParentNode(); XMLSize_t startOffset = indexOf( startAncestor, commonParent ); XMLSize_t endOffset = indexOf( endAncestor, commonParent ); ++startOffset; int cnt = (int)endOffset - (int)startOffset; DOMNode* sibling = startAncestor->getNextSibling(); while( cnt > 0 ) { DOMNode* nextSibling = sibling->getNextSibling(); n = traverseFullySelected( sibling, how ); if ( frag!=0 ) frag->appendChild( n ); sibling = nextSibling; --cnt; } n = traverseRightBoundary( endAncestor, how ); if ( frag!=0 ) frag->appendChild( n ); if ( how != CLONE_CONTENTS ) { setStartAfter( startAncestor ); collapse( true ); } return frag; } /** * Traverses the "right boundary" of this range and * operates on each "boundary node" according to the * how parameter. It is a-priori assumed * by this method that the right boundary does * not contain the range's start container. * * A "right boundary" is best visualized by thinking * of a sample tree: * A * /|\ * / | \ * / | \ * B C D * /|\ /|\ * E F G H I J * * Imagine first a range that begins between the * "E" and "F" nodes and ends between the * "I" and "J" nodes. The start container is * "B" and the end container is "D". Given this setup, * the following applies: * * Partially Selected Nodes: B, D
* Fully Selected Nodes: F, G, C, H, I * * The "right boundary" is the highest subtree node * that contains the ending container. The root of * this subtree is always partially selected. * * In this example, the nodes that are traversed * as "right boundary" nodes are: H, I, and D. * */ DOMNode* DOMRangeImpl::traverseRightBoundary( DOMNode*root, int how ) { DOMNode*next = getSelectedNode( fEndContainer, (int)fEndOffset-1 ); bool isFullySelected = ( next!=fEndContainer ); if ( next==root ) return traverseNode( next, isFullySelected, false, how ); DOMNode*parent = next->getParentNode(); DOMNode*clonedParent = traverseNode( parent, false, false, how ); while( parent!=0 ) { while( next!=0 ) { DOMNode* prevSibling = next->getPreviousSibling(); DOMNode* clonedChild = traverseNode( next, isFullySelected, false, how ); if ( how!=DELETE_CONTENTS ) { clonedParent->insertBefore( clonedChild, clonedParent->getFirstChild() ); } isFullySelected = true; next = prevSibling; } if ( parent==root ) return clonedParent; next = parent->getPreviousSibling(); parent = parent->getParentNode(); DOMNode* clonedGrandParent = traverseNode( parent, false, false, how ); if ( how!=DELETE_CONTENTS ) clonedGrandParent->appendChild( clonedParent ); clonedParent = clonedGrandParent; } // should never occur return 0; } /** * Traverses the "left boundary" of this range and * operates on each "boundary node" according to the * how parameter. It is a-priori assumed * by this method that the left boundary does * not contain the range's end container. * * A "left boundary" is best visualized by thinking * of a sample tree: * * A * /|\ * / | \ * / | \ * B C D * /|\ /|\ * E F G H I J * * Imagine first a range that begins between the * "E" and "F" nodes and ends between the * "I" and "J" nodes. The start container is * "B" and the end container is "D". Given this setup, * the following applies: * * Partially Selected Nodes: B, D
* Fully Selected Nodes: F, G, C, H, I * * The "left boundary" is the highest subtree node * that contains the starting container. The root of * this subtree is always partially selected. * * In this example, the nodes that are traversed * as "left boundary" nodes are: F, G, and B. * */ DOMNode* DOMRangeImpl::traverseLeftBoundary( DOMNode*root, int how ) { DOMNode*next = getSelectedNode( getStartContainer(), (int)getStartOffset() ); bool isFullySelected = ( next!=getStartContainer() ); if ( next==root ) return traverseNode( next, isFullySelected, true, how ); DOMNode* parent = next->getParentNode(); DOMNode* clonedParent = traverseNode( parent, false, true, how ); while( parent!=0 ) { while( next!=0 ) { DOMNode* nextSibling = next->getNextSibling(); DOMNode* clonedChild = traverseNode( next, isFullySelected, true, how ); if ( how!=DELETE_CONTENTS ) clonedParent->appendChild(clonedChild); isFullySelected = true; next = nextSibling; } if ( parent==root ) return clonedParent; next = parent->getNextSibling(); parent = parent->getParentNode(); DOMNode* clonedGrandParent = traverseNode( parent, false, true, how ); if ( how!=DELETE_CONTENTS ) clonedGrandParent->appendChild( clonedParent ); clonedParent = clonedGrandParent; } // should never occur return 0; } /** * Utility method for traversing a single node. * Does not properly handle a text node containing both the * start and end offsets. Such nodes should * have been previously detected and been routed to traverseTextNode. * */ DOMNode* DOMRangeImpl::traverseNode( DOMNode* n, bool isFullySelected, bool isLeft, int how ) { if ( isFullySelected ) return traverseFullySelected( n, how ); short type = n->getNodeType(); if((type == DOMNode::TEXT_NODE || type == DOMNode::CDATA_SECTION_NODE || type == DOMNode::COMMENT_NODE || type == DOMNode::PROCESSING_INSTRUCTION_NODE)) return traverseTextNode( n, isLeft, how ); return traversePartiallySelected( n, how ); } /** * Utility method for traversing a single node when * we know a-priori that the node if fully * selected. * */ DOMNode* DOMRangeImpl::traverseFullySelected( DOMNode* n, int how ) { switch( how ) { case CLONE_CONTENTS: return n->cloneNode( true ); case EXTRACT_CONTENTS: return n; case DELETE_CONTENTS: // revisit: // should I release the removed node? // not released in case user still referencing it externally n->getParentNode()->removeChild(n); return 0; } return 0; } /** * Utility method for traversing a single node when * we know a-priori that the node if partially * selected and is not a text node. * */ DOMNode* DOMRangeImpl::traversePartiallySelected( DOMNode*n, int how ) { switch( how ) { case DELETE_CONTENTS: return 0; case CLONE_CONTENTS: case EXTRACT_CONTENTS: return n->cloneNode( false ); } return 0; } /** * Utility method for traversing a text node that we know * a-priori to be on a left or right boundary of the range. * This method does not properly handle text nodes that contain * both the start and end points of the range. * */ DOMNode* DOMRangeImpl::traverseTextNode( DOMNode*n, bool isLeft, int how ) { XMLCh* txtValue = XMLString::replicate(n->getNodeValue(), fMemoryManager); ArrayJanitor janValue(txtValue, fMemoryManager); if ( isLeft ) { XMLSize_t startLen = XMLString::stringLen(fStartContainer->getNodeValue()); XMLSize_t offset = getStartOffset(); if (offset == 0) { if ( how != CLONE_CONTENTS ) n->setNodeValue(XMLUni::fgZeroLenString); } else { XMLCh* oldNodeValue; XMLCh oldTemp[4000]; if (offset >= 3999) { oldNodeValue = (XMLCh*) fMemoryManager->allocate ( (offset+1) * sizeof(XMLCh) );//new XMLCh[offset+1]; } else { oldNodeValue = oldTemp; } XMLString::subString(oldNodeValue, txtValue, 0, offset, ((DOMDocumentImpl *)fDocument)->getMemoryManager()); if ( how != CLONE_CONTENTS ) n->setNodeValue( ((DOMDocumentImpl *)fDocument)->getPooledString(oldNodeValue) ); if (offset>= 3999) fMemoryManager->deallocate(oldNodeValue);//delete[] oldNodeValue; } if ( how==DELETE_CONTENTS ) return 0; DOMNode* newNode = n->cloneNode( false ); if (startLen == offset) { newNode->setNodeValue(XMLUni::fgZeroLenString); } else { XMLCh* newNodeValue; XMLCh newTemp[4000]; if (offset >= 3999) { newNodeValue = (XMLCh*) fMemoryManager->allocate ( (offset+1) * sizeof(XMLCh) );//new XMLCh[offset+1]; } else { newNodeValue = newTemp; } XMLString::subString(newNodeValue, txtValue, offset, startLen, ((DOMDocumentImpl *)fDocument)->getMemoryManager()); newNode->setNodeValue( ((DOMDocumentImpl *)fDocument)->getPooledString(newNodeValue) ); if (offset>= 3999) fMemoryManager->deallocate(newNodeValue);//delete[] newNodeValue; } return newNode; } else { XMLSize_t endLen = XMLString::stringLen(fEndContainer->getNodeValue()); XMLSize_t offset = getEndOffset(); if (endLen == offset) { if ( how != CLONE_CONTENTS ) n->setNodeValue(XMLUni::fgZeroLenString); } else { XMLCh* oldNodeValue; XMLCh oldTemp[4000]; if (offset >= 3999) { oldNodeValue = (XMLCh*) fMemoryManager->allocate ( (offset+1) * sizeof(XMLCh) );//new XMLCh[offset+1]; } else { oldNodeValue = oldTemp; } XMLString::subString(oldNodeValue, txtValue, offset, endLen, ((DOMDocumentImpl *)fDocument)->getMemoryManager()); if ( how != CLONE_CONTENTS ) n->setNodeValue( ((DOMDocumentImpl *)fDocument)->getPooledString(oldNodeValue) ); if (offset>= 3999) fMemoryManager->deallocate(oldNodeValue);//delete[] oldNodeValue; } if ( how==DELETE_CONTENTS ) return 0; DOMNode* newNode = n->cloneNode( false ); if (offset == 0) { newNode->setNodeValue(XMLUni::fgZeroLenString); } else { XMLCh* newNodeValue; XMLCh newTemp[4000]; if (offset >= 3999) { newNodeValue = (XMLCh*) fMemoryManager->allocate ( (offset+1) * sizeof(XMLCh) );//new XMLCh[offset+1]; } else { newNodeValue = newTemp; } XMLString::subString(newNodeValue, txtValue, 0, offset, ((DOMDocumentImpl *)fDocument)->getMemoryManager()); newNode->setNodeValue( ((DOMDocumentImpl *)fDocument)->getPooledString(newNodeValue) ); if (offset>= 3999) fMemoryManager->deallocate(newNodeValue);//delete[] newNodeValue; } return newNode; } } /** * Utility method to retrieve a child node by index. This method * assumes the caller is trying to find out which node is * selected by the given index. Note that if the index is * greater than the number of children, this implies that the * first node selected is the parent node itself. * */ DOMNode* DOMRangeImpl::getSelectedNode( DOMNode*container, int offset ) { short type = container->getNodeType(); if((type == DOMNode::TEXT_NODE || type == DOMNode::CDATA_SECTION_NODE || type == DOMNode::COMMENT_NODE || type == DOMNode::PROCESSING_INSTRUCTION_NODE)) return container; // This case is an important convenience for // traverseRightBoundary() if ( offset<0 ) return container; DOMNode*child = container->getFirstChild(); while( child!=0 && offset > 0 ) { --offset; child = child->getNextSibling(); } if ( child!=0 ) return child; return container; } void DOMRangeImpl::checkReadOnly(DOMNode* start, DOMNode* end, XMLSize_t startOffset, XMLSize_t endOffset) { if ((start == 0) || (end == 0) ) return; DOMNode*sNode = 0; short type = start->getNodeType(); if ( type == DOMNode::DOCUMENT_TYPE_NODE ) { throw DOMException( DOMException::HIERARCHY_REQUEST_ERR, 0, fMemoryManager); } if((type == DOMNode::TEXT_NODE || type == DOMNode::CDATA_SECTION_NODE || type == DOMNode::COMMENT_NODE || type == DOMNode::PROCESSING_INSTRUCTION_NODE)) { if (castToNodeImpl(start)->isReadOnly()) { throw DOMException( DOMException::NO_MODIFICATION_ALLOWED_ERR, 0, fMemoryManager); } //if both start and end are text check and return if (start == end) return; sNode = start; } else { //set the start and end nodes to check sNode = start->getFirstChild(); for(XMLSize_t i = 0; igetNextSibling(); } DOMNode* eNode; type = end->getNodeType(); if ( type == DOMNode::DOCUMENT_TYPE_NODE ) { throw DOMException( DOMException::HIERARCHY_REQUEST_ERR, 0, fMemoryManager); } if((type == DOMNode::TEXT_NODE || type == DOMNode::CDATA_SECTION_NODE || type == DOMNode::COMMENT_NODE || type == DOMNode::PROCESSING_INSTRUCTION_NODE)) { eNode = end; //need to check only till this node } else { //need to check all the kids that fall before the end offset value eNode = end->getFirstChild(); if (endOffset > 0) { for (XMLSize_t i = 0; igetNextSibling(); } } //recursivly search if any node is readonly recurseTreeAndCheck(sNode, eNode); } void DOMRangeImpl::recurseTreeAndCheck(DOMNode* start, DOMNode* end) { for(DOMNode* node=start; node != 0 && node !=end; node=node->getNextSibling()) { if ( node->getNodeType()== DOMNode::DOCUMENT_TYPE_NODE ) { throw DOMException( DOMException::HIERARCHY_REQUEST_ERR, 0, fMemoryManager); } if (castToNodeImpl(node)->isReadOnly()) { throw DOMException( DOMException::NO_MODIFICATION_ALLOWED_ERR, 0, fMemoryManager); } if (node->hasChildNodes()) { node = node->getFirstChild(); recurseTreeAndCheck(node, end); } } } DOMNode* DOMRangeImpl::removeChild(DOMNode* parent, DOMNode* child) { fRemoveChild = child; //only a precaution measure not to update this range data before removal DOMNode*n = parent->removeChild(child); fRemoveChild = 0; return n; } // // Mutation functions // /* This function is called from DOM. * The text has already been replaced. * Fix-up any offsets. */ void DOMRangeImpl::receiveReplacedText(DOMNode* node) { if (node == 0) return; short type = fStartContainer->getNodeType(); if (node == fStartContainer && (type == DOMNode::TEXT_NODE || type == DOMNode::CDATA_SECTION_NODE || type == DOMNode::COMMENT_NODE || type == DOMNode::PROCESSING_INSTRUCTION_NODE)) { fStartOffset = 0; } type = fEndContainer->getNodeType(); if (node == fEndContainer && (type == DOMNode::TEXT_NODE || type == DOMNode::CDATA_SECTION_NODE || type == DOMNode::COMMENT_NODE || type == DOMNode::PROCESSING_INSTRUCTION_NODE)) { fEndOffset = 0; } } /** This function is called from DOM. * The text has already been deleted. * Fix-up any offsets. */ void DOMRangeImpl::updateRangeForDeletedText(DOMNode* node, XMLSize_t offset, XMLSize_t count) { if (node == 0) return; short type = fStartContainer->getNodeType(); if (node == fStartContainer && (type == DOMNode::TEXT_NODE || type == DOMNode::CDATA_SECTION_NODE || type == DOMNode::COMMENT_NODE || type == DOMNode::PROCESSING_INSTRUCTION_NODE)) { if (fStartOffset > offset+count) { fStartOffset = fStartOffset-count; } else if (fStartOffset > offset) { fStartOffset = offset; } } type = fEndContainer->getNodeType(); if (node == fEndContainer && (type == DOMNode::TEXT_NODE || type == DOMNode::CDATA_SECTION_NODE || type == DOMNode::COMMENT_NODE || type == DOMNode::PROCESSING_INSTRUCTION_NODE)) { if (fEndOffset > offset+count) { fEndOffset = fEndOffset-count; } else if (fEndOffset > offset) { fEndOffset = offset; } } } /** This function is called from DOM. * The text has already beeen inserted. * Fix-up any offsets. */ void DOMRangeImpl::updateRangeForInsertedText(DOMNode* node, XMLSize_t offset, XMLSize_t count) { if (node == 0) return; short type = fStartContainer->getNodeType(); if (node == fStartContainer && (type == DOMNode::TEXT_NODE || type == DOMNode::CDATA_SECTION_NODE || type == DOMNode::COMMENT_NODE || type == DOMNode::PROCESSING_INSTRUCTION_NODE)) { if (fStartOffset > offset) { fStartOffset = offset; } } type = fEndContainer->getNodeType(); if (node == fEndContainer && (type == DOMNode::TEXT_NODE || type == DOMNode::CDATA_SECTION_NODE || type == DOMNode::COMMENT_NODE || type == DOMNode::PROCESSING_INSTRUCTION_NODE)) { if (fEndOffset > offset) { fEndOffset = fEndOffset+count; } } } /** This function must be called by the DOM _BEFORE_ * a node is deleted, because at that time it is * connected in the DOM tree, which we depend on. */ void DOMRangeImpl::updateRangeForDeletedNode(DOMNode* node) { if (node == 0) return; if (fRemoveChild == node) return; if (node->getParentNode() == fStartContainer) { XMLSize_t index = indexOf(node, fStartContainer); if ( fStartOffset > index) { fStartOffset--; } } if (node->getParentNode() == fEndContainer) { XMLSize_t index = indexOf(node, fEndContainer); if ( fEndOffset > index) { fEndOffset--; } } if (node->getParentNode() != fStartContainer || node->getParentNode() != fEndContainer) { if (isAncestorOf(node, fStartContainer)) { DOMNode* tpNode = node->getParentNode(); setStartContainer( tpNode ); fStartOffset = indexOf( node, tpNode); } if (isAncestorOf(node, fEndContainer)) { DOMNode* tpNode = node->getParentNode(); setEndContainer( tpNode ); fEndOffset = indexOf( node, tpNode); } } } void DOMRangeImpl::updateRangeForInsertedNode(DOMNode* node) { if (node == 0) return; if (node->getParentNode() == fStartContainer) { XMLSize_t index = indexOf(node, fStartContainer); if (index < fStartOffset) { fStartOffset++; } } if (node->getParentNode() == fEndContainer) { XMLSize_t index = indexOf(node, fEndContainer); if (index < fEndOffset) { fEndOffset++; } } } void DOMRangeImpl::updateSplitInfo(DOMNode* oldNode, DOMNode* startNode, XMLSize_t offset) { if (startNode == 0) return; short type = fStartContainer->getNodeType(); if (oldNode == fStartContainer && (type == DOMNode::TEXT_NODE || type == DOMNode::CDATA_SECTION_NODE || type == DOMNode::COMMENT_NODE || type == DOMNode::PROCESSING_INSTRUCTION_NODE)) { if (fStartOffset > offset) { fStartOffset = fStartOffset - offset; fStartContainer = startNode; } } type = fEndContainer->getNodeType(); if (oldNode == fEndContainer && (type == DOMNode::TEXT_NODE || type == DOMNode::CDATA_SECTION_NODE || type == DOMNode::COMMENT_NODE || type == DOMNode::PROCESSING_INSTRUCTION_NODE)) { if (fEndOffset > offset) { fEndContainer = startNode; fEndOffset = fEndOffset - offset; } } } XERCES_CPP_NAMESPACE_END