blob: 572034f74ae7d473b63e1b02cb3362b8117eda5f [file] [log] [blame]
/*
* The Apache Software License, Version 1.1
*
* Copyright (c) 2001 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution,
* if any, must include the following acknowledgment:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowledgment may appear in the software itself,
* if and wherever such third-party acknowledgments normally appear.
*
* 4. The names "Xerces" and "Apache Software Foundation" must
* not be used to endorse or promote products derived from this
* software without prior written permission. For written
* permission, please contact apache\@apache.org.
*
* 5. Products derived from this software may not be called "Apache",
* nor may "Apache" appear in their name, without prior written
* permission of the Apache Software Foundation.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation, and was
* originally based on software copyright (c) 2001, International
* Business Machines, Inc., http://www.ibm.com . For more information
* on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*/
/*
* $Id$
*/
#include "IDNodeImpl.hpp"
#include "IDRangeImpl.hpp"
#include "IDTextImpl.hpp"
#include "IDDocumentImpl.hpp"
#include "IDOM_DOMException.hpp"
#include "IDOM_Document.hpp"
#include "IDDocumentFragmentImpl.hpp"
#include "IDOM_Document.hpp"
#include "IDOM_RangeException.hpp"
#include "IDOM_DOMException.hpp"
#include "IDOM_Text.hpp"
#include "IDCasts.hpp"
#include <framework/XMLBuffer.hpp>
//---------------------
// C'tor and D'tor
//---------------------
IDRangeImpl::IDRangeImpl(IDOM_Document* doc)
: fDocument(doc),
fStartContainer(doc),
fStartOffset(0),
fEndContainer(doc),
fEndOffset(0),
fDetached(false),
fCollapsed(true),
fRemoveChild(0)
{
}
IDRangeImpl::IDRangeImpl(const IDRangeImpl& other)
{
fDocument = other.fDocument;
fStartContainer = other.fStartContainer;
fStartOffset = other.fStartOffset;
fEndContainer = other.fEndContainer;
fEndOffset = other.fEndOffset;
fDetached = other.fDetached;
fCollapsed = other.fCollapsed;
fRemoveChild = other.fRemoveChild;
}
IDRangeImpl::~IDRangeImpl()
{
}
//-------------------------------
// Public getter functions
//-------------------------------
IDOM_Node* IDRangeImpl::getStartContainer() const
{
return fStartContainer;
}
unsigned int IDRangeImpl::getStartOffset() const
{
return fStartOffset;
}
IDOM_Node* IDRangeImpl::getEndContainer() const
{
return fEndContainer;
}
unsigned int IDRangeImpl::getEndOffset() const
{
return fEndOffset;
}
bool IDRangeImpl::getCollapsed() const
{
if (fDetached)
{
throw IDOM_DOMException(
IDOM_DOMException::INVALID_STATE_ERR, 0);
}
return ((fStartContainer == fEndContainer)
&& (fStartOffset == fEndOffset));
}
//-------------------------------
// Public getter functions
//-------------------------------
void IDRangeImpl::setStartContainer(const IDOM_Node* node)
{
if (fDetached)
{
throw IDOM_DOMException(
IDOM_DOMException::INVALID_STATE_ERR, 0);
}
fStartContainer = (IDOM_Node*) node;
}
void IDRangeImpl::setStartOffset(unsigned int offset)
{
if (fDetached)
{
throw IDOM_DOMException(
IDOM_DOMException::INVALID_STATE_ERR, 0);
}
fStartOffset = offset;
}
void IDRangeImpl::setEndContainer(const IDOM_Node* node)
{
if (fDetached)
{
throw IDOM_DOMException(
IDOM_DOMException::INVALID_STATE_ERR, 0);
}
fEndContainer = (IDOM_Node*) node;
}
void IDRangeImpl::setEndOffset(unsigned int offset)
{
if (fDetached)
{
throw IDOM_DOMException(
IDOM_DOMException::INVALID_STATE_ERR, 0);
}
fEndOffset = offset;
}
void IDRangeImpl::setStart(const IDOM_Node* refNode, unsigned int offset)
{
validateNode(refNode);
checkIndex(refNode, offset);
fStartContainer = (IDOM_Node*) refNode;
fStartOffset = offset;
if ((fDocument != refNode->getOwnerDocument() )
&& (refNode->getOwnerDocument() != 0) )
{
fDocument = refNode->getOwnerDocument();
collapse(true);
}
//compare the start and end boundary point
//collapse if start point is after the end point
if(compareBoundaryPoints(IDOM_Range::END_TO_START, this) == 1)
collapse(true); //collapse the range positions to start
else
fCollapsed = false;
}
void IDRangeImpl::setEnd(const IDOM_Node* refNode, unsigned int offset)
{
validateNode(refNode);
checkIndex(refNode, offset);
fEndContainer = (IDOM_Node*) refNode;
fEndOffset = offset;
if ((fDocument != refNode->getOwnerDocument() )
&& (refNode->getOwnerDocument() != 0) )
{
fDocument = refNode->getOwnerDocument();
collapse(false);
}
//compare the start and end boundary point
//collapse if start point is after the end point
if(compareBoundaryPoints(IDOM_Range::END_TO_START, this) == 1)
collapse(false); //collapse the range positions to end
else
fCollapsed = false;
}
void IDRangeImpl::setStartBefore(const IDOM_Node* refNode)
{
if( fDetached) {
throw IDOM_DOMException(
IDOM_DOMException::INVALID_STATE_ERR, 0);
}
if ( !hasLegalRootContainer(refNode) || !isLegalContainedNode(refNode)) {
throw IDOM_RangeException(
IDOM_RangeException::INVALID_NODE_TYPE_ERR, 0);
}
fStartContainer = refNode->getParentNode();
unsigned int i = 0;
for (IDOM_Node* n = (IDOM_Node*) refNode; n!=0; n = n->getPreviousSibling()) {
i++;
}
if (i == 0)
fStartOffset = 0;
else
fStartOffset = i-1;
if ((fDocument != refNode->getOwnerDocument())
&& (refNode->getOwnerDocument() != 0) )
{
fDocument = refNode->getOwnerDocument();
collapse(true);
}
//compare the start and end boundary point
//collapse if start point is after the end point
if(compareBoundaryPoints(IDOM_Range::END_TO_START, this) == 1)
collapse(true); //collapse the range positions to start
else
fCollapsed = false;
}
void IDRangeImpl::setStartAfter(const IDOM_Node* refNode)
{
if( fDetached) {
throw IDOM_DOMException(
IDOM_DOMException::INVALID_STATE_ERR, 0);
}
if ( !hasLegalRootContainer(refNode) || !isLegalContainedNode(refNode)) {
throw IDOM_RangeException(
IDOM_RangeException::INVALID_NODE_TYPE_ERR, 0);
}
fStartContainer = refNode->getParentNode();
unsigned int i = 0;
for (IDOM_Node* n = (IDOM_Node*) refNode; n!=0; n = n->getPreviousSibling()) {
i++;
}
fStartOffset = i;
if ((fDocument != refNode->getOwnerDocument() )
&& (refNode->getOwnerDocument() != 0) )
{
fDocument = refNode->getOwnerDocument();
collapse(true);
}
//compare the start and end boundary point
//collapse if start point is after the end point
if(compareBoundaryPoints(IDOM_Range::END_TO_START, this) == 1)
collapse(true); //collapse the range positions to start
else
fCollapsed = false;
}
void IDRangeImpl::setEndBefore(const IDOM_Node* refNode)
{
if( fDetached) {
throw IDOM_DOMException(
IDOM_DOMException::INVALID_STATE_ERR, 0);
}
if ( !hasLegalRootContainer(refNode) || !isLegalContainedNode(refNode)) {
throw IDOM_RangeException(
IDOM_RangeException::INVALID_NODE_TYPE_ERR, 0);
}
fEndContainer = refNode->getParentNode();
unsigned int i = 0;
for (IDOM_Node* n = (IDOM_Node*) refNode; n!=0; n = n->getPreviousSibling(), i++) ;
if (i< 1)
fEndOffset = 0;
else
fEndOffset = i-1;
if ((fDocument != refNode->getOwnerDocument() )
&& (refNode->getOwnerDocument() != 0) )
{
fDocument = refNode->getOwnerDocument();
collapse(true);
}
//compare the start and end boundary point
//collapse if start point is after the end point
if(compareBoundaryPoints(IDOM_Range::END_TO_START, this) == 1)
collapse(false); //collapse the range positions to end
else
fCollapsed = false;
}
void IDRangeImpl::setEndAfter(const IDOM_Node* refNode)
{
if( fDetached) {
throw IDOM_DOMException(
IDOM_DOMException::INVALID_STATE_ERR, 0);
}
if ( !hasLegalRootContainer(refNode) || !isLegalContainedNode(refNode)) {
throw IDOM_RangeException(
IDOM_RangeException::INVALID_NODE_TYPE_ERR, 0);
}
fEndContainer = refNode->getParentNode();
unsigned int i = 0;
for (IDOM_Node* n = (IDOM_Node*) refNode; n!=0; n = n->getPreviousSibling(), i++) ;
if (i ==0)
fEndOffset = 0;
else
fEndOffset = i;
if ((fDocument != refNode->getOwnerDocument() )
&& (refNode->getOwnerDocument() != 0) )
{
fDocument = refNode->getOwnerDocument();
collapse(true);
}
//compare the start and end boundary point
//collapse if start point is after the end point
if(compareBoundaryPoints(IDOM_Range::END_TO_START, this) == 1)
collapse(false); //collapse the range positions to end
else
fCollapsed = false;
}
//-------------------------------
// Public Misc. functions
//-------------------------------
void IDRangeImpl::detach()
{
if( fDetached) {
throw IDOM_DOMException(
IDOM_DOMException::INVALID_STATE_ERR, 0);
}
fDetached = true;
//0ify nodes
fStartContainer = 0;
fStartOffset = 0;
fEndContainer = 0;
fEndOffset = 0;
fCollapsed = true;
fRemoveChild = 0;
}
void IDRangeImpl::collapse(bool toStart)
{
if( fDetached) {
throw IDOM_DOMException(
IDOM_DOMException::INVALID_STATE_ERR, 0);
}
if (toStart) {
fEndContainer = fStartContainer;
fEndOffset = fStartOffset;
} else {
fStartContainer = fEndContainer;
fStartOffset = fEndOffset;
}
fCollapsed = true;
}
void IDRangeImpl::selectNode(const IDOM_Node* refNode)
{
validateNode(refNode);
if ( !isLegalContainedNode(refNode)) {
throw IDOM_RangeException(
IDOM_RangeException::INVALID_NODE_TYPE_ERR, 0);
}
//First check for the text type node
if (refNode->getNodeType() == IDOM_Node::TEXT_NODE)
{
//The node itself is the container.
fStartContainer = (IDOM_Node*) refNode;
fEndContainer = (IDOM_Node*) refNode;
//Select all the contents of the node
fStartOffset = 0;
fEndOffset = ((IDOM_Text *)refNode)->getLength();
return;
}
IDOM_Node* parent = refNode->getParentNode();
if (parent != 0 ) // REVIST: what to do if it IS 0?
{
fStartContainer = parent;
fEndContainer = parent;
unsigned int i = 0;
for (IDOM_Node* n = parent->getFirstChild(); n!=0, n!=refNode; n = n->getNextSibling()) {
i++;
}
fStartOffset = i;
fEndOffset = fStartOffset+1;
}
}
void IDRangeImpl::selectNodeContents(const IDOM_Node* node)
{
validateNode(node);
fStartContainer = (IDOM_Node*) node;
fEndContainer = (IDOM_Node*) node;
fStartOffset = 0;
if (node->getNodeType() == IDOM_Node::TEXT_NODE ) {
fEndOffset = ((IDOM_Text *)node)->getLength();
return;
}
IDOM_Node* first = node->getFirstChild();
if (first == 0) {
fEndOffset = 0;
return;
}
unsigned int i = 0;
for (IDOM_Node* n = first; n!=0; n = n->getNextSibling()) {
i++;
}
fEndOffset = i;
}
void IDRangeImpl::surroundContents(IDOM_Node* newParent)
{
if (newParent==0) return;
//check for elimination criteria
if( fDetached) {
throw IDOM_DOMException(
IDOM_DOMException::INVALID_STATE_ERR, 0);
}
if (newParent->getOwnerDocument() !=fDocument) {
throw IDOM_DOMException(
IDOM_DOMException::WRONG_DOCUMENT_ERR, 0);
}
int type = newParent->getNodeType();
if ( !isLegalContainedNode(newParent)
|| type == IDOM_Node::DOCUMENT_TYPE_NODE)
{
throw IDOM_RangeException(
IDOM_RangeException::INVALID_NODE_TYPE_ERR, 0);
}
IDOM_Node* root = (IDOM_Node*) getCommonAncestorContainer();
IDOM_Node* realStart = fStartContainer;
IDOM_Node* realEnd = fEndContainer;
if (fStartContainer->getNodeType() == IDOM_Node::TEXT_NODE) {
realStart = fStartContainer->getParentNode();
}
if (fEndContainer->getNodeType() == IDOM_Node::TEXT_NODE) {
realEnd = fEndContainer->getParentNode();
}
if (realStart != realEnd) {
throw IDOM_RangeException(
IDOM_RangeException::BAD_BOUNDARYPOINTS_ERR, 0);
}
IDOM_DocumentFragment* frag = (IDOM_DocumentFragment*) extractContents();
insertNode(newParent);
newParent->appendChild(frag);
selectNode(newParent);
}
short IDRangeImpl::compareBoundaryPoints(IDOM_Range::CompareHow how, const IDOM_Range* srcRange) const
{
if (fDocument != ((IDRangeImpl*)srcRange)->fDocument) {
throw IDOM_DOMException(
IDOM_DOMException::WRONG_DOCUMENT_ERR, 0);
}
if( fDetached) {
throw IDOM_DOMException(
IDOM_DOMException::INVALID_STATE_ERR, 0);
}
IDOM_Node* pointA;
IDOM_Node* pointB;
int offsetA, offsetB;
switch (how)
{
case (IDOM_Range::START_TO_START) :
pointB = srcRange->getStartContainer();
pointA = fStartContainer;
offsetB = srcRange->getStartOffset();
offsetA = fStartOffset;
break;
case (IDOM_Range::START_TO_END) :
pointB = srcRange->getStartContainer();
pointA = fEndContainer;
offsetB = srcRange->getStartOffset();
offsetA = fEndOffset;
break;
case (IDOM_Range::END_TO_START) :
pointB = srcRange->getEndContainer();
pointA = fStartContainer;
offsetB = srcRange->getEndOffset();
offsetA = fStartOffset;
break;
case (IDOM_Range::END_TO_END) :
pointB = srcRange->getEndContainer();
pointA = fEndContainer;
offsetB = srcRange->getEndOffset();
offsetA = fEndOffset;
break;
}
// 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 (IDOM_Node* node = pointA->getFirstChild(); node != 0; node=node->getNextSibling()) {
if (isAncestorOf(node, pointB)) {
int index = indexOf(node, pointA);
if (offsetA <= index) return -1;
return 1;
}
}
// case 3: Child C of container B is ancestor of A
for (IDOM_Node* nd = pointB->getFirstChild(); nd != 0; nd=nd->getNextSibling()) {
if (isAncestorOf(nd, pointA)) {
int index = indexOf(nd, pointB);
if (index < offsetB ) return -1;
return 1; //B strictly before A
}
}
// case 4: preorder traversal of context tree.
IDOM_Node* ancestor = (IDOM_Node*) commonAncestorOf(pointA, pointB);
IDOM_Node* current = ancestor;
do {
if (current == pointA) return -1;
if (current == pointB) return 1;
current = nextNode(current, true);
}
while (current!=0 && current!=ancestor);
return -2; // this should never happen
}
void IDRangeImpl:: deleteContents()
{
traverseContents(DELETE_CONTENTS);
}
IDOM_DocumentFragment* IDRangeImpl::extractContents()
{
checkReadOnly(fStartContainer, fEndContainer, fStartOffset, fEndOffset);
return traverseContents(EXTRACT_CONTENTS);
}
IDOM_DocumentFragment* IDRangeImpl::cloneContents() const
{
// cast off const.
return ((IDRangeImpl *)this)->traverseContents(CLONE_CONTENTS);
}
void IDRangeImpl::insertNode(IDOM_Node* newNode)
{
if (newNode == 0) return; //don't have to do anything
for (IDOM_Node* aNode = fStartContainer; aNode!=0; aNode = aNode->getParentNode()) {
if (castToNodeImpl(newNode)->isReadOnly()) {
throw IDOM_DOMException(
IDOM_DOMException::NO_MODIFICATION_ALLOWED_ERR, 0);
}
}
if (fDocument != newNode->getOwnerDocument()) {
throw IDOM_DOMException(
IDOM_DOMException::WRONG_DOCUMENT_ERR, 0);
}
// Prevent cycles in the tree.
//isKidOK() is not checked here as its taken care by insertBefore() function
if (isAncestorOf( newNode, fStartContainer)) {
throw IDOM_DOMException(
IDOM_DOMException::HIERARCHY_REQUEST_ERR, 0);
}
if( fDetached) {
throw IDOM_DOMException(
IDOM_DOMException::INVALID_STATE_ERR, 0);
}
int type = newNode->getNodeType();
if (type == IDOM_Node::ATTRIBUTE_NODE
|| type == IDOM_Node::ENTITY_NODE
|| type == IDOM_Node::NOTATION_NODE
|| type == IDOM_Node::DOCUMENT_NODE)
{
throw IDOM_RangeException(
IDOM_RangeException::INVALID_NODE_TYPE_ERR, 0);
}
IDOM_Node* parent;
IDOM_Node* next;
if (fStartContainer->getNodeType() == IDOM_Node::TEXT_NODE) {
//set 'parent' and 'next' here
parent = fStartContainer->getParentNode();
//split the text nodes
if (fStartOffset > 0)
((IDOM_Text*)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(unsigned int i = 0; (i < fStartOffset) && (next != 0); i++) {
next=next->getNextSibling();
}
}
if (parent != 0) {
if (next != 0)
parent->insertBefore(newNode, next);
else
parent->appendChild(newNode);
}
}
IDOM_Range* IDRangeImpl::cloneRange() const
{
if( fDetached) {
throw IDOM_DOMException(
IDOM_DOMException::INVALID_STATE_ERR, 0);
}
IDOM_Range* range = fDocument->createRange();
range->setStart(fStartContainer, fStartOffset);
range->setEnd(fEndContainer, fEndOffset);
return range;
}
const XMLCh* IDRangeImpl::toString() const
{
if( fDetached) {
throw IDOM_DOMException(
IDOM_DOMException::INVALID_STATE_ERR, 0);
}
IDOM_Node* node = fStartContainer;
IDOM_Node* stopNode = fEndContainer;
XMLBuffer retStringBuf;
if ( (fStartContainer->getNodeType() == IDOM_Node::TEXT_NODE)
|| (fStartContainer->getNodeType() == IDOM_Node::CDATA_SECTION_NODE) ) {
if (fStartContainer == fEndContainer) {
if (fEndOffset == fStartOffset) {
return XMLUni::fgZeroLenString;
}
else {
XMLCh* tempString;
XMLCh temp[4000];
if ((fEndOffset-fStartOffset) >= 3999)
tempString = new XMLCh[fEndOffset-fStartOffset+1];
else
tempString = temp;
XMLString::subString(tempString, fStartContainer->getNodeValue(), fStartOffset, fEndOffset);
const XMLCh* retString = ((IDDocumentImpl *)fDocument)->getPooledString(tempString);
if ((fEndOffset-fStartOffset) >= 3999)
delete[] tempString;
return retString;
}
} else {
unsigned int length = XMLString::stringLen(fStartContainer->getNodeValue());
if (length != fStartOffset) {
XMLCh* tempString;
XMLCh temp[4000];
if ((length - fStartOffset) >= 3999)
tempString = new XMLCh[length - fStartOffset+1];
else
tempString = temp;
XMLString::subString(tempString, fStartContainer->getNodeValue(), fStartOffset, length);
retStringBuf.append(tempString);
if ((length - fStartOffset) >= 3999)
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
unsigned int counter = 0;
while (counter<fStartOffset && node!=0) {
node=node->getNextSibling();
counter++;
}
}
if (node == 0) {
node = nextNode(fStartContainer,false);
}
}
if ( fEndContainer->getNodeType()!= IDOM_Node::TEXT_NODE &&
fEndContainer->getNodeType()!= IDOM_Node::CDATA_SECTION_NODE ){
int i=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;
if (node->getNodeType() == IDOM_Node::TEXT_NODE
|| node->getNodeType() == IDOM_Node::CDATA_SECTION_NODE) {
retStringBuf.append(node->getNodeValue());
}
node = nextNode(node, true);
}
if (fEndContainer->getNodeType() == IDOM_Node::TEXT_NODE
|| fEndContainer->getNodeType() == IDOM_Node::CDATA_SECTION_NODE) {
if (fEndOffset != 0) {
XMLCh* tempString;
XMLCh temp[4000];
if (fEndOffset >= 3999)
tempString = new XMLCh[fEndOffset+1];
else
tempString = temp;
XMLString::subString(tempString, fEndContainer->getNodeValue(), 0, fEndOffset);
retStringBuf.append(tempString);
if (fEndOffset >= 3999)
delete[] tempString;
}
}
return ((IDDocumentImpl *)fDocument)->getPooledString(retStringBuf.getRawBuffer());
}
IDOM_Document* IDRangeImpl::getDocument()
{
return fDocument;
}
const IDOM_Node* IDRangeImpl::getCommonAncestorContainer() const
{
return commonAncestorOf(fStartContainer, fEndContainer);
}
//---------------------
//private functions
//---------------------
bool IDRangeImpl::isValidAncestorType(const IDOM_Node* node) const
{
for (IDOM_Node* aNode = (IDOM_Node*) node; aNode!=0; aNode = aNode->getParentNode()) {
short type = aNode->getNodeType();
if ( type == IDOM_Node::ENTITY_NODE
|| type == IDOM_Node::NOTATION_NODE
|| type == IDOM_Node::DOCUMENT_TYPE_NODE)
return false;
}
return true;
}
bool IDRangeImpl::isAncestorOf(const IDOM_Node* a, const IDOM_Node* b) {
for (IDOM_Node* node = (IDOM_Node*) b; node != 0; node=node->getParentNode()) {
if (node == a) return true;
}
return false;
}
bool IDRangeImpl::hasLegalRootContainer(const IDOM_Node* node) const {
if ( node==0 )
return false;
IDOM_Node* rootContainer = (IDOM_Node*)node;
for (; rootContainer->getParentNode()!=0; rootContainer = rootContainer->getParentNode())
;
switch( rootContainer->getNodeType() ) {
case IDOM_Node::ATTRIBUTE_NODE:
case IDOM_Node::DOCUMENT_NODE:
case IDOM_Node::DOCUMENT_FRAGMENT_NODE:
return true;
}
return false;
}
bool IDRangeImpl::isLegalContainedNode(const IDOM_Node* node ) const {
if ( node==0 )
return false;
switch( node->getNodeType() )
{
case IDOM_Node::DOCUMENT_NODE:
case IDOM_Node::DOCUMENT_FRAGMENT_NODE:
case IDOM_Node::ATTRIBUTE_NODE:
case IDOM_Node::ENTITY_NODE:
case IDOM_Node::NOTATION_NODE:
return false;
}
return true;
}
unsigned short IDRangeImpl::indexOf(const IDOM_Node* child, const IDOM_Node* parent) const
{
unsigned short i = 0;
if (child->getParentNode() != parent) return (unsigned short)-1;
for(IDOM_Node* node = child->getPreviousSibling(); node!= 0; node=node->getPreviousSibling()) {
i++;
}
return i;
}
void IDRangeImpl::validateNode(const IDOM_Node* node) const
{
if( fDetached) {
throw IDOM_DOMException(
IDOM_DOMException::INVALID_STATE_ERR, 0);
}
if ( !isValidAncestorType(node)) {
throw IDOM_RangeException(
IDOM_RangeException::INVALID_NODE_TYPE_ERR, 0);
}
}
const IDOM_Node* IDRangeImpl::commonAncestorOf(const IDOM_Node* pointA, const IDOM_Node* pointB) const
{
if (fDetached)
throw IDOM_DOMException(IDOM_DOMException::INVALID_STATE_ERR, 0);
if (pointA->getOwnerDocument() != pointB->getOwnerDocument())
throw IDOM_DOMException( IDOM_DOMException::WRONG_DOCUMENT_ERR, 0 );
//if the containers are same then it itself is its common ancestor.
if (pointA == pointB)
return pointA;
typedef RefVectorOf<IDOM_Node> VectorNodes;
VectorNodes startV(1, false);
IDOM_Node* node;
for (node=fStartContainer; node != 0; node=node->getParentNode())
{
startV.addElement(node);
}
VectorNodes endV(1, false);
for (node=fEndContainer; node != 0; node=node->getParentNode())
{
endV.addElement(node);
}
int s = startV.size()-1;
int e = endV.size()-1;
IDOM_Node* commonAncestor;
while (s>=0 && e>=0) {
if (startV.elementAt(s) == endV.elementAt(e)) {
commonAncestor = startV.elementAt(s);
}
else break;
--s;
--e;
}
return commonAncestor;
}
void IDRangeImpl::checkIndex(const IDOM_Node* node, unsigned int offset) const
{
if (offset < 0) {
throw IDOM_DOMException( IDOM_DOMException::INDEX_SIZE_ERR, 0 );
}
short type = node->getNodeType();
if((type == IDOM_Node::TEXT_NODE
|| type == IDOM_Node::CDATA_SECTION_NODE
|| type == IDOM_Node::COMMENT_NODE
|| type == IDOM_Node::PROCESSING_INSTRUCTION_NODE)) {
if (offset > XMLString::stringLen(node->getNodeValue()))
throw IDOM_DOMException( IDOM_DOMException::INDEX_SIZE_ERR, 0 );
else return;
}
IDOM_Node* child = node->getFirstChild();
unsigned int i = 0;
for (; child != 0; i++) {
child = child->getNextSibling();
}
if (i < offset) {
throw IDOM_DOMException( IDOM_DOMException::INDEX_SIZE_ERR, 0 );
}
}
IDOM_Node* IDRangeImpl::nextNode(const IDOM_Node* node, bool visitChildren) const
{
if (node == 0) return 0;
IDOM_Node* 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.
IDOM_Node* parent = node->getParentNode();
while ( (parent != 0) && (parent != fDocument) )
{
result = parent->getNextSibling();
if (result != 0) {
return result;
} else {
parent = parent->getParentNode();
if (parent == fEndContainer) return parent;
}
}
// 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.
*/
IDOM_DocumentFragment* IDRangeImpl::traverseContents(TraversalType how)
{
if (fDetached)
throw IDOM_DOMException(IDOM_DOMException::INVALID_STATE_ERR, 0);
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
for (IDOM_Node* node = fStartContainer->getFirstChild(); node != 0; node=node->getNextSibling()) {
if (isAncestorOf(node, fEndContainer))
return traverseCommonStartContainer( node, how );
}
// case 3: Child C of end container is ancestor of start container
for (IDOM_Node* nd = fEndContainer->getFirstChild(); nd != 0; nd=nd->getNextSibling()) {
if (isAncestorOf(nd, fStartContainer))
return traverseCommonEndContainer( nd, how );
}
// case 4: preorder traversal of context tree.
// There is a common ancestor container. Find the
// ancestor siblings that are children of that container.
IDOM_Node* ancestor = (IDOM_Node*)commonAncestorOf(fStartContainer, fEndContainer);
return traverseCommonAncestors( ancestor, ancestor, how );
}
/**
* Visits the nodes selected by this range when we know
* a-priori that the start and end containers are the same.
*
*/
IDOM_DocumentFragment* IDRangeImpl::traverseSameContainer( int how )
{
IDOM_DocumentFragment* frag = 0;
if ( how!=DELETE_CONTENTS)
frag = fDocument->createDocumentFragment();
// If selection is empty, just return the fragment
if ( fStartOffset==fEndOffset )
return frag;
IDOM_Node* current = fStartContainer;
IDOM_Node* cloneCurrent = 0;
// Text node needs special case handling
if ( fStartContainer->getNodeType()== IDOM_Node::TEXT_NODE )
{
cloneCurrent = fStartContainer->cloneNode(false);
if (fEndOffset == fStartOffset) {
cloneCurrent->setNodeValue(XMLUni::fgZeroLenString);
}
else {
XMLCh* tempString;
XMLCh temp[4000];
if (fEndOffset >= 3999)
tempString = new XMLCh[fEndOffset+1];
else
tempString = temp;
XMLString::subString(tempString, cloneCurrent->getNodeValue(), fStartOffset, fEndOffset);
cloneCurrent->setNodeValue(((IDDocumentImpl *)fDocument)->getPooledString(tempString));
if (fEndOffset >= 3999)
delete[] tempString;
}
// set the original text node to its new value
if ( how != CLONE_CONTENTS )
((IDOM_Text*)fStartContainer)->deleteData(fStartOffset, fEndOffset-fStartOffset);
if ( how != DELETE_CONTENTS)
frag->appendChild(cloneCurrent);
}
else {
// Copy nodes between the start/end offsets.
IDOM_Node* n = getSelectedNode( fStartContainer, fStartOffset );
int cnt = fEndOffset - fStartOffset;
while( cnt > 0 )
{
IDOM_Node* sibling = n->getNextSibling();
IDOM_Node* 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
*
*/
IDOM_DocumentFragment* IDRangeImpl::traverseCommonStartContainer( IDOM_Node*endAncestor, int how )
{
IDOM_DocumentFragment* frag = 0;
if ( how!=DELETE_CONTENTS)
frag = fDocument->createDocumentFragment();
IDOM_Node*n = traverseRightBoundary( endAncestor, how );
if ( frag!=0 )
frag->appendChild( n );
int endIdx = indexOf( endAncestor, fStartContainer );
int cnt = endIdx - fStartOffset;
if ( cnt <=0 )
{
// Collapse to just before the endAncestor, which
// is partially selected.
if ( how != CLONE_CONTENTS )
{
setEndBefore( endAncestor );
collapse( false );
}
return frag;
}
n = endAncestor->getPreviousSibling();
while( cnt > 0 )
{
IDOM_Node* sibling = n->getPreviousSibling();
IDOM_Node* 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
*
*/
IDOM_DocumentFragment* IDRangeImpl::traverseCommonEndContainer( IDOM_Node*startAncestor, int how )
{
IDOM_DocumentFragment* frag = 0;
if ( how!=DELETE_CONTENTS)
frag = fDocument->createDocumentFragment();
IDOM_Node* n = traverseLeftBoundary( startAncestor, how );
if ( frag!=0 )
frag->appendChild( n );
int startIdx = indexOf( startAncestor, fEndContainer );
++startIdx; // Because we already traversed it....
int cnt = fEndOffset - startIdx;
n = startAncestor->getNextSibling();
while( cnt > 0 )
{
IDOM_Node* sibling = n->getNextSibling();
IDOM_Node* 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.
*/
IDOM_DocumentFragment* IDRangeImpl::traverseCommonAncestors( IDOM_Node*startAncestor, IDOM_Node*endAncestor, int how )
{
IDOM_DocumentFragment* frag = 0;
if ( how!=DELETE_CONTENTS)
frag = fDocument->createDocumentFragment();
IDOM_Node*n = traverseLeftBoundary( startAncestor, how );
if ( frag!=0 )
frag->appendChild( n );
IDOM_Node*commonParent = startAncestor->getParentNode();
int startOffset = indexOf( startAncestor, commonParent );
int endOffset = indexOf( endAncestor, commonParent );
++startOffset;
int cnt = endOffset - startOffset;
IDOM_Node* sibling = startAncestor->getNextSibling();
while( cnt > 0 )
{
IDOM_Node* 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<br>
* 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.
*
*/
IDOM_Node*IDRangeImpl::traverseRightBoundary( IDOM_Node*root, int how )
{
IDOM_Node*next = getSelectedNode( fEndContainer, fEndOffset-1 );
bool isFullySelected = ( next!=fEndContainer );
if ( next==root )
return traverseNode( next, isFullySelected, false, how );
IDOM_Node*parent = next->getParentNode();
IDOM_Node*clonedParent = traverseNode( parent, false, false, how );
while( parent!=0 )
{
while( next!=0 )
{
IDOM_Node* prevSibling = next->getPreviousSibling();
IDOM_Node* 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();
IDOM_Node* 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<br>
* 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.
*
*/
IDOM_Node*IDRangeImpl::traverseLeftBoundary( IDOM_Node*root, int how )
{
IDOM_Node*next = getSelectedNode( getStartContainer(), getStartOffset() );
bool isFullySelected = ( next!=getStartContainer() );
if ( next==root )
return traverseNode( next, isFullySelected, true, how );
IDOM_Node* parent = next->getParentNode();
IDOM_Node* clonedParent = traverseNode( parent, false, true, how );
while( parent!=0 )
{
while( next!=0 )
{
IDOM_Node* nextSibling = next->getNextSibling();
IDOM_Node* 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();
IDOM_Node* 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.
*
*/
IDOM_Node*IDRangeImpl::traverseNode( IDOM_Node* n, bool isFullySelected, bool isLeft, int how )
{
if ( isFullySelected )
return traverseFullySelected( n, how );
if ( n->getNodeType()== IDOM_Node::TEXT_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.
*
*/
IDOM_Node*IDRangeImpl::traverseFullySelected( IDOM_Node* n, int how )
{
switch( how )
{
case CLONE_CONTENTS:
return n->cloneNode( true );
case EXTRACT_CONTENTS:
if ( n->getNodeType()== IDOM_Node::DOCUMENT_TYPE_NODE )
{
throw IDOM_DOMException(
IDOM_DOMException::HIERARCHY_REQUEST_ERR, 0);
}
return n;
case DELETE_CONTENTS:
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.
*
*/
IDOM_Node*IDRangeImpl::traversePartiallySelected( IDOM_Node*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.
*
*/
IDOM_Node*IDRangeImpl::traverseTextNode( IDOM_Node*n, bool isLeft, int how )
{
const XMLCh* txtValue = n->getNodeValue();
if ( isLeft )
{
int startLen = XMLString::stringLen(fStartContainer->getNodeValue());
int offset = getStartOffset();
if (offset == 0) {
if ( how != CLONE_CONTENTS )
n->setNodeValue(XMLUni::fgZeroLenString);
}
else {
XMLCh* oldNodeValue;
XMLCh oldTemp[4000];
if (offset >= 3999) {
oldNodeValue = new XMLCh[offset+1];
}
else {
oldNodeValue = oldTemp;
}
XMLString::subString(oldNodeValue, txtValue, 0, offset);
if ( how != CLONE_CONTENTS )
n->setNodeValue( ((IDDocumentImpl *)fDocument)->getPooledString(oldNodeValue) );
if (offset>= 3999)
delete[] oldNodeValue;
}
if ( how==DELETE_CONTENTS )
return 0;
IDOM_Node* newNode = n->cloneNode( false );
if (startLen == offset) {
newNode->setNodeValue(XMLUni::fgZeroLenString);
}
else {
XMLCh* newNodeValue;
XMLCh newTemp[4000];
if (offset >= 3999) {
newNodeValue = new XMLCh[offset+1];
}
else {
newNodeValue = newTemp;
}
XMLString::subString(newNodeValue, txtValue, offset, startLen);
newNode->setNodeValue( ((IDDocumentImpl *)fDocument)->getPooledString(newNodeValue) );
if (offset>= 3999)
delete[] newNodeValue;
}
return newNode;
}
else
{
int endLen = XMLString::stringLen(fEndContainer->getNodeValue());
int offset = getEndOffset();
if (endLen == offset) {
if ( how != CLONE_CONTENTS )
n->setNodeValue(XMLUni::fgZeroLenString);
}
else {
XMLCh* oldNodeValue;
XMLCh oldTemp[4000];
if (offset >= 3999) {
oldNodeValue = new XMLCh[offset+1];
}
else {
oldNodeValue = oldTemp;
}
XMLString::subString(oldNodeValue, txtValue, offset, endLen);
if ( how != CLONE_CONTENTS )
n->setNodeValue( ((IDDocumentImpl *)fDocument)->getPooledString(oldNodeValue) );
if (offset>= 3999)
delete[] oldNodeValue;
}
if ( how==DELETE_CONTENTS )
return 0;
IDOM_Node* newNode = n->cloneNode( false );
if (offset == 0) {
newNode->setNodeValue(XMLUni::fgZeroLenString);
}
else {
XMLCh* newNodeValue;
XMLCh newTemp[4000];
if (offset >= 3999) {
newNodeValue = new XMLCh[offset+1];
}
else {
newNodeValue = newTemp;
}
XMLString::subString(newNodeValue, txtValue, 0, offset);
newNode->setNodeValue( ((IDDocumentImpl *)fDocument)->getPooledString(newNodeValue) );
if (offset>= 3999)
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.
*
*/
IDOM_Node*IDRangeImpl::getSelectedNode( IDOM_Node*container, int offset )
{
if ( container->getNodeType() == IDOM_Node::TEXT_NODE )
return container;
// This case is an important convenience for
// traverseRightBoundary()
if ( offset<0 )
return container;
IDOM_Node*child = container->getFirstChild();
while( child!=0 && offset > 0 )
{
--offset;
child = child->getNextSibling();
}
if ( child!=0 )
return child;
return container;
}
void IDRangeImpl::checkReadOnly(IDOM_Node* start, IDOM_Node* end,
unsigned int startOffset, unsigned int endOffset)
{
if ((start == 0) || (end == 0) ) return;
//if both start and end are text check and return
if (start->getNodeType() == IDOM_Node::TEXT_NODE) {
if (castToNodeImpl(start)->isReadOnly()) {
throw IDOM_DOMException(
IDOM_DOMException::NO_MODIFICATION_ALLOWED_ERR, 0);
}
if (start == end)
return;
}
//set the start and end nodes to check
IDOM_Node*sNode = start->getFirstChild();
for(unsigned int i = 0; i<startOffset; i++)
sNode = sNode->getNextSibling();
IDOM_Node* eNode;
if (end->getNodeType() == IDOM_Node::TEXT_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();
for (unsigned int i = 0; i<endOffset-1; i++)
eNode = eNode->getNextSibling();
}
//recursivly search if any node is readonly
recurseTreeAndCheck(sNode, eNode);
}
void IDRangeImpl::recurseTreeAndCheck(IDOM_Node* start, IDOM_Node* end)
{
for(IDOM_Node* node=start; node != 0 && node !=end; node=node->getNextSibling())
{
if (castToNodeImpl(node)->isReadOnly()) {
throw IDOM_DOMException(
IDOM_DOMException::NO_MODIFICATION_ALLOWED_ERR, 0);
}
if (node->hasChildNodes()) {
node = node->getFirstChild();
recurseTreeAndCheck(node, end);
}
}
}
IDOM_Node*IDRangeImpl::removeChild(IDOM_Node* parent, IDOM_Node* child)
{
fRemoveChild = child; //only a precaution measure not to update this range data before removal
IDOM_Node*n = parent->removeChild(child);
fRemoveChild = 0;
return n;
}
//
// Mutation functions
//
/* This function is called from DOM.
* The text has already beeen replaced.
* Fix-up any offsets.
*/
void IDRangeImpl::receiveReplacedText(IDOM_Node* node)
{
if (node == 0) return;
if (node == fStartContainer
&& fStartContainer->getNodeType() == IDOM_Node::TEXT_NODE) {
fStartOffset = 0;
}
if (node == fEndContainer
&& fEndContainer->getNodeType() == IDOM_Node::TEXT_NODE) {
fEndOffset = 0;
}
}
/** This function is called from DOM.
* The text has already beeen inserted.
* Fix-up any offsets.
*/
void IDRangeImpl::updateRangeForDeletedText(IDOM_Node* node, unsigned int offset, int count)
{
if (node == 0) return;
if (node == fStartContainer
&& fStartContainer->getNodeType() == IDOM_Node::TEXT_NODE) {
if (fStartOffset > offset+count) {
fStartOffset = fStartOffset-count;
} else if (fStartOffset > offset) {
fStartOffset = offset;
}
}
if (node == fEndContainer
&& fEndContainer->getNodeType() == IDOM_Node::TEXT_NODE) {
if (fEndOffset > offset+count) {
fEndOffset = fEndOffset-count;
} else if (fEndOffset > offset) {
fEndOffset = offset;
}
}
}
/** 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 IDRangeImpl::updateRangeForDeletedNode(IDOM_Node* node)
{
if (node == 0) return;
if (fRemoveChild == node) return;
if (node->getParentNode() == fStartContainer) {
unsigned short index = indexOf(node, fStartContainer);
if ( fStartOffset > index) {
fStartOffset--;
}
}
if (node->getParentNode() == fEndContainer) {
unsigned short index = indexOf(node, fEndContainer);
if ( fEndOffset > index) {
fEndOffset--;
}
}
if (node->getParentNode() != fStartContainer
|| node->getParentNode() != fEndContainer) {
if (isAncestorOf(node, fStartContainer)) {
IDOM_Node* tpNode = node->getParentNode();
setStartContainer( tpNode );
fStartOffset = indexOf( node, tpNode);
}
if (isAncestorOf(node, fEndContainer)) {
IDOM_Node* tpNode = node->getParentNode();
setEndContainer( tpNode );
fEndOffset = indexOf( node, tpNode);
}
}
}
void IDRangeImpl::updateRangeForInsertedNode(IDOM_Node* node) {
if (node == 0) return;
if (node->getParentNode() == fStartContainer) {
unsigned int index = indexOf(node, fStartContainer);
if (index < fStartOffset) {
fStartOffset++;
}
}
if (node->getParentNode() == fEndContainer) {
unsigned int index = indexOf(node, fEndContainer);
if (index < fEndOffset) {
fEndOffset++;
}
}
}
void IDRangeImpl::updateSplitInfo(IDOM_Text* oldNode, IDOM_Text* startNode, unsigned int offset)
{
if (startNode == 0) return;
if (fStartContainer == oldNode && fStartOffset > offset) {
fStartOffset = fStartOffset - offset;
fStartContainer = startNode;
}
if (fEndContainer == oldNode && fEndOffset > offset) {
fEndContainer = startNode;
fEndOffset = fEndOffset - offset;
}
}