You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@xerces.apache.org by tn...@locus.apache.org on 2000/12/06 20:42:50 UTC

cvs commit: xml-xerces/c/tests/DOM/RangeTest RangeTest.cpp

tng         00/12/06 11:42:49

  Modified:    c/src/dom RangeImpl.cpp RangeImpl.hpp
               c/tests/DOM/RangeTest RangeTest.cpp
  Log:
  further fixes to Range, and update RangeTest.cpp with more test coverage
  
  Revision  Changes    Path
  1.8       +616 -506  xml-xerces/c/src/dom/RangeImpl.cpp
  
  Index: RangeImpl.cpp
  ===================================================================
  RCS file: /home/cvs/xml-xerces/c/src/dom/RangeImpl.cpp,v
  retrieving revision 1.7
  retrieving revision 1.8
  diff -u -r1.7 -r1.8
  --- RangeImpl.cpp	2000/11/30 20:06:22	1.7
  +++ RangeImpl.cpp	2000/12/06 19:42:45	1.8
  @@ -55,12 +55,13 @@
    */
   
   /*
  - * $Id: RangeImpl.cpp,v 1.7 2000/11/30 20:06:22 andyh Exp $
  + * $Id: RangeImpl.cpp,v 1.8 2000/12/06 19:42:45 tng Exp $
    */
   
   #include <util/RefVectorOf.hpp>
   #include "NodeImpl.hpp"
   #include "RangeImpl.hpp"
  +#include "TextImpl.hpp"
   #include "DocumentImpl.hpp"
   #include "DOM_DOMException.hpp"
   #include "DOM_Document.hpp"
  @@ -164,27 +165,52 @@
   
   void RangeImpl::setStartContainer(const DOM_Node& node) 
   {
  +    if (fDetached)
  +    {
  +        throw DOM_DOMException(
  +            DOM_DOMException::INVALID_STATE_ERR, null);
  +    }
  +
       fStartContainer = node;
   }
   
   void RangeImpl::setStartOffset(unsigned int offset) 
   {
  +    if (fDetached)
  +    {
  +        throw DOM_DOMException(
  +            DOM_DOMException::INVALID_STATE_ERR, null);
  +    }
  +
       fStartOffset = offset;
   }
   
   void RangeImpl::setEndContainer(const DOM_Node& node) 
   {
  +    if (fDetached)
  +    {
  +        throw DOM_DOMException(
  +            DOM_DOMException::INVALID_STATE_ERR, null);
  +    }
  +
       fEndContainer = node;
       
   }
   
   void RangeImpl::setEndOffset(unsigned int offset) 
   {
  +    if (fDetached)
  +    {
  +        throw DOM_DOMException(
  +            DOM_DOMException::INVALID_STATE_ERR, null);
  +    }
  +
       fEndOffset = offset;
   }
   
   void RangeImpl::setStart(const DOM_Node& refNode, unsigned int offset)
   {
  +    validateNode(refNode);
       checkIndex(refNode, offset);
       
       fStartContainer = refNode;
  @@ -207,6 +233,7 @@
   
   void RangeImpl::setEnd(const DOM_Node& refNode, unsigned int offset)
   {
  +    validateNode(refNode);
       checkIndex(refNode, offset);
               
       fEndContainer   = refNode;
  @@ -229,7 +256,14 @@
   
   void RangeImpl::setStartBefore(const DOM_Node& refNode)
   {
  -    validateNode(refNode);
  +    if( fDetached) {
  +        throw DOM_DOMException(
  +            DOM_DOMException::INVALID_STATE_ERR, null);
  +    }
  +    if ( !hasLegalRootContainer(refNode) || !isLegalContainedNode(refNode)) {
  +        throw DOM_RangeException(
  +            DOM_RangeException::INVALID_NODE_TYPE_ERR, null);
  +    }
       
       fStartContainer = refNode.getParentNode();
      unsigned int i = 0;
  @@ -258,7 +292,14 @@
   
   void RangeImpl::setStartAfter(const DOM_Node& refNode)
   {
  -    validateNode(refNode);
  +    if( fDetached) {
  +        throw DOM_DOMException(
  +            DOM_DOMException::INVALID_STATE_ERR, null);
  +    }
  +    if ( !hasLegalRootContainer(refNode) || !isLegalContainedNode(refNode)) {
  +        throw DOM_RangeException(
  +            DOM_RangeException::INVALID_NODE_TYPE_ERR, null);
  +    }
   
       fStartContainer = refNode.getParentNode();
       unsigned int i = 0;
  @@ -285,7 +326,14 @@
   
   void RangeImpl::setEndBefore(const DOM_Node& refNode)
   {
  -    validateNode(refNode);
  +    if( fDetached) {
  +        throw DOM_DOMException(
  +            DOM_DOMException::INVALID_STATE_ERR, null);
  +    }
  +    if ( !hasLegalRootContainer(refNode) || !isLegalContainedNode(refNode)) {
  +        throw DOM_RangeException(
  +            DOM_RangeException::INVALID_NODE_TYPE_ERR, null);
  +    }
       
       fEndContainer = refNode.getParentNode();
       unsigned int i = 0;
  @@ -313,7 +361,14 @@
   
   void RangeImpl::setEndAfter(const DOM_Node& refNode)
   {
  -    validateNode(refNode);
  +    if( fDetached) {
  +        throw DOM_DOMException(
  +            DOM_DOMException::INVALID_STATE_ERR, null);
  +    }
  +    if ( !hasLegalRootContainer(refNode) || !isLegalContainedNode(refNode)) {
  +        throw DOM_RangeException(
  +            DOM_RangeException::INVALID_NODE_TYPE_ERR, null);
  +    }
       
       fEndContainer = refNode.getParentNode();
       unsigned int i = 0;
  @@ -343,6 +398,11 @@
   //-------------------------------
   void RangeImpl::detach()
   {
  +    if( fDetached) {
  +        throw DOM_DOMException(
  +            DOM_DOMException::INVALID_STATE_ERR, null);
  +    }
  +
       fDetached = true;
       
       //nullify nodes
  @@ -375,18 +435,12 @@
   void RangeImpl::selectNode(const DOM_Node& refNode)
   {
       validateNode(refNode);
  -    short type = refNode.getNodeType();
  -    if (type ==  DOM_Node::ATTRIBUTE_NODE
  -        || type == DOM_Node::ENTITY_NODE
  -        || type == DOM_Node::NOTATION_NODE
  -        || type == DOM_Node::DOCUMENT_TYPE_NODE
  -        || type == DOM_Node::DOCUMENT_FRAGMENT_NODE) {
  -
  +    if ( !isLegalContainedNode(refNode)) {
           throw DOM_RangeException(
               DOM_RangeException::INVALID_NODE_TYPE_ERR, null);
       }
       //First check for the text type node
  -    if (type ==  DOM_Node::TEXT_NODE) 
  +    if (refNode.getNodeType() ==  DOM_Node::TEXT_NODE)
       {
           //The node itself is the container.
           fStartContainer = refNode;
  @@ -417,14 +471,6 @@
   void RangeImpl::selectNodeContents(const DOM_Node& node)
   {
       validateNode(node);
  -     short type = node.getNodeType();
  -    if (type == DOM_Node::ENTITY_NODE
  -        || type == DOM_Node::NOTATION_NODE
  -        || type == DOM_Node::DOCUMENT_TYPE_NODE) {
  -
  -        throw DOM_RangeException(
  -            DOM_RangeException::INVALID_NODE_TYPE_ERR, null);
  -    }
       
       fStartContainer = node;
       fEndContainer = node;
  @@ -463,12 +509,8 @@
       }
       
       int type = newParent.getNodeType();
  -    if (type == DOM_Node::ATTRIBUTE_NODE
  -        || type == DOM_Node::ENTITY_NODE
  -        || type == DOM_Node::NOTATION_NODE
  -        || type == DOM_Node::DOCUMENT_TYPE_NODE
  -        || type == DOM_Node::DOCUMENT_NODE
  -        || type == DOM_Node::DOCUMENT_FRAGMENT_NODE)
  +    if ( !isLegalContainedNode(newParent)
  +        || type == DOM_Node::DOCUMENT_TYPE_NODE)
       {
           throw DOM_RangeException(
               DOM_RangeException::INVALID_NODE_TYPE_ERR, null);
  @@ -580,190 +622,12 @@
   
   void RangeImpl:: deleteContents()
   {
  -    if ((fStartContainer == null) || (fEndContainer == null) ) return;
  -    checkReadOnly(fStartContainer, fEndContainer, fStartOffset, fEndOffset);
  -    
  -    if( fDetached) {
  -        throw DOM_DOMException(
  -            DOM_DOMException::INVALID_STATE_ERR, null);
  -    }
  -    
  -    DOM_Node current = fStartContainer;
  -    
  -    // if same container, simplify case
  -    if (fStartContainer == fEndContainer) {
  -        if (fStartOffset == fEndOffset) { // already  collapsed
  -            return; 
  -        } 
  -        if (fStartOffset > fEndOffset)
  -            throw DOM_RangeException(DOM_RangeException::BAD_BOUNDARYPOINTS_ERR, null);
  -        
  -        if (fStartContainer.getNodeType() == DOM_Node::TEXT_NODE) {
  -            DOMString value = fStartContainer.getNodeValue();
  -            unsigned int realStart = fStartOffset;
  -            unsigned int realEnd = fEndOffset;
  -            
  -            if (fStartOffset > value.length()) realStart = value.length()-1;
  -            if (fEndOffset > value.length()) realEnd = value.length()-1;
  -            
  -            ((DOM_Text &)fStartContainer).deleteData(realStart, realEnd-realStart);
  -            
  -        } else {
  -            current = fStartContainer.getFirstChild();  
  -            unsigned int i = 0;
  -            //move till the start offset 
  -            for(i = 0; i < fStartOffset && current != null; i++) {
  -                current=current.getNextSibling();
  -            }
  -                        
  -            //delete all children after the start offset to end offset
  -            for(i = 0; i < (fEndOffset-fStartOffset) && (current != null); i++) {
  -                DOM_Node newCurrent = current.getNextSibling();
  -                removeChild(fStartContainer, current);
  -                current = newCurrent;
  -            }
  -        }
  -        collapse(true);
  -        return;
  -    }
  -    
  -    //The case of partial selections and when start container is not the same as end one
  -    bool deleteCurrent  = false;
  -    
  -     // initialize current for startContainer.
  -    if (current.getNodeType() == DOM_Node::TEXT_NODE) {
  -        ((DOM_Text &)current).deleteData(fStartOffset, current.getNodeValue().length()-fStartOffset);
  -    } else {
  -        current = current.getFirstChild();
  -        for (unsigned int i = 0 ; i < fStartOffset && current != null; i++){
  -            current = current.getNextSibling();
  -        }
  -        if (current == null) {
  -            current = fStartContainer;
  -        } else if (current != fStartContainer)
  -            deleteCurrent = true;
  -    }
  -    
  -    DOM_Node parent         = null;
  -    DOM_Node next;
  -    DOM_Node partialNode    = null;
  -    int partialInt          = START;
  -    DOM_Node startRoot      = null;
  -    
  -    DOM_Node root = getCommonAncestorContainer();
  -    // traverse up from the startContainer...
  -    // current starts as the node to delete;
  -    while (current != root && current != null) {
  -        
  -        parent = current.getParentNode();
  -        if (parent == root) {
  -            if (startRoot == null)
  -                startRoot = current;
  -        } else {
  -            if (partialNode == null) {
  -                partialNode = parent;
  -                partialInt = AFTER;
  -            }
  -        }
  -        
  -        if (parent != root) {
  -            next = current.getNextSibling();
  -            DOM_Node nextnext;
  -            while (next != null)  {
  -                nextnext = next.getNextSibling();
  -                removeChild(parent, next);
  -                next = nextnext;
  -            }
  -        }
  -        
  -        if (deleteCurrent) {
  -            removeChild(parent, current);
  -            deleteCurrent = false;
  -        }
  -        current = parent;
  -    }
  -    
  -    DOM_Node endRoot = null;
  -    // initialize current for endContainer.
  -    current = fEndContainer;
  -    if (current.getNodeType() == DOM_Node::TEXT_NODE) {
  -        ((DOM_Text &)current).deleteData(0, fEndOffset); 
  -    } else {
  -        
  -        if (fEndOffset == 0) { // "before"
  -            current = fEndContainer;
  -        }
  -        else {
  -            current = current.getFirstChild();
  -            for(unsigned int i = 1; i < fEndOffset && current != null; i++) {
  -                current=current.getNextSibling();
  -            }
  -            if (current==null) { // REVIST: index-out-of-range what to do?
  -                current = fEndContainer.getLastChild();
  -            } else 
  -                if (current != fStartContainer) {
  -                    deleteCurrent = true;
  -                }
  -                
  -        }
  -    }
  -    
  -    // traverse up from the endContainer...
  -    while (current != root && current != null) {
  -        
  -        parent = current.getParentNode();
  -        if (parent == root) {
  -            if (endRoot == null)
  -                endRoot = current;
  -        } else {
  -            if (partialNode==null) {
  -                partialNode = parent;
  -                partialInt = BEFORE;
  -            }
  -        }
  -        
  -        if (parent != root && parent != null) {
  -            next = current.getPreviousSibling();
  -            DOM_Node nextnext;
  -            while (next != null) {
  -                nextnext = next.getPreviousSibling();
  -                removeChild(parent, next);
  -                next = nextnext;
  -            }
  -        }
  -        
  -        if (deleteCurrent) {
  -            removeChild(parent, current);
  -            deleteCurrent = false;
  -        }
  -        current = parent;
  -    }
  -    
  -    //if (endRoot == null || startRoot == null) return; //REVIST
  -    current = endRoot.getPreviousSibling();
  -    DOM_Node prev = null;
  -    while (current != null && current != startRoot ) {
  -        prev = current.getPreviousSibling();
  -        parent = current.getParentNode();
  -        if (parent != null) {
  -            removeChild(parent, current);
  -        }
  -        current = prev;
  -    }
  -    
  -    if (partialNode == null) {
  -        collapse(true);
  -    } else if (partialInt == AFTER) {
  -        setStartAfter(partialNode);
  -        setEndAfter(partialNode);
  -    } else if (partialInt == BEFORE) {
  -        setStartBefore(partialNode);
  -        setEndBefore(partialNode);
  -    }
  +    traverseContents(DELETE_CONTENTS);
   }
   
   DOM_DocumentFragment RangeImpl::extractContents()
   {
  +    checkReadOnly(fStartContainer, fEndContainer, fStartOffset, fEndOffset);
       return traverseContents(EXTRACT_CONTENTS);
   }
   
  @@ -778,10 +642,12 @@
   {
       if (newNode == null) return; //don't have to do anything
   
  -    if (fStartContainer.getParentNode().fImpl->isReadOnly()) {
  +    for (DOM_Node aNode = fStartContainer; aNode!=null; aNode = aNode.getParentNode()) {
  +        if (aNode.fImpl->isReadOnly()) {
           throw DOM_DOMException(
               DOM_DOMException::NO_MODIFICATION_ALLOWED_ERR, null);
       }
  +    }
       
       if (fDocument != newNode.getOwnerDocument()) {
           throw DOM_DOMException(
  @@ -804,8 +670,7 @@
       if (type == DOM_Node::ATTRIBUTE_NODE
           || type == DOM_Node::ENTITY_NODE
           || type == DOM_Node::NOTATION_NODE
  -        || type == DOM_Node::DOCUMENT_NODE
  -        || type == DOM_Node::DOCUMENT_FRAGMENT_NODE)
  +        || type == DOM_Node::DOCUMENT_NODE)
       {
           throw DOM_RangeException(
               DOM_RangeException::INVALID_NODE_TYPE_ERR, null);
  @@ -870,43 +735,52 @@
       }
       
       DOM_Node node = fStartContainer;
  +    DOM_Node stopNode = fEndContainer;
       
       DOMString tempString;
       if ( (fStartContainer.getNodeType() == DOM_Node::TEXT_NODE)
           || (fStartContainer.getNodeType() == DOM_Node::CDATA_SECTION_NODE) ) {
           if (fStartContainer == fEndContainer) {
  -            //the substringData returns a substring from start to end all inclusive
  -            //we want a substring begins at fStartOffset inclusive, but ends at fEndOffset exclusive
  -            tempString.appendData(fStartContainer.getNodeValue().substringData(fStartOffset, fEndOffset-1));
  +            tempString.appendData(fStartContainer.getNodeValue().substringData(fStartOffset, fEndOffset-fStartOffset));
               return tempString;
           } else {
               int length = fStartContainer.getNodeValue().length();
               tempString.appendData(fStartContainer.getNodeValue().substringData(fStartOffset, length - fStartOffset));
  +            node = nextNode(node, true);
           }
  -    }else if (node == fEndContainer){
  -        DOM_Node anode = node.getFirstChild();
  -        unsigned int i = 0;
  -        for ( ;i<fStartOffset; i++)
  -            anode = anode.getNextSibling();
  -        for( ; ( i<fEndOffset || anode!=null); anode = anode.getNextSibling(), i++) {
  -            if( (anode.getNodeType() == DOM_Node::TEXT_NODE)
  -                || (anode.getNodeType() == DOM_Node::CDATA_SECTION_NODE)) {
  -                tempString.appendData(anode.getNodeValue());
  +    }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!=null) {
  +                node=node.getNextSibling();
  +                counter++;
               }
  +        }
  +        if (node == null) {
  +            node = nextNode(fStartContainer,false);
           }
  -        return tempString;
       }
  -    DOM_Node root = getCommonAncestorContainer();
       
  -    while (node != fEndContainer) {
  -         node = nextNode(node, true);
  -        if ((node == null) || (node == fEndContainer)) break;
  +    if ( fEndContainer.getNodeType()!= DOM_Node.TEXT_NODE &&
  +        fEndContainer.getNodeType()!= DOM_Node.CDATA_SECTION_NODE ){
  +        int i=fEndOffset;
  +        stopNode = fEndContainer.getFirstChild();
  +        while( i>0 && stopNode!=null ){
  +            --i;
  +            stopNode = stopNode.getNextSibling();
  +        }
  +        if ( stopNode == null )
  +            stopNode = nextNode( fEndContainer, false );
  +    }
           
  -        if (node.getNodeType() == DOM_Node::TEXT_NODE
  -            ||  node.getNodeType() == DOM_Node::CDATA_SECTION_NODE
  -            ) {
  +    while (node != stopNode) {  //look into all kids of the Range
  +        if (node == null) break;
  +        if (node.getNodeType() == DOM_Node.TEXT_NODE
  +            ||  node.getNodeType() == DOM_Node.CDATA_SECTION_NODE) {
               tempString.appendData(node.getNodeValue());
           }
  +        node = nextNode(node, true);
       }
       
       if (fEndContainer.getNodeType() == DOM_Node::TEXT_NODE
  @@ -950,6 +824,36 @@
       return false;
   }
   
  +bool RangeImpl::hasLegalRootContainer(const DOM_Node& node) const {
  +    if ( node==null )
  +        return false;
  +
  +    DOM_Node rootContainer = node;
  +    for (; rootContainer.getParentNode()!=null; rootContainer = rootContainer.getParentNode());
  +    switch( rootContainer.getNodeType() ) {
  +        case DOM_Node::ATTRIBUTE_NODE:
  +        case DOM_Node::DOCUMENT_NODE:
  +        case DOM_Node::DOCUMENT_FRAGMENT_NODE:
  +        return true;
  +    }
  +    return false;
  +}
  +
  +bool RangeImpl::isLegalContainedNode(const DOM_Node& node ) const {
  +   if ( node==null )
  +       return false;
  +   switch( node.getNodeType() )
  +   {
  +       case DOM_Node::DOCUMENT_NODE:
  +       case DOM_Node::DOCUMENT_FRAGMENT_NODE:
  +       case DOM_Node::ATTRIBUTE_NODE:
  +       case DOM_Node::ENTITY_NODE:
  +       case DOM_Node::NOTATION_NODE:
  +       return false;
  +   }
  +   return true;
  +}
  +
   unsigned short RangeImpl::indexOf(const DOM_Node& child, const DOM_Node& parent) const
   {
       unsigned short i = 0;
  @@ -1022,8 +926,6 @@
   
   void RangeImpl::checkIndex(const DOM_Node& node, unsigned int offset) const
   {
  -    validateNode(node);
  -
       if (offset < 0) {
           throw DOM_DOMException( DOM_DOMException::INDEX_SIZE_ERR, null );
       }
  @@ -1091,12 +993,12 @@
   }
   
   
  -/** This is the master traversal function which is used by
  -*  both extractContents and cloneContents().
  +/** 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.
   */
  -DOM_DocumentFragment RangeImpl::traverseContents(TraversalType trvType)
  +DOM_DocumentFragment RangeImpl::traverseContents(TraversalType how)
   {
  -
       if (fDetached) 
               throw DOM_DOMException(DOM_DOMException::INVALID_STATE_ERR, null);
       
  @@ -1104,285 +1006,500 @@
           return DOM_DocumentFragment(); // REVIST: Throw exception?
       }
       
  -    DOM_DocumentFragment frag = fDocument.createDocumentFragment() ;
  +    /* 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.
  +    */
       
  -    DOM_Node current = fStartContainer;
  -    DOM_Node cloneCurrent = null;
  -    DOM_Node cloneParent = null;
  -    DOM_Node partialNode = null;
  -    int partialInt = START;
  -    
  -    // if same container, simplify case
  -    if (fStartContainer == fEndContainer) {
  -        if (fStartOffset == fEndOffset) { // eg collapsed
  -            return frag; // REVIST: what is correct re spec?
  -        }
  -        if (fStartContainer.getNodeType() == DOM_Node::TEXT_NODE) {
  -            cloneCurrent = fStartContainer.cloneNode(false);
  -            cloneCurrent.setNodeValue(
  -                cloneCurrent.getNodeValue().substringData(fStartOffset, fEndOffset));
  -            if (trvType == EXTRACT_CONTENTS) {
  -                ((DOM_Text &)current).deleteData(fStartOffset, fEndOffset-fStartOffset);
  -            }
  -            frag.appendChild(cloneCurrent);
  -        } else {
  -            current = current.getFirstChild();
  -            unsigned int i = 0;
  -            for(i = 0; i < fStartOffset && current != null; i++) {
  -                current=current.getNextSibling();
  -            }
  -            unsigned int n = fEndOffset-fStartOffset;
  -            for(i = 0; i < n && current != null ;i++) {
  -                DOM_Node newCurrent=current.getNextSibling();
  +    // case 1: same container
  +    if ( fStartContainer == fEndContainer )
  +        return traverseSameContainer( how );
                   
  -                if (trvType == CLONE_CONTENTS) {
  -                    cloneCurrent = current.cloneNode(true);
  -                    frag.appendChild(cloneCurrent);
  -                } else
  -                    if (trvType == EXTRACT_CONTENTS) {
  -                        frag.appendChild(current);
  -                    }
  -                    current = newCurrent;
  -            }
  -        }
  -        if (trvType == EXTRACT_CONTENTS ) {
  -            collapse(true);
  -        }
  -        return frag;
  +    // case 2: Child C of start container is ancestor of end container
  +    for (DOM_Node node = fStartContainer.getFirstChild(); node != null; node=node.getNextSibling()) {
  +        if (isAncestorOf(node, fEndContainer))
  +            return traverseCommonStartContainer( node, how );
       }
       
  -    //***** END SIMPLE CASE ****
  -   
  -
  -    DOM_Node root = getCommonAncestorContainer();
  -    DOM_Node parent = null;
  -    // go up the start tree...
  -    current = fStartContainer;
  -
  -    bool endAtRoot = false;
  -    
  -    //REVIST: Always clone TEXT_NODE's?
  -    if (current.getNodeType() == DOM_Node::TEXT_NODE) {
  -        cloneCurrent = current.cloneNode(false);
  -        cloneCurrent.setNodeValue(
  -            ((DOM_Text&)cloneCurrent).getNodeValue().substringData(fStartOffset, current.getNodeValue().length() - fStartOffset));
  -        if (trvType == EXTRACT_CONTENTS) {
  -            ((DOM_Text&)current).deleteData(fStartOffset, current.getNodeValue().length()-fStartOffset);
  -        }
  -    } else {
  -        current = current.getFirstChild();
  -        for(unsigned int i = 0; i < fStartOffset && current != null; i++) {
  -            current=current.getNextSibling();
  -        }
  -        // current is now at the offset.
  -        if (current==null) { //"after"
  -            current = fStartContainer;
  +    // case 3: Child C of end container  is ancestor of start container
  +    for (DOM_Node nd = fEndContainer.getFirstChild(); nd != null; nd=nd.getNextSibling()) {
  +        if (isAncestorOf(nd, fStartContainer))
  +             return traverseCommonEndContainer( nd, how );
           }
           
  -        if (trvType == CLONE_CONTENTS) {
  -            cloneCurrent = current.cloneNode(true);
  -        } else if (trvType == EXTRACT_CONTENTS ) {
  -                cloneCurrent = current;
  -        }
  +    // case 4: preorder traversal of context tree.
  +    // There is a common ancestor container.  Find the
  +    // ancestor siblings that are children of that container.
  +    DOM_Node ancestor = commonAncestorOf(fStartContainer, fEndContainer);
  +    return traverseCommonAncestors( ancestor, ancestor, how );
       }
  -    
  -    DOM_Node startRoot = null;
  -    DOM_Node endRoot = null;
  -    parent = null;
       
  -
  -    if (root == fEndContainer) {
  -        if (fStartContainer.getParentNode() == fEndContainer) { 
  -            //a unique situation when start and end are partial under the same pass
  -            DOM_Node endNode = fEndContainer.getFirstChild();
  -            for (unsigned int i = 0; 
  -                i <= fEndOffset-2; 
  -                i++, endNode = endNode.getNextSibling()) ;
  -            
  -            if (cloneParent == null)
  -                cloneParent = root.cloneNode(false);
  -            
  -            cloneParent.appendChild(cloneCurrent); //clone the node from above
  +/**
  + * Visits the nodes selected by this range when we know
  + * a-priori that the start and end containers are the same.
  + *
  + */
  +DOM_DocumentFragment RangeImpl::traverseSameContainer( int how )
  +{
  +    DOM_DocumentFragment frag = null;
  +    if ( how!=DELETE_CONTENTS)
  +        frag = fDocument.createDocumentFragment();
               
  -            for (current= current.getNextSibling(); 
  -                current != null, current != endNode.getNextSibling(); 
  -                current=current.getNextSibling()) {
  -                if (trvType == CLONE_CONTENTS) {
  -                    cloneCurrent = current.cloneNode(true);
  -                    cloneParent.appendChild(cloneCurrent);
  -                } else if (trvType == EXTRACT_CONTENTS) {
  -                        cloneParent.appendChild(current);
  -                }
  -            }
  -            if (trvType == EXTRACT_CONTENTS) {
  -                collapse(true);
  -            }
  -            frag.appendChild(cloneParent);
  +    // If selection is empty, just return the fragment
  +    if ( fStartOffset==fEndOffset )
               return frag;
  -        }
  -    }
  -
  -    // going up in a direct line from boundary point
  -    // through parents to the common ancestor,
  -    // all these nodes are partially selected, and must
  -    // be cloned.
  -    while (current != root) {
  -        parent = current.getParentNode();
  -        
  -        if (parent == root) {
  -            cloneParent = frag;
  -            startRoot = current;
  -        } else {
  -            //check if (parent == null) case too
  -            cloneParent = parent.cloneNode(false);
  -            if (partialNode==null && parent != root) {
  -                partialNode = parent;
  -                partialInt = AFTER;
  -            }
  -            
  -        }
  -        
  -        // The children to the "right" of the "ancestor hierarchy"
  -        // are "fully-selected".
  -        DOM_Node next = null;
  -        
  -        //increment to the next sibling BEFORE doing the appendChild
  -        current = current.getNextSibling();
  -        //do this appendChild after the increment above.
  -        cloneParent.appendChild(cloneCurrent);
  -        
  -        while (current != null) {
  -            next = current.getNextSibling();
               
  -            if (current != null && parent != root) {
  -                if (trvType == CLONE_CONTENTS) {
  -                    cloneCurrent = current.cloneNode(true);
  -                    cloneParent.appendChild(cloneCurrent);
  -                } else
  -                    if (trvType == EXTRACT_CONTENTS) {
  -                        cloneParent.appendChild(current);
  -                    }
  -            }
  -            current = next;
  -        }
  -        
  -        current = parent;
  -        cloneCurrent = cloneParent;
  -    }
  -    
  -    // go up the end tree...
  -    current = fEndContainer;
  +    DOM_Node current = fStartContainer;
  +    DOM_Node cloneCurrent = null;
       
  -    if (current.getNodeType() == DOM_Node::TEXT_NODE) {
  -        cloneCurrent = current.cloneNode(false);
  +    // Text node needs special case handling
  +    if ( fStartContainer.getNodeType()== DOM_Node::TEXT_NODE )
  +    {
  +        cloneCurrent = fStartContainer.cloneNode(false);
           cloneCurrent.setNodeValue(
  -            (cloneCurrent.getNodeValue()).substringData(0,fEndOffset));
  -        if (trvType == EXTRACT_CONTENTS) {
  -            ((DOM_Text&)current).deleteData(0, fEndOffset);
  -        }
  -    } else {
  -        if (fEndOffset == 0) { // "before"
  -            current = fEndContainer;
  -        }
  -        else {
  -            current = current.getFirstChild();
  -            for(unsigned int i = 1; i < fEndOffset && current != null; i++) {
  -                current=current.getNextSibling();
  -            }
  -            if (current==null) { // REVIST: index-out-of-range what to do?
  -                current = fEndContainer.getLastChild();
  -            }
  -        }
  -        if (trvType == CLONE_CONTENTS) {
  -            cloneCurrent = current.cloneNode(true);
  -        } else
  -            if (trvType == EXTRACT_CONTENTS ) {
  -                cloneCurrent = current;
  -            }
  -    }
  -    
  -    while (current != root && current != null) {
  -        parent = current.getParentNode();
  -        if (parent == root) {
  -            cloneParent = frag;
  -            endRoot = current;
  -        } else {
  -            cloneParent = parent.cloneNode(false);
  -            if (partialNode==null && parent != root) {
  -                partialNode = parent;
  -                partialInt = BEFORE;
  -            }
  -        }
  -        
  -        DOM_Node holdCurrent = current;
  -        
  -        current = parent.getFirstChild();
  -        
  -        cloneParent.appendChild(cloneCurrent);
  +            cloneCurrent.getNodeValue().substringData(fStartOffset, fEndOffset));
           
  -        DOM_Node next = null;
  -        while (current != holdCurrent && current != null) {
  -            next = current.getNextSibling();
  -            // The leftmost children are fully-selected
  -            // and are removed, and appended, not cloned.
  -            if (current != null && parent != root) {
  -                if (trvType == CLONE_CONTENTS) {
  -                    cloneCurrent = current.cloneNode(true);
  -                    cloneParent.appendChild(cloneCurrent);
  -                } else
  -                    if (trvType == EXTRACT_CONTENTS) {
  -                        //cloneCurrent = current;
  -                        cloneParent.appendChild(current);
  -                    }
  -            }
  -            current = next;
  -        }
  -        
  -        current = parent;
  -        cloneCurrent = cloneParent;
  -        
  +        // set the original text node to its new value
  +        if ( how != CLONE_CONTENTS )
  +            ((DOM_Text &)fStartContainer).deleteData(fStartOffset, fEndOffset-fStartOffset);
  +        if ( how != DELETE_CONTENTS)
  +            frag.appendChild(cloneCurrent);
       }
  -    
  -    // traverse the "fully-selected" middle...
  -    DOM_Node clonedPrevious = frag.getLastChild();
  -    current = endRoot.getPreviousSibling();
  -    DOM_Node prev = null;
  -    while (current != startRoot && current != null) {
  -        prev = current.getPreviousSibling();
  -        
  -        if (trvType == CLONE_CONTENTS) {
  -            cloneCurrent = current.cloneNode(true);
  -        } else
  -            if (trvType == EXTRACT_CONTENTS) {
  -                cloneCurrent = current;
  +    else {
  +        // Copy nodes between the start/end offsets.
  +        DOM_Node n = getSelectedNode( fStartContainer, fStartOffset );
  +        int cnt = fEndOffset - fStartOffset;
  +        while( cnt > 0 )
  +        {
  +            DOM_Node sibling = n.getNextSibling();
  +            DOM_Node xferNode = traverseFullySelected( n, how );
  +            if ( frag!=null )
  +                frag.appendChild( xferNode );
  +            --cnt;
  +            n = sibling;
               }
  -            
  -            frag.insertBefore(cloneCurrent, clonedPrevious);
  -            
  -            current = prev;
  -            clonedPrevious = cloneCurrent;
       }
       
  -    // collapse the range...
  -    if (trvType == EXTRACT_CONTENTS ) {
  -        if (partialNode == null) {
  +    // Nothing is partially selected, so collapse to start point
  +    if ( how != CLONE_CONTENTS )
               collapse(true);
  -        } else
  -            if (partialInt == AFTER) {
  -                setStartAfter(partialNode);
  -                setEndAfter(partialNode);
  -            }
  -            else if (partialInt == BEFORE) {
  -                setStartBefore(partialNode);
  -                setEndBefore(partialNode);
  -            }
  +    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
  + *
  + */
  +DOM_DocumentFragment RangeImpl::traverseCommonStartContainer( DOM_Node endAncestor, int how )
  +{
  +    DOM_DocumentFragment frag = null;
  +    if ( how!=DELETE_CONTENTS)
  +        frag = fDocument.createDocumentFragment();
  +    DOM_Node n = traverseRightBoundary( endAncestor, how );
  +    if ( frag!=null )
  +        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 )
  +    {
  +        DOM_Node sibling = n.getPreviousSibling();
  +        DOM_Node xferNode = traverseFullySelected( n, how );
  +        if ( frag!=null )
  +            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;
   }
   
  -void RangeImpl::checkReadOnly(DOM_Node& start, DOM_Node& end, 
  +/**
  + * 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
  + *
  + */
  +DOM_DocumentFragment RangeImpl::traverseCommonEndContainer( DOM_Node startAncestor, int how )
  +{
  +    DOM_DocumentFragment frag = null;
  +    if ( how!=DELETE_CONTENTS)
  +        frag = fDocument.createDocumentFragment();
  +    DOM_Node n = traverseLeftBoundary( startAncestor, how );
  +    if ( frag!=null )
  +        frag.appendChild( n );
  +    int startIdx = indexOf( startAncestor, fEndContainer );
  +    ++startIdx;  // Because we already traversed it....
  +
  +    int cnt = fEndOffset - startIdx;
  +    n = startAncestor.getNextSibling();
  +    while( cnt > 0 )
  +    {
  +        DOM_Node sibling = n.getNextSibling();
  +        DOM_Node xferNode = traverseFullySelected( n, how );
  +        if ( frag!=null )
  +            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.
  + */
  +DOM_DocumentFragment RangeImpl::traverseCommonAncestors( DOM_Node startAncestor, DOM_Node endAncestor, int how )
  +{
  +    DOM_DocumentFragment frag = null;
  +    if ( how!=DELETE_CONTENTS)
  +        frag = fDocument.createDocumentFragment();
  +
  +    DOM_Node n = traverseLeftBoundary( startAncestor, how );
  +    if ( frag!=null )
  +        frag.appendChild( n );
  +
  +    DOM_Node commonParent = startAncestor.getParentNode();
  +    int startOffset = indexOf( startAncestor, commonParent );
  +    int endOffset = indexOf( endAncestor, commonParent );
  +    ++startOffset;
  +
  +    int cnt = endOffset - startOffset;
  +    DOM_Node sibling = startAncestor.getNextSibling();
  +
  +    while( cnt > 0 )
  +    {
  +        DOM_Node nextSibling = sibling.getNextSibling();
  +        n = traverseFullySelected( sibling, how );
  +        if ( frag!=null )
  +            frag.appendChild( n );
  +        sibling = nextSibling;
  +        --cnt;
  +    }
  +
  +    n = traverseRightBoundary( endAncestor, how );
  +    if ( frag!=null )
  +        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.
  + *
  + */
  +DOM_Node RangeImpl::traverseRightBoundary( DOM_Node root, int how )
  +{
  +    DOM_Node next = getSelectedNode( fEndContainer, fEndOffset-1 );
  +    bool isFullySelected = ( next!=fEndContainer );
  +
  +    if ( next==root )
  +        return traverseNode( next, isFullySelected, false, how );
  +
  +    DOM_Node parent = next.getParentNode();
  +    DOM_Node clonedParent = traverseNode( parent, false, false, how );
  +
  +    while( parent!=null )
  +    {
  +        while( next!=null )
  +        {
  +            DOM_Node prevSibling = next.getPreviousSibling();
  +            DOM_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();
  +        DOM_Node clonedGrandParent = traverseNode( parent, false, false, how );
  +        if ( how!=DELETE_CONTENTS )
  +            clonedGrandParent.appendChild( clonedParent );
  +        clonedParent = clonedGrandParent;
  +
  +    }
  +
  +    // should never occur
  +    return null;
  +}
  +
  +/**
  + * 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.
  + *
  + */
  +DOM_Node RangeImpl::traverseLeftBoundary( DOM_Node root, int how )
  +{
  +    DOM_Node next = getSelectedNode( getStartContainer(), getStartOffset() );
  +    bool isFullySelected = ( next!=getStartContainer() );
  +
  +    if ( next==root )
  +        return traverseNode( next, isFullySelected, true, how );
  +
  +    DOM_Node parent = next.getParentNode();
  +    DOM_Node clonedParent = traverseNode( parent, false, true, how );
  +
  +    while( parent!=null )
  +    {
  +        while( next!=null )
  +        {
  +            DOM_Node nextSibling = next.getNextSibling();
  +            DOM_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();
  +        DOM_Node clonedGrandParent = traverseNode( parent, false, true, how );
  +        if ( how!=DELETE_CONTENTS )
  +            clonedGrandParent.appendChild( clonedParent );
  +        clonedParent = clonedGrandParent;
  +
  +    }
  +
  +    // should never occur
  +    return null;
  +
  +}
  +
  +/**
  + * 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.
  + *
  + */
  +DOM_Node RangeImpl::traverseNode( DOM_Node n, bool isFullySelected, bool isLeft, int how )
  +{
  +    if ( isFullySelected )
  +        return traverseFullySelected( n, how );
  +    if ( n.getNodeType()== DOM_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.
  + *
  + */
  +DOM_Node RangeImpl::traverseFullySelected( DOM_Node n, int how )
  +{
  +    switch( how )
  +    {
  +    case CLONE_CONTENTS:
  +        return n.cloneNode( true );
  +    case EXTRACT_CONTENTS:
  +        if ( n.getNodeType()== DOM_Node::DOCUMENT_TYPE_NODE )
  +        {
  +            throw DOM_DOMException(
  +                DOM_DOMException::HIERARCHY_REQUEST_ERR, null);
  +        }
  +        return n;
  +    case DELETE_CONTENTS:
  +        n.getParentNode().removeChild(n);
  +        return null;
  +    }
  +    return null;
  +}
  +
  +/**
  + * Utility method for traversing a single node when
  + * we know a-priori that the node if partially
  + * selected and is not a text node.
  + *
  + */
  +DOM_Node RangeImpl::traversePartiallySelected( DOM_Node n, int how )
  +{
  +    switch( how )
  +    {
  +    case DELETE_CONTENTS:
  +        return null;
  +    case CLONE_CONTENTS:
  +    case EXTRACT_CONTENTS:
  +        return n.cloneNode( false );
  +    }
  +    return null;
  +}
  +
  +/**
  + * 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.
  + *
  + */
  +DOM_Node RangeImpl::traverseTextNode( DOM_Node n, bool isLeft, int how )
  +{
  +    DOMString txtValue = n.getNodeValue();
  +    DOMString newNodeValue;
  +    DOMString oldNodeValue;
  +
  +    if ( isLeft )
  +    {
  +        int offset = getStartOffset();
  +        newNodeValue = txtValue.substringData( offset , fStartContainer.getNodeValue().length());
  +        oldNodeValue = txtValue.substringData( 0, offset );
  +    }
  +    else
  +    {
  +        int offset = getEndOffset();
  +        newNodeValue = txtValue.substringData( 0, offset );
  +        oldNodeValue = txtValue.substringData( offset , fEndContainer.getNodeValue().length() );
  +    }
  +
  +    if ( how != CLONE_CONTENTS )
  +        n.setNodeValue( oldNodeValue );
  +    if ( how==DELETE_CONTENTS )
  +        return null;
  +    DOM_Node newNode = n.cloneNode( false );
  +    newNode.setNodeValue( 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.
  + *
  + */
  +DOM_Node RangeImpl::getSelectedNode( DOM_Node container, int offset )
  +{
  +    if ( container.getNodeType() == DOM_Node::TEXT_NODE )
  +        return container;
  +
  +    // This case is an important convenience for
  +    // traverseRightBoundary()
  +    if ( offset<0 )
  +        return container;
  +
  +    DOM_Node child = container.getFirstChild();
  +    while( child!=null && offset > 0 )
  +    {
  +        --offset;
  +        child = child.getNextSibling();
  +    }
  +    if ( child!=null )
  +        return child;
  +    return container;
  +}
  +
  +void RangeImpl::checkReadOnly(DOM_Node& start, DOM_Node& end,
                                 unsigned int startOffset, unsigned int endOffset)
   {
       if ((start == null) || (end == null) ) return;
  @@ -1399,7 +1516,7 @@
       DOM_Node sNode = start.getFirstChild();
       for(unsigned int i = 0; i<startOffset; i++)
           sNode = sNode.getNextSibling();
  -    
  +
       DOM_Node eNode;
       if (end.getNodeType() == DOM_Node::TEXT_NODE) {
           eNode = end; //need to check only till this node
  @@ -1415,7 +1532,7 @@
   
   void RangeImpl::recurseTreeAndCheck(DOM_Node& start, DOM_Node& end)
   {
  -    for(DOM_Node node=start; node != null, node !=end; node=node.getNextSibling()) 
  +    for(DOM_Node node=start; node != null && node !=end; node=node.getNextSibling())
       {
           if (node.fImpl->isReadOnly()) {
               throw DOM_DOMException(
  @@ -1512,26 +1629,22 @@
       
       if (node->getParentNode() == fEndContainer.fImpl) {
           unsigned short index = indexOf(tNode, fEndContainer);
  -        if ( fEndOffset < index) {
  +        if ( fEndOffset > index) {
               fEndOffset--;
           }
       }
       
       if (node->getParentNode() != fStartContainer.fImpl
  -        &&  node->getParentNode() != fEndContainer.fImpl) {
  +        ||  node->getParentNode() != fEndContainer.fImpl) {
           if (isAncestorOf(node, fStartContainer)) {
  -            if (( node->getParentNode()->getNodeType() == DOM_Node::DOCUMENT_FRAGMENT_NODE) )
  -                return; //if the node's up in the heirarchy and its parent is doc-frag ignore
               DOM_Node tpNode(node->getParentNode());
               setStartContainer( tpNode );
  -            fStartOffset = indexOf( tNode, tpNode)-1;
  +            fStartOffset = indexOf( tNode, tpNode);
           }
           if (isAncestorOf(node, fEndContainer)) {
  -            if (( node->getParentNode()->getNodeType() == DOM_Node::DOCUMENT_FRAGMENT_NODE))
  -                return;
               DOM_Node tpNode(node->getParentNode());
               setEndContainer( tpNode );
  -            fEndOffset = indexOf( tNode, tpNode)-1;
  +            fEndOffset = indexOf( tNode, tpNode);
           }
       }
       
  @@ -1541,7 +1654,7 @@
       if (node == null) return;
       
       if (node->getParentNode() == fStartContainer.fImpl) {
  -        unsigned int index = indexOf(DOM_Node(node), fStartContainer) -1;
  +        unsigned int index = indexOf(DOM_Node(node), fStartContainer);
           if (index < fStartOffset) {
               fStartOffset++;
           }
  @@ -1549,10 +1662,7 @@
       
       if (node->getParentNode() == fEndContainer.fImpl) {
           unsigned int index = indexOf(DOM_Node(node), fEndContainer);
  -        //if index is equal then the text is inserted before the end of 
  -        //range so should get included in the range
  -        
  -        if (index <= fEndOffset) {
  +        if (index < fEndOffset) {
               fEndOffset++;
           }
       }
  
  
  
  1.4       +17 -2     xml-xerces/c/src/dom/RangeImpl.hpp
  
  Index: RangeImpl.hpp
  ===================================================================
  RCS file: /home/cvs/xml-xerces/c/src/dom/RangeImpl.hpp,v
  retrieving revision 1.3
  retrieving revision 1.4
  diff -u -r1.3 -r1.4
  --- RangeImpl.hpp	2000/11/30 20:06:23	1.3
  +++ RangeImpl.hpp	2000/12/06 19:42:46	1.4
  @@ -57,7 +57,7 @@
    */
   
    /*
  - * $Id: RangeImpl.hpp,v 1.3 2000/11/30 20:06:23 andyh Exp $
  + * $Id: RangeImpl.hpp,v 1.4 2000/12/06 19:42:46 tng Exp $
    */
   
   
  @@ -133,7 +133,8 @@
   private:
       enum TraversalType {
           EXTRACT_CONTENTS = 1,
  -        CLONE_CONTENTS   = 2
  +        CLONE_CONTENTS   = 2,
  +        DELETE_CONTENTS  = 3
       };
   
       enum TraversePoint {
  @@ -151,6 +152,8 @@
       //misc functions
       void        validateNode(const DOM_Node& node) const;
       bool        isValidAncestorType(const DOM_Node& node) const; 
  +    bool        hasLegalRootContainer(const DOM_Node& node) const;
  +    bool        isLegalContainedNode(const DOM_Node& node ) const;
       void        checkIndex(const DOM_Node& node, unsigned int offset) const;
       static bool isAncestorOf(const DOM_Node& a, const DOM_Node& b);
       
  @@ -163,6 +166,18 @@
                       unsigned int starOffset, unsigned int endOffset);
       void        recurseTreeAndCheck(DOM_Node& start, DOM_Node& end);
       DOM_Node    removeChild(DOM_Node& parent, DOM_Node& child);
  +
  +    DOM_DocumentFragment traverseSameContainer( int how );
  +    DOM_DocumentFragment traverseCommonStartContainer( DOM_Node endAncestor, int how );
  +    DOM_DocumentFragment traverseCommonEndContainer( DOM_Node startAncestor, int how );
  +    DOM_DocumentFragment traverseCommonAncestors( DOM_Node startAncestor, DOM_Node endAncestor, int how );
  +    DOM_Node    traverseRightBoundary( DOM_Node root, int how );
  +    DOM_Node    traverseLeftBoundary( DOM_Node root, int how );
  +    DOM_Node    traverseNode( DOM_Node n, bool isFullySelected, bool isLeft, int how );
  +    DOM_Node    traverseFullySelected( DOM_Node n, int how );
  +    DOM_Node    traversePartiallySelected( DOM_Node n, int how );
  +    DOM_Node    traverseTextNode( DOM_Node n, bool isLeft, int how );
  +    DOM_Node    getSelectedNode( DOM_Node container, int offset );
   
      
       //private data 
  
  
  
  1.2       +512 -38   xml-xerces/c/tests/DOM/RangeTest/RangeTest.cpp
  
  Index: RangeTest.cpp
  ===================================================================
  RCS file: /home/cvs/xml-xerces/c/tests/DOM/RangeTest/RangeTest.cpp,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- RangeTest.cpp	2000/07/28 01:33:39	1.1
  +++ RangeTest.cpp	2000/12/06 19:42:48	1.2
  @@ -57,7 +57,7 @@
   
   
   /**
  - * $Id: RangeTest.cpp,v 1.1 2000/07/28 01:33:39 aruna1 Exp $
  + * $Id: RangeTest.cpp,v 1.2 2000/12/06 19:42:48 tng Exp $
    */
   
   
  @@ -151,6 +151,7 @@
       extractContents
       toString
       detach
  +    removeChild
       */  
       {
           
  @@ -223,13 +224,13 @@
        
               //Tests start here
               // Initial dom tree looks like :
  -            // <Body><H1>Title</H1><P>Blah xyz</P>
  -            //i.e.,            Body
  -            //     _____________|______________
  +            // <Body><H1>TitleAnother Text</H1><P>Blah xyz</P></Body>
  +            //i.e.,            Body(rt)
  +            //     _____________|________________
               //     |                           |
  -            //     H1                          P
  -            //     |                           |
  -            //    "Title"                    "Blah xyz"
  +            //  ___H1(E11)___                    P(E12)
  +            //  |           |                    |
  +            //  "Title"  "Another Text"        "Blah xyz"
   
     
               //test for start and end settings of a range
  @@ -275,6 +276,17 @@
               TASSERT(range.getEndContainer() == rt);
               TASSERT(range.getEndOffset() == 5);
   
  +            //After above operations, now the tree looks like:
  +            // <Body><Element3/><H1>TitleAnother Text</H1><Element2/><Element1/><P>Blah xyz</P></Body>
  +            //i.e.,            Body(rt)
  +            //     _____________|_______________________________________________________________
  +            //     |                |                  |                |                      |
  +            //  Element3(E122)  ___H1(E11)___        Element2(E121)    Element1(E120)          P(E12)
  +            //                  |           |                                                  |
  +            //               "Title"  "Another Text"                                        "Blah xyz"
  +            //
  +            // range has rt as start and end container, and 2 as start offset, 5 as end offset
  +
               //changing selection
               range.selectNode(rt.getLastChild().getPreviousSibling());
               TASSERT(range.getStartContainer() == rt);
  @@ -288,42 +300,86 @@
   
               range.setStart(rt.getFirstChild().getNextSibling().getFirstChild(), 2);
               TASSERT(range.getStartContainer() == rt.getFirstChild().getNextSibling().getFirstChild());
  +            TASSERT(range.getStartContainer().getNodeValue().equals("Title"));
               TASSERT(range.getStartOffset() == 2);
             
               range.setEnd(rt.getFirstChild().getNextSibling().getFirstChild(), 4);
               TASSERT(range.getEndContainer() == rt.getFirstChild().getNextSibling().getFirstChild());
  +            TASSERT(range.getEndContainer().getNodeValue().equals("Title"));
               TASSERT(range.getEndOffset() == 4);
  +            TASSERT(range.toString().equals("tl"));
   
               //inserting text between a text node
               range.insertNode(E210);
   
               //only end offset moves and new node gets into range as being inserted at boundary point
  -            TASSERT(range.getStartContainer() == rt.getFirstChild().getNextSibling().getLastChild().getPreviousSibling());
  -            TASSERT(range.getStartOffset() == 0);
  +            TASSERT(range.getStartContainer() == rt.getFirstChild().getNextSibling().getFirstChild());
  +            TASSERT(range.getStartOffset() == 2);
               TASSERT(range.getEndContainer() == rt.getFirstChild().getNextSibling().getLastChild().getPreviousSibling());
               TASSERT(range.getEndOffset() == 2);
   
  -            //inserting element node before the selcted text node
  +            //inserting element node before the selected text node
               range.insertNode(E120);
               //only end offset moves and new node gets into range as being inserted at boundary point
  -            TASSERT(range.getStartContainer() == rt.getFirstChild().getNextSibling().getLastChild().getPreviousSibling());
  -            TASSERT(range.getStartOffset() == 0);
  +            TASSERT(range.getStartContainer() == rt.getFirstChild().getNextSibling().getFirstChild());
  +            TASSERT(range.getStartContainer().getNodeValue().equals("Ti"));
  +            TASSERT(range.getStartOffset() == 2);
               TASSERT(range.getEndContainer() == rt.getFirstChild().getNextSibling().getLastChild().getPreviousSibling());
  +            TASSERT(range.getEndContainer().getNodeValue().equals("tle"));
               TASSERT(range.getEndOffset() == 2);
  +            TASSERT(E11.getChildNodes().getLength()==6);
   
              //checking the text replacment
               range.getStartContainer().setNodeValue("ReplacedText");
  -            //collapsed
  -            TASSERT(range.getCollapsed() == true);
  -
  -            //the offsets are set to 0
  +            //only the start offset is impact
  +            TASSERT(range.getStartContainer() == rt.getFirstChild().getNextSibling().getFirstChild());
  +            TASSERT(range.getStartContainer().getNodeValue().equals("ReplacedText"));
               TASSERT(range.getStartOffset() == 0);
  -            TASSERT(range.getEndOffset() == 0);
  +            TASSERT(range.getEndContainer() == rt.getFirstChild().getNextSibling().getLastChild().getPreviousSibling());
  +            TASSERT(range.getEndContainer().getNodeValue().equals("tle"));
  +            TASSERT(range.getEndOffset() == 2);
  +
  +            //After above operations, now the tree looks like:
  +            // <Body><Element3/><H1>ReplacedText<Element1/>insertedTexttleAnother Text</H1><Element2/><P>Blah xyz</P></Body>
  +            //i.e.,            Body(rt)
  +            //     _____________|_______________________________________________________________________________________________
  +            //     |                |                                                                          |                |
  +            //  Element3(E122)  ___H1(E11)___________________________________________________________        Element2(E121)    P(E12)
  +            //                  |              |     |                |                      |      |                             |
  +            //               "ReplacedText"   ""   Element1(E120)   "insertedText"(E210)   "tle"  "Another Text"              "Blah xyz"
  +            //
  +            // range has "ReplacedText" as start container and "tle" as end container
  +            //   and 0 as start offset, 2 as end offset
          
               //changing the selection. Preparing for 'surround'
               range.setStart(range.getStartContainer().getParentNode(), 2);
               range.setEnd(range.getStartContainer(), 5);
  +            TASSERT(range.getStartContainer().getNodeName().equals("H1"));
  +            TASSERT(range.getEndContainer().getNodeName().equals("H1"));
  +            TASSERT(range.toString().equals("insertedTexttle"));
  +
               range.surroundContents(E311);          
  +            TASSERT(range.getStartContainer().getNodeName().equals("H1"));
  +            TASSERT(range.getStartOffset() == 2);
  +            TASSERT(range.getEndContainer().getNodeName().equals("H1"));
  +            TASSERT(range.getEndOffset() == 3);
  +            TASSERT(E11.getChildNodes().getLength()==4);
  +            TASSERT(E311.getChildNodes().getLength()==3);
  +            TASSERT(range.toString().equals("insertedTexttle"));
  +
  +            //After above operations, now the tree looks like:
  +            // <Body><Element3/><H1>ReplacedText<SurroundNode1><Element1/>insertedTexttle</SurroundNode1>Another Text</H1><Element2/><P>Blah xyz</P></Body>
  +            //i.e.,            Body(rt)
  +            //     _____________|_________________________________________________________________________
  +            //     |                |                                                    |                |
  +            //  Element3(E122)  ___H1(E11)___________________________________        Element2(E121)    P(E12)
  +            //                  |              |     |                      |                            |
  +            //               "ReplacedText"   ""   SurroundNode1(E311)  "Another Text"              "Blah xyz"
  +            //                          ____________ |_____________________________
  +            //                          |                    |                    |
  +            //                          Element1(E120)   "insertedText"(E210)   "tle"
  +            //
  +            // range has H1 as start and end container and 2 as start offset, 3 as end offset
                    
               //testing cloning            
               DOM_Range aRange = range.cloneRange();
  @@ -339,11 +395,11 @@
               short compVal = range.compareBoundaryPoints(DOM_Range::END_TO_END, aRange);
               TASSERT(compVal == 0); 
               compVal = range.compareBoundaryPoints(DOM_Range::START_TO_START, aRange);
  -            TASSERT(compVal == -1);
  +            TASSERT(compVal == 1);
               compVal = range.compareBoundaryPoints(DOM_Range::START_TO_END, aRange);
  -            TASSERT(compVal == -1);
  -            compVal = range.compareBoundaryPoints(DOM_Range::END_TO_START, aRange);
               TASSERT(compVal == 1);
  +            compVal = range.compareBoundaryPoints(DOM_Range::END_TO_START, aRange);
  +            TASSERT(compVal == -1);
   
               //testing collapse
               //not collapsed
  @@ -351,46 +407,50 @@
               TASSERT(range.getStartOffset() == 2);
               TASSERT(range.getEndOffset() == 3);
   
  -            
               //selectNodeContents 
               range.selectNodeContents(rt.getLastChild().getFirstChild());
               TASSERT(range.getStartContainer() == rt.getLastChild().getFirstChild());
               TASSERT(range.getEndContainer() == rt.getLastChild().getFirstChild());
               TASSERT(range.getStartOffset() == 0);
               TASSERT(range.getEndOffset() == 8);
  +            TASSERT(range.toString().equals("Blah xyz"));
               
               //testing collapse
               range.collapse(true); //collapse to start
               TASSERT(range.getCollapsed() == true);
               TASSERT(range.getStartOffset() == 0);
               TASSERT(range.getEndOffset() == 0);
  +            TASSERT(range.toString().equals(""));
               TASSERT(aRange.getEndOffset() == 3); //other range is unaffected
  +            TASSERT(aRange.toString().equals("eplacedTextinsertedTexttle"));
              
  -
  -            // DOM Tree now looks like this
  -            //          <Body>
  -            //  |---------|----------|--------------|
  -            //  Element3  H1      Element2          P
  -            //            |                         |
  -            //  |------|-----------|             "Blah xyz"
  -            // "Ti" "insertedText" SurroundNode     
  -            //                     |
  -            //        |--------|---------------|
  -            //        Element1 "ReplacedText   "AnotherText"
  -
  -            // range has H1 as start and end container and 2 as start and end offset. in collapsed state
  -            // aRange has "Ti" as start with 1 as start offset, H1 as end and 3 as end offset
  -
  -
  +            //After above operations, now the tree looks like:
  +            // <Body><Element3/><H1>ReplacedText<SurroundNode1><Element1/>insertedTexttle</SurroundNode1>Another Text</H1><Element2/><P>Blah xyz</P></Body>
  +            //i.e.,            Body(rt)
  +            //     _____________|_________________________________________________________________________
  +            //     |                |                                                    |                |
  +            //  Element3(E122)  ___H1(E11)___________________________________        Element2(E121)    P(E12)
  +            //                  |              |     |                      |                            |
  +            //               "ReplacedText"   ""   SurroundNode1(E311)  "Another Text"              "Blah xyz"
  +            //                          ____________ |_____________________________
  +            //                          |                    |                    |
  +            //                          Element1(E120)   "insertedText"(E210)   "tle"
  +            //
  +            // range has "Blah xyz" as start and end container and 0 as start and end offset (collapsed)
  +            // aRange has "ReplacedText" as start container and H1 as end container
  +            //    and 1 as start offset and 3 as end offset
          
               DOM_DocumentFragment docFrag = aRange.cloneContents();
               TASSERT( docFrag != 0);
               range.selectNode(rt.getFirstChild());
               TASSERT(range.getStartContainer() == rt); 
  +            TASSERT(range.getEndContainer() == rt);
  +            TASSERT(range.getStartOffset() == 0);
  +            TASSERT(range.getEndOffset() == 1);
               
               //Testing toString()
               const char* str = aRange.toString().transcode();
  -            char* str2 = "iinsertedTextReplacedTextAnotherText";
  +            char* str2 = "eplacedTextinsertedTexttle";
               TASSERT(*str == *str2);
   
               //start and end before and after tests
  @@ -413,10 +473,424 @@
               //testing extract()
               DOM_DocumentFragment frag2 = range.extractContents();
               TASSERT( frag2 != 0);
  +
  +            //After above operations, now the tree looks like:
  +            // <Body><Element3/></Body>
  +            //i.e.,            Body(rt)
  +            //                  |
  +            //               Element3(E122)
  +            //
  +            // aRange has rt as start and end container, and 1 as start and end offset (collapsed)
  +            // range has rt as start and end container, and 1 as start and end offset (collapsed)
  +            //
  +            //and frag2 looks:
  +            // <Body>ReplacedText<SurroundNode1><Element1/>insertedTexttle</SurroundNode1>Another Text</H1><Element2/><P>Blah xyz</P></Body>
  +            //i.e.,             Body(rt)
  +            //      ______________|________________________________________________________
  +            //      |                                                    |                |
  +            //   ___H1(E11)___________________________________        Element2(E121)    P(E12)
  +            //   |              |     |                      |                            |
  +            //"ReplacedText"   ""   SurroundNode1(E311)  "Another Text"              "Blah xyz"
  +            //           ____________ |_____________________________
  +            //           |                    |                    |
  +            //        Element1(E120)   "insertedText"(E210)   "tle"
  +            //
  +
  +            //the tree do not have those node anymore after extract
  +            //only Element3 left
  +            TASSERT(rt.getChildNodes().getLength()==1);
  +
  +            //aRange is collapsed
  +            TASSERT(aRange.getCollapsed() == true);
  +            TASSERT(aRange.getStartContainer() == rt);
  +            TASSERT(aRange.getStartOffset() == 1);
  +            TASSERT(aRange.getEndContainer() == rt);
  +            TASSERT(aRange.getEndOffset() == 1);
  +
  +            //range is collapsed as well
  +            TASSERT(range.getCollapsed() == true);
  +            TASSERT(range.getStartContainer() == rt);
  +            TASSERT(range.getStartOffset() == 1);
  +            TASSERT(range.getEndContainer() == rt);
  +            TASSERT(range.getEndOffset() == 1);
  +
  +            //test the document fragment frag2
  +            TASSERT(frag2.getChildNodes().getLength()==3);
  +
               //detaching the other range
               aRange.detach();
               range.detach();
   
  +           //***************************************************************
  +           //another set of test
  +           //TEST createRange, setStart and setEnd, insertnode
  +           //***************************************************************
  +           DOM_Document doc2 = DOM_Document::createDocument();
  +           DOM_Element root2 = doc2.createElement("root2");
  +           doc2.appendChild(root2);
  +           //case 1: simple text node, start==end
  +           // <body>text1</body>
  +           DOM_Element body = doc2.createElement("body");
  +           DOM_Text text1 = doc2.createTextNode("text1");
  +           body.appendChild(text1);
  +           root2.appendChild(body);
  +
  +           //set range
  +           DOM_Range range1 = doc2.createRange();
  +           range1.setStart(text1,1);
  +           range1.setEnd(text1,3);
  +
  +           TASSERT(range1.toString().equals("ex"));
  +           TASSERT(range1.getStartOffset()==1);
  +           TASSERT(range1.getStartContainer().getNodeValue().equals("text1"));
  +           TASSERT(range1.getEndOffset()==3);
  +           TASSERT(range1.getEndContainer().getNodeValue().equals("text1"));
  +
  +           //now insert a text node
  +           //<body>ttext2ext1</body>
  +           DOM_Text text2 = doc2.createTextNode("text2");
  +           range1.insertNode(text2);
  +
  +           TASSERT(range1.toString().equals("text2ex"));
  +           TASSERT(range1.getStartOffset()==1);
  +           TASSERT(range1.getStartContainer().getNodeValue().equals("t"));
  +           TASSERT(range1.getEndOffset()==2);
  +           TASSERT(range1.getEndContainer().getNodeValue().equals("ext1"));
  +
  +           //now insert a non-text node
  +           //<body>t<p1/>text2ext1</body>
  +           DOM_Element p1 = doc2.createElement("p1");
  +           range1.insertNode(p1);
  +
  +           TASSERT(range1.toString().equals("text2ex"));
  +           TASSERT(range1.getStartOffset()==1);
  +           TASSERT(range1.getStartContainer().getNodeValue().equals("t"));
  +           TASSERT(range1.getEndOffset()==2);
  +           TASSERT(range1.getEndContainer().getNodeValue().equals("ext1"));
  +
  +           //case 2: non-text node, start==end
  +           // <head><h1/></head>
  +           DOM_Element head = doc2.createElement("head");
  +           DOM_Element h1 = doc2.createElement("h1");
  +           head.appendChild(h1);
  +           root2.appendChild(head);
  +
  +           //set range
  +           DOM_Range range2 = doc2.createRange();
  +           range2.setStart(head,0);
  +           range2.setEnd(head,1);
  +
  +           TASSERT(range2.toString().equals(""));
  +           TASSERT(range2.getStartOffset()==0);
  +           TASSERT(range2.getStartContainer().getNodeName().equals("head"));
  +           TASSERT(range2.getEndOffset()==1);
  +           TASSERT(range2.getEndContainer().getNodeName().equals("head"));
  +
  +           //now insert a non-text node
  +           //<head><h2/><h1/></head>
  +           DOM_Element h2 = doc2.createElement("h2");
  +           range2.insertNode(h2);
  +
  +           TASSERT(range2.toString().equals(""));
  +           TASSERT(range2.getStartOffset()==0);
  +           TASSERT(range2.getStartContainer().getNodeName().equals("head"));
  +           TASSERT(range2.getEndOffset()==2);
  +           TASSERT(range2.getEndContainer().getNodeName().equals("head"));
  +
  +           //now insert a text node
  +           //<head>text5<h2/><h1/></head>
  +           DOM_Text text5 = doc2.createTextNode("text5");
  +           range2.insertNode(text5);
  +
  +           TASSERT(range2.toString().equals("text5"));
  +           TASSERT(range2.getStartOffset()==0);
  +           TASSERT(range2.getStartContainer().getNodeName().equals("head"));
  +           TASSERT(range2.getEndOffset()==3);
  +           TASSERT(range2.getEndContainer().getNodeName().equals("head"));
  +
  +           //case 3: simple text node, start!=end
  +           // <body2>text3</body2>
  +           DOM_Element body2 = doc2.createElement("body2");
  +           DOM_Text text3 = doc2.createTextNode("text3");
  +           body2.appendChild(text3);
  +           root2.appendChild(body2);
  +
  +           //set range
  +           DOM_Range range3 = doc2.createRange();
  +           range3.setStart(text3,1);
  +           range3.setEnd(body2,1);
  +
  +           TASSERT(range3.toString().equals("ext3"));
  +           TASSERT(range3.getStartOffset()==1);
  +           TASSERT(range3.getStartContainer().getNodeValue().equals("text3"));
  +           TASSERT(range3.getEndOffset()==1);
  +           TASSERT(range3.getEndContainer().getNodeName().equals("body2"));
  +
  +           //now insert a textnode
  +           //<body2>ttext4ext3</body2>
  +           DOM_Text text4 = doc2.createTextNode("text4");
  +           range3.insertNode(text4);
  +
  +           TASSERT(range3.toString().equals(""));
  +           TASSERT(range3.getStartOffset()==1);
  +           TASSERT(range3.getStartContainer().getNodeValue().equals("t"));
  +           TASSERT(range3.getEndOffset()==1);
  +           TASSERT(range3.getEndContainer().getNodeName().equals("body2"));
  +
  +           //now insert a non-text node
  +           //<body2>t<p2/>text4ext3</body2>
  +           DOM_Element p2 = doc2.createElement("p2");
  +           range3.insertNode(p2);
  +
  +           //extra empty node caused by splitting 't'
  +           TASSERT(range3.toString().equals(""));
  +           TASSERT(range3.getStartOffset()==1);
  +           TASSERT(range3.getStartContainer().getNodeValue().equals("t"));
  +           TASSERT(range3.getEndOffset()==1);
  +           TASSERT(range3.getEndContainer().getNodeName().equals("body2"));
  +
  +           //test toString a bit
  +           range3.setStart(body2,1);
  +           range3.setEnd(body2,5);
  +
  +           TASSERT(range3.toString().equals("text4ext3"));
  +
  +           range3.setStart(body2,0);
  +           range3.setEnd(body2,5);
  +
  +           TASSERT(range3.toString().equals("ttext4ext3"));
  +
  +           //case 4: non-text node, start!=end
  +           // <head2><h3/></head2>
  +           DOM_Element head2 = doc2.createElement("head2");
  +           DOM_Element h3 = doc2.createElement("h3");
  +           head2.appendChild(h3);
  +           root2.appendChild(head2);
  +
  +           //set range
  +           DOM_Range range4 = doc2.createRange();
  +           range4.setStart(head2,0);
  +           range4.setEnd(h3,0);
  +
  +           TASSERT(range4.toString().equals(""));
  +           TASSERT(range4.getStartOffset()==0);
  +           TASSERT(range4.getStartContainer().getNodeName().equals("head2"));
  +           TASSERT(range4.getEndOffset()==0);
  +           TASSERT(range4.getEndContainer().getNodeName().equals("h3"));
  +
  +           //now insert a non-text node
  +           //<head2><h4/><h3/></head2>
  +           DOM_Element h4 = doc2.createElement("h4");
  +           range4.insertNode(h4);
  +
  +           TASSERT(range4.toString().equals(""));
  +           TASSERT(range4.getStartOffset()==0);
  +           TASSERT(range4.getStartContainer().getNodeName().equals("head2"));
  +           TASSERT(range4.getEndOffset()==0);
  +           TASSERT(range4.getEndContainer().getNodeName().equals("h3"));
  +
  +           //now insert a text node
  +           //<head2>text6<h4/><h3/></head2>
  +           DOM_Text text6 = doc2.createTextNode("text6");
  +           range4.insertNode(text6);
  +
  +           TASSERT(range4.toString().equals("text6"));
  +           TASSERT(range4.getStartOffset()==0);
  +           TASSERT(range4.getStartContainer().getNodeName().equals("head2"));
  +           TASSERT(range4.getEndOffset()==0);
  +           TASSERT(range4.getEndContainer().getNodeName().equals("h3"));
  +
  +           //***************************************************************
  +           // quick test of updating
  +           //***************************************************************
  +           // <upbody>text1</upbody>
  +           DOM_Element upbody = doc2.createElement("upbody");
  +           DOM_Text uptext1 = doc2.createTextNode("uptext1");
  +           upbody.appendChild(uptext1);
  +           root2.appendChild(upbody);
  +
  +           DOM_Range uprange = doc2.createRange();
  +           uprange.setStart(upbody,0);
  +           uprange.setEnd(upbody,1);
  +
  +           TASSERT(uprange.toString().equals("uptext1"));
  +           TASSERT(uprange.getStartOffset()==0);
  +           TASSERT(uprange.getStartContainer().getNodeName().equals("upbody"));
  +           TASSERT(uprange.getEndOffset()==1);
  +           TASSERT(uprange.getEndContainer().getNodeName().equals("upbody"));
  +
  +           // split text
  +           uptext1.splitText(1);
  +
  +           TASSERT(uprange.toString().equals("u"));
  +           TASSERT(uprange.getStartOffset()==0);
  +           TASSERT(uprange.getStartContainer().getNodeName().equals("upbody"));
  +           TASSERT(uprange.getEndOffset()==1);
  +           TASSERT(uprange.getEndContainer().getNodeName().equals("upbody"));
  +
  +           //insert node
  +           DOM_Element upbody2 = doc2.createElement("upbody2");
  +           DOM_Text uptext2 = doc2.createTextNode("uptext2");
  +           upbody2.appendChild(uptext2);
  +           root2.appendChild(upbody2);
  +
  +           DOM_Range uprange2 = doc2.createRange();
  +           uprange2.setStart(uptext2,1);
  +           uprange2.setEnd(upbody2,1);
  +
  +           DOM_Range uprange3 = doc2.createRange();
  +           uprange3.setStart(uptext2,1);
  +           uprange3.setEnd(upbody2,1);
  +
  +           TASSERT(uprange2.toString().equals("ptext2"));
  +           TASSERT(uprange2.getStartOffset()==1);
  +           TASSERT(uprange2.getStartContainer().getNodeValue().equals("uptext2"));
  +           TASSERT(uprange2.getEndOffset()==1);
  +           TASSERT(uprange2.getEndContainer().getNodeName().equals("upbody2"));
  +
  +           TASSERT(uprange3.toString().equals("ptext2"));
  +           TASSERT(uprange3.getStartOffset()==1);
  +           TASSERT(uprange3.getStartContainer().getNodeValue().equals("uptext2"));
  +           TASSERT(uprange3.getEndOffset()==1);
  +           TASSERT(uprange3.getEndContainer().getNodeName().equals("upbody2"));
  +
  +           DOM_Element upp1 = doc2.createElement("upp1");
  +           uprange2.insertNode(upp1);
  +
  +           TASSERT(uprange2.toString().equals(""));
  +           TASSERT(uprange2.getStartOffset()==1);
  +           TASSERT(uprange2.getStartContainer().getNodeValue().equals("u"));
  +           TASSERT(uprange2.getEndOffset()==1);
  +           TASSERT(uprange2.getEndContainer().getNodeName().equals("upbody2"));
  +
  +           TASSERT(uprange3.toString().equals(""));
  +           TASSERT(uprange3.getStartOffset()==1);
  +           TASSERT(uprange3.getStartContainer().getNodeValue().equals("u"));
  +           TASSERT(uprange3.getEndOffset()==1);
  +           TASSERT(uprange3.getEndContainer().getNodeName().equals("upbody2"));
  +
  +           //***************************************************************
  +           //another set of test
  +           //<foo><c/><moo><b/></moo>ab<a>Hello cd</a><cool>ef</cool></foo>
  +           //
  +           //  ______________________foo_____________________
  +           //  |          |           |          |           |
  +           //  c         moo        "ab"         a          cool
  +           //             |                      |           |
  +           //             b                    "Hello cd"   "ef"
  +           //
  +           DOM_Document doc3 = DOM_Document::createDocument();
  +           DOM_Element root3 = doc3.createElement("root");
  +           doc3.appendChild(root3);
  +
  +           DOM_Element foo = doc3.createElement("foo");
  +           DOM_Element moo = doc3.createElement("moo");
  +           DOM_Element cool = doc3.createElement("cool");
  +           DOM_Text ab = doc3.createTextNode("ab");
  +           DOM_Text cd = doc3.createTextNode("Hello cd");
  +           DOM_Text ef = doc3.createTextNode("ef");
  +
  +           DOM_Element a = doc3.createElement("a");
  +           DOM_Element b = doc3.createElement("b");
  +           DOM_Element c = doc3.createElement("c");
  +
  +           root3.appendChild(foo);
  +           foo.appendChild(c);
  +           foo.appendChild(moo);
  +           foo.appendChild(ab);
  +           foo.appendChild(a);
  +           foo.appendChild(cool);
  +           moo.appendChild(b);
  +           a.appendChild(cd);
  +           cool.appendChild(ef);
  +
  +           //***************************************************************
  +           //TEST toString
  +           //***************************************************************
  +           DOM_Range newtestrange = doc3.createRange();
  +           //case 1:
  +           //start container is text node
  +           //   i) end container is also text node
  +           //    a) start==end
  +           //    b) start!=end
  +           //  ii) end container is not text node
  +           //    a) start==end => impossible
  +           //    b) start!=end
  +           //
  +           //case 2:
  +           //start container is not text node
  +           //   i) end container is text node
  +           //    a) start==end => impossible
  +           //    b) start!=end
  +           //  ii) end container is not text node
  +           //    a) start==end
  +           //    b) start!=end
  +
  +           //case 1, i, a
  +           newtestrange.setStart( cd, 1 );
  +           newtestrange.setEnd( cd, 4 );
  +
  +           TASSERT(newtestrange.toString().equals("ell"));
  +
  +           //case 1, i, b
  +           newtestrange.setStart( cd, 1 );
  +           newtestrange.setEnd( ef, 2 );
  +
  +           TASSERT(newtestrange.toString().equals("ello cdef"));
  +
  +           //case 1, ii, b
  +           newtestrange.setStart( cd, 1 );
  +           newtestrange.setEnd( foo, 4 );
  +
  +           TASSERT(newtestrange.toString().equals("ello cd"));
  +
  +           //case 2, i, b
  +           newtestrange.setStart( foo, 1 );
  +           newtestrange.setEnd( cd, 5 );
  +
  +           TASSERT(newtestrange.toString().equals("abHello"));
  +
  +           //case 2, ii, a
  +           newtestrange.setStart( foo, 1 );
  +           newtestrange.setEnd( foo, 4 );
  +
  +           TASSERT(newtestrange.toString().equals("abHello cd"));
  +
  +           //case 2, ii, b
  +           newtestrange.setStart( moo, 1 );
  +           newtestrange.setEnd( foo, 4 );
  +
  +           TASSERT(newtestrange.toString().equals("abHello cd"));
  +
  +           //***************************************************************
  +           //test removeChild
  +           //***************************************************************
  +           DOM_Range newrange = doc3.createRange();
  +           newrange.setStart( moo, 0 );
  +           newrange.setEnd( foo, 4 );
  +
  +           TASSERT(newrange.getStartOffset()==0);
  +           TASSERT(newrange.getStartContainer().getNodeName().equals("moo"));
  +           TASSERT(newrange.getEndOffset()==4);
  +           TASSERT(newrange.getEndContainer().getNodeName().equals("foo"));
  +           TASSERT(newrange.toString().equals("abHello cd"));
  +
  +           DOM_Node n = newrange.cloneContents();
  +           DOM_NodeList nol = foo.getChildNodes();
  +
  +           //removing moo
  +           foo.removeChild(nol.item(1));
  +           TASSERT(newrange.getStartOffset()==1);
  +           TASSERT(newrange.getStartContainer().getNodeName().equals("foo"));
  +           TASSERT(newrange.getEndOffset()==3);
  +           TASSERT(newrange.getEndContainer().getNodeName().equals("foo"));
  +           TASSERT(newrange.toString().equals("abHello cd"));
  +
  +           TASSERT(newtestrange.getStartOffset()==1);
  +           TASSERT(newtestrange.getStartContainer().getNodeName().equals("foo"));
  +           TASSERT(newtestrange.getEndOffset()==3);
  +           TASSERT(newtestrange.getEndContainer().getNodeName().equals("foo"));
  +           TASSERT(newtestrange.toString().equals("abHello cd"));
   
       }
       TESTEPILOG;