You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@xerces.apache.org by le...@apache.org on 2001/01/18 03:49:57 UTC

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

lehors      01/01/17 18:49:57

  Modified:    c/Projects/Win32/VC6/xerces-all/XercesLib XercesLib.dsp
               c/src/dom AttrImpl.cpp AttrImpl.hpp ChildNode.cpp
                        ChildNode.hpp DocumentImpl.cpp DocumentTypeImpl.cpp
                        DocumentTypeImpl.hpp ElementImpl.cpp
                        ElementImpl.hpp EntityReferenceImpl.cpp
                        EntityReferenceImpl.hpp Makefile.in NodeImpl.cpp
                        NodeImpl.hpp ParentNode.cpp ParentNode.hpp
               c/tests/DOM/DOMMemTest DOMMemTest.cpp
  Removed:     c/src/dom ChildAndParentNode.cpp ChildAndParentNode.hpp
                        CommonParentNode.cpp CommonParentNode.hpp
  Log:
  This commit is a port of the latest changes I've made to the Java version,
  it brings yet another optimization to this DOM implementation.
  The whole idea is to try and avoid to always create a Text node to hold
  the value of an attribute. The DOM spec requires it, so we still have
  to do it in case getFirstChild() is called for instance. The reason
  attribute values are stored as a list of nodes is so that they can carry
  more than a simple string. They can also contain EntityReference nodes.
  However, most of the times people only have a single string that they
  only set and get through Element.set/getAttribute or Attr.set/getValue.
  In this new version, the Attr node has a value pointer which can either
  be the DOMString directly or a pointer to the first ChildNode. A flag tells
  which one it currently is.
  Note that while we try to stick with the direct DOMString as much as
  possible once we've switched to a node there is no going back. This is
  because we have no way to know whether the application keeps referring to
  the node we once returned (I guess that's not so true for C++!).
  
  The gain in memory varies on the density of attributes in the document.
  But in the tests I've run I've seen up to 12% of memory gain in the Java
  version. And the good thing is that it also leads to a slight gain in speed
  because we allocate fewer objects! I mean, that's until we have to actually
  create the node...
  
  To avoid too much duplicated code, I got rid of ParentNode and renamed
  ChildAndParentNode, which I never really liked, to ParentNode for
  simplicity, this doesn't make much of a difference in memory usage because
  there are only very few objects that are only a Parent. This is only true
  now because AttrImpl now inherits directly from NodeImpl and has its own
  implementation of the ParentNode's node behavior. So there is still some
  duplicated code there.
  
  Revision  Changes    Path
  1.46      +0 -8      xml-xerces/c/Projects/Win32/VC6/xerces-all/XercesLib/XercesLib.dsp
  
  Index: XercesLib.dsp
  ===================================================================
  RCS file: /home/cvs/xml-xerces/c/Projects/Win32/VC6/xerces-all/XercesLib/XercesLib.dsp,v
  retrieving revision 1.45
  retrieving revision 1.46
  diff -u -r1.45 -r1.46
  --- XercesLib.dsp	2001/01/12 22:09:05	1.45
  +++ XercesLib.dsp	2001/01/18 02:49:53	1.46
  @@ -872,14 +872,6 @@
   # End Source File
   # Begin Source File
   
  -SOURCE=..\..\..\..\..\src\dom\ChildAndParentNode.cpp
  -# End Source File
  -# Begin Source File
  -
  -SOURCE=..\..\..\..\..\src\dom\ChildAndParentNode.hpp
  -# End Source File
  -# Begin Source File
  -
   SOURCE=..\..\..\..\..\src\dom\ChildNode.cpp
   # End Source File
   # Begin Source File
  
  
  
  1.22      +434 -18   xml-xerces/c/src/dom/AttrImpl.cpp
  
  Index: AttrImpl.cpp
  ===================================================================
  RCS file: /home/cvs/xml-xerces/c/src/dom/AttrImpl.cpp,v
  retrieving revision 1.21
  retrieving revision 1.22
  diff -u -r1.21 -r1.22
  --- AttrImpl.cpp	2000/08/17 22:47:07	1.21
  +++ AttrImpl.cpp	2001/01/18 02:49:54	1.22
  @@ -55,7 +55,10 @@
    */
   
   /*
  - * $Id: AttrImpl.cpp,v 1.21 2000/08/17 22:47:07 lehors Exp $
  + * $Id: AttrImpl.cpp,v 1.22 2001/01/18 02:49:54 lehors Exp $
  + *
  + * <p><b>WARNING</b>: Some of the code here is partially duplicated in
  + * ParentNode, be careful to keep these two classes in sync!
    */
   
   #include "AttrImpl.hpp"
  @@ -65,24 +68,46 @@
   #include "ElementImpl.hpp"
   #include "DStringPool.hpp"
   #include "NodeIDMap.hpp"
  +#include "RangeImpl.hpp"
   
  +/*
  + * The handling of the value field being either the first child node (a
  + * ChildNode*) or directly the value (a DOMString) is rather tricky. In the
  + * DOMString case we need to get the field in the right type so that the
  + * compiler is happy and the appropriate operator gets called. This is
  + * essential for the reference counts of the DOMStrings involved to be updated
  + * as due.
  + * This is consistently achieved by taking the address of the value field and
  + * changing it into a DOMString*, and then dereferencing it to get a DOMString.
  + * The typical piece of code is:
  + * DOMString *x = (DOMString *)&value;
  + *  ... use of *x which is the DOMString ...
  + */
   
   AttrImpl::AttrImpl(DocumentImpl *ownerDoc, const DOMString &aName) 
  -    : ParentNode (ownerDoc)
  +    : NodeImpl (ownerDoc)
   {
       name = aName.clone();
       isSpecified(true);
  +    hasStringValue(true);
  +    value = null;
   };
   
   AttrImpl::AttrImpl(const AttrImpl &other, bool deep)
  -    : ParentNode(other)
  +    : NodeImpl(other)
   {
       name = other.name.clone();
   	
  -	if (other.isSpecified())
  -		isSpecified(true);
  -	else
  -		isSpecified(false);
  +    isSpecified(other.isSpecified());
  +
  +    /* We must initialize the void* value to null in *all* cases. Failing to do
  +     * so would cause, in case of assignment to a DOMString later, its content
  +     * to be derefenced as a DOMString, which would lead the ref count code to
  +     * be called on something that is not actually a DOMString... Really bad
  +     * things would then happen!!!
  +     */
  +    value = null;
  +    hasStringValue(other.hasStringValue());
   
       if (other.isIdAttr())
       {
  @@ -90,14 +115,56 @@
           this->getOwnerDocument()->getNodeIDMap()->add(this);
       }
       
  -	cloneChildren(other);
  +    // take care of case where there are kids
  +    if (!hasStringValue()) {
  +        cloneChildren(other);
  +    }
  +    else {
  +        // get the address of the value field of this as a DOMString*
  +        DOMString *x = (DOMString*) &value;
  +        // and the address of the value field of other as a DOMString*
  +        DOMString *y = (DOMString*) &(other.value);
  +        // We can now safely do the cloning and assignement, both operands
  +        // being a DOMString their ref counts will be updated appropriately
  +        *x = y->clone();
  +    }
   };
   
   
   AttrImpl::~AttrImpl() {
  -};
  +    if (hasStringValue()) {
  +        // if value is a DOMString we must make sure its ref count is updated.
  +        // this is achieved by changing the address of the value field into a
  +        // DOMString* and setting the value field to null
  +        DOMString *x = (DOMString *) &value;
  +        *x = null;
  +    }
  +}
   
   
  +// create a real Text node as child if we don't have one yet
  +void AttrImpl::makeChildNode() {
  +    if (hasStringValue()) {
  +        if (value != null) {
  +            // change the address of the value field into a DOMString*
  +            DOMString *x = (DOMString *) &value;
  +            // create a Text node passing the DOMString it points to
  +            TextImpl *text =
  +              (TextImpl *) getOwnerDocument()->createTextNode(*x);
  +            // get the DOMString ref count to be updated by setting the value
  +            // field to null
  +            *x = null;
  +            // finally reassign the value to the node address
  +            value = text;
  +            text->isFirstChild(true);
  +            text->previousSibling = text;
  +            text->ownerNode = this;
  +            text->isOwned(true);
  +        }
  +        hasStringValue(false);
  +    }
  +}
  +
   NodeImpl * AttrImpl::cloneNode(bool deep) 
   {
       return new AttrImpl(*this, deep);
  @@ -136,9 +203,16 @@
   
   DOMString AttrImpl::getValue() 
   {
  -    if (firstChild == null) {
  +    if (value == null) {
           return 0; // return "";
       }
  +    if (hasStringValue()) {
  +        // change value into a DOMString*
  +        DOMString *x = (DOMString *) &value;
  +        // return the DOMString it points to
  +        return *x;
  +    }
  +    ChildNode *firstChild = (ChildNode *) value;
       ChildNode *node = firstChild->nextSibling;
       if (node == null) {
           return firstChild->getNodeValue().clone();
  @@ -195,16 +269,22 @@
       if (isIdAttr())
           this->getOwnerDocument()->getNodeIDMap()->remove(this);
   
  -    NodeImpl *kid;
  -    while ((kid = firstChild) != null)         // Remove existing kids
  -    {
  -        removeChild(kid);
  -        if (kid->nodeRefCount == 0)
  -            NodeImpl::deleteIf(kid);
  +    if (!hasStringValue() && value != null) {
  +        NodeImpl *kid;
  +        while ((kid = (ChildNode *) value) != null) { // Remove existing kids
  +            removeChild(kid);
  +            if (kid->nodeRefCount == 0)
  +                NodeImpl::deleteIf(kid);
  +        }
       }
   
  -    if (val != null)              // Create and add the new one
  -        appendChild(ownerDocument->createTextNode(val));
  +    if (val != null) {
  +        // directly store the string as the value by changing the value field
  +        // into a DOMString
  +        DOMString *x = (DOMString *) &value;
  +        *x = val.clone();
  +        hasStringValue(true);
  +    }
       isSpecified(true);
       changed();
       
  @@ -243,3 +323,339 @@
       ownerNode = ownerElem;
       isOwned(false);
   }
  +
  +
  +// ParentNode stuff
  +
  +void AttrImpl::cloneChildren(const NodeImpl &other) {
  +  //    for (NodeImpl *mykid = other.getFirstChild(); 
  +    for (NodeImpl *mykid = ((NodeImpl&)other).getFirstChild(); 
  +         mykid != null; 
  +         mykid = mykid->getNextSibling()) {
  +        this->appendChild(mykid->cloneNode(true));
  +    }
  +}
  +
  +NodeListImpl *AttrImpl::getChildNodes() {
  +    return this;
  +}
  +
  +
  +NodeImpl * AttrImpl::getFirstChild() {
  +    makeChildNode();
  +    return (ChildNode *) value;
  +}
  +
  +
  +NodeImpl * AttrImpl::getLastChild() {
  +    return lastChild();
  +}
  +
  +ChildNode * AttrImpl::lastChild() {
  +    // last child is stored as the previous sibling of first child
  +    makeChildNode();
  +    return value != null ? ((ChildNode *) value)->previousSibling : null;
  +}
  +
  +void AttrImpl::lastChild(ChildNode *node) {
  +    // store lastChild as previous sibling of first child
  +    if (value != null) {
  +        ((ChildNode *) value)->previousSibling = node;
  +    }
  +}
  +
  +unsigned int AttrImpl::getLength() {
  +    if (hasStringValue()) {
  +        return 1;
  +    }
  +    ChildNode *node = (ChildNode *) value;
  +    int length = 0;
  +    while (node != null) {
  +        length++;
  +        node = node->nextSibling;
  +    }
  +    return length;
  +}
  +
  +bool AttrImpl::hasChildNodes()
  +{ 
  +    return value != null;
  +}; 
  +
  +
  +
  +NodeImpl *AttrImpl::insertBefore(NodeImpl *newChild, NodeImpl *refChild) {
  +    if (isReadOnly())
  +        throw DOM_DOMException(
  +        DOM_DOMException::NO_MODIFICATION_ALLOWED_ERR, null);
  +    
  +    if (newChild->getOwnerDocument() != getOwnerDocument())
  +        throw DOM_DOMException(DOM_DOMException::WRONG_DOCUMENT_ERR, null);
  +    
  +    // Convert to internal type, to avoid repeated casting  
  +    ChildNode * newInternal= (ChildNode *)newChild;
  +    
  +    // Prevent cycles in the tree
  +    // newChild cannot be ancestor of this Node, and actually cannot be this
  +    bool treeSafe=true;
  +    for(NodeImpl *a=this;
  +        treeSafe && a!=null;
  +        a=a->getParentNode())
  +        treeSafe=(newInternal!=a);
  +    if(!treeSafe)
  +        throw DOM_DOMException(DOM_DOMException::HIERARCHY_REQUEST_ERR,null);
  +    
  +    // refChild must in fact be a child of this node (or null)
  +    if (refChild!=null && refChild->getParentNode() != this)
  +        throw DOM_DOMException(DOM_DOMException::NOT_FOUND_ERR,null);
  +    
  +    // refChild cannot be same as newChild
  +    if(refChild==newInternal)
  +        throw DOM_DOMException(DOM_DOMException::HIERARCHY_REQUEST_ERR,null);
  +
  +    if (newInternal->isDocumentFragmentImpl())
  +    {
  +        // SLOW BUT SAFE: We could insert the whole subtree without
  +        // juggling so many next/previous pointers. (Wipe out the
  +        // parent's child-list, patch the parent pointers, set the
  +        // ends of the list.) But we know some subclasses have special-
  +        // case behavior they add to insertBefore(), so we don't risk it.
  +        // This approch also takes fewer bytecodes.
  +        
  +        // NOTE: If one of the children is not a legal child of this
  +        // node, throw HIERARCHY_REQUEST_ERR before _any_ of the children
  +        // have been transferred. (Alternative behaviors would be to
  +        // reparent up to the first failure point or reparent all those
  +        // which are acceptable to the target node, neither of which is
  +        // as robust. PR-DOM-0818 isn't entirely clear on which it
  +        // recommends?????
  +        
  +        // No need to check kids for right-document; if they weren't,
  +        // they wouldn't be kids of that DocFrag.
  +        for(NodeImpl *kid=newInternal->getFirstChild(); // Prescan
  +        kid!=null;
  +        kid=kid->getNextSibling())
  +        {
  +            if (!DocumentImpl::isKidOK(this, kid))
  +              throw DOM_DOMException(DOM_DOMException::HIERARCHY_REQUEST_ERR,null);
  +        }                       
  +        while(newInternal->hasChildNodes())     // Move
  +            insertBefore(newInternal->getFirstChild(),refChild);
  +    }
  +    
  +    else if (!DocumentImpl::isKidOK(this, newInternal))
  +        throw DOM_DOMException(DOM_DOMException::HIERARCHY_REQUEST_ERR,null);
  +    
  +    else {
  +        makeChildNode(); // make sure we have a node and not a string
  +
  +        NodeImpl *oldparent=newInternal->getParentNode();
  +        if(oldparent!=null) {
  +            oldparent->removeChild(newInternal);
  +        }
  +
  +        // Convert to internal type, to avoid repeated casting
  +        ChildNode *refInternal = (ChildNode *)refChild;
  +
  +        // Attach up
  +        newInternal->ownerNode = this;
  +        newInternal->isOwned(true);
  +        
  +        // Attach before and after
  +        // Note: firstChild.previousSibling == lastChild!!
  +        ChildNode *firstChild = (ChildNode *) value;
  +        if (firstChild == null) {
  +            // this our first and only child
  +            value = newInternal; // firstChild = newInternal
  +            newInternal->isFirstChild(true);
  +            newInternal->previousSibling = newInternal;
  +        } else {
  +            if (refInternal == null) {
  +                // this is an append
  +                ChildNode *lastChild = firstChild->previousSibling;
  +                lastChild->nextSibling = newInternal;
  +                newInternal->previousSibling = lastChild;
  +                firstChild->previousSibling = newInternal;
  +            } else {
  +                // this is an insert
  +                if (refChild == firstChild) {
  +                    // at the head of the list
  +                    firstChild->isFirstChild(false);
  +                    newInternal->nextSibling = firstChild;
  +                    newInternal->previousSibling =
  +                        firstChild->previousSibling;
  +                    firstChild->previousSibling = newInternal;
  +                    value = newInternal; // firstChild = newInternal;
  +                    newInternal->isFirstChild(true);
  +                } else {
  +                    // somewhere in the middle
  +                    ChildNode *prev = refInternal->previousSibling;
  +                    newInternal->nextSibling = refInternal;
  +                    prev->nextSibling = newInternal;
  +                    refInternal->previousSibling = newInternal;
  +                    newInternal->previousSibling = prev;
  +                }
  +            }
  +        }
  +
  +        changed();
  +
  +        if (this->getOwnerDocument() != null) {
  +            typedef RefVectorOf<RangeImpl> RangeImpls;
  +            RangeImpls* ranges = this->getOwnerDocument()->getRanges();
  +            if ( ranges != null) {
  +                unsigned int sz = ranges->size();
  +                for (unsigned int i =0; i<sz; i++) {
  +                    ranges->elementAt(i)->updateRangeForInsertedNode(newInternal);
  +                }
  +            }
  +        }
  +    }
  +
  +    return newInternal;
  +}
  +  
  +  
  +NodeImpl *AttrImpl::item(unsigned int index) {
  +
  +    if (hasStringValue()) {
  +        if (index != 0 || value == null) {
  +            return null;
  +        }
  +        else {
  +            makeChildNode();
  +            return (NodeImpl *) value;
  +        }
  +    }
  +    ChildNode *nodeListNode = (ChildNode *) value;
  +    for (unsigned int nodeListIndex = 0; 
  +         nodeListIndex < index && nodeListNode != null; 
  +         nodeListIndex++) {
  +        nodeListNode = nodeListNode->nextSibling;
  +    }
  +    return nodeListNode;
  +}
  +  
  +  
  +NodeImpl *AttrImpl::removeChild(NodeImpl *oldChild) {
  +    if (isReadOnly())
  +        throw DOM_DOMException(
  +        DOM_DOMException::NO_MODIFICATION_ALLOWED_ERR, null);
  +    
  +    if (hasStringValue()
  +        // we don't have any child per say so it can't be one of them!
  +        || (oldChild != null && oldChild->getParentNode() != this))
  +        throw DOM_DOMException(DOM_DOMException::NOT_FOUND_ERR, null);
  +    
  +    // fix other ranges for change before deleting the node
  +    if (this->getOwnerDocument() !=  null) {
  +        typedef RefVectorOf<RangeImpl> RangeImpls;
  +        RangeImpls* ranges = this->getOwnerDocument()->getRanges();
  +        if (ranges != null) {
  +            unsigned int sz = ranges->size();
  +            if (sz != 0) {
  +                for (unsigned int i =0; i<sz; i++) {
  +                    if (ranges->elementAt(i) != null) 
  +                        ranges->elementAt(i)->updateRangeForDeletedNode(oldChild);
  +                }
  +            }
  +        }
  +    }
  +    
  +    ChildNode * oldInternal = (ChildNode *) oldChild;
  +    
  +    // Patch linked list around oldChild
  +    // Note: lastChild == firstChild->previousSibling
  +    if (oldInternal == value) {
  +        // removing first child
  +        oldInternal->isFirstChild(false);
  +        value = oldInternal->nextSibling; // firstChild = oldInternal->nextSibling
  +        ChildNode *firstChild = (ChildNode *) value;
  +        if (firstChild != null) {
  +            firstChild->isFirstChild(true);
  +            firstChild->previousSibling = oldInternal->previousSibling;
  +        }
  +    } else {
  +        ChildNode *prev = oldInternal->previousSibling;
  +        ChildNode *next = oldInternal->nextSibling;
  +        prev->nextSibling = next;
  +        if (next == null) {
  +            // removing last child
  +            ChildNode *firstChild = (ChildNode *) value;
  +            firstChild->previousSibling = prev;
  +        } else {
  +            // removing some other child in the middle
  +            next->previousSibling = prev;
  +        }
  +    }
  +
  +    // Remove oldInternal's references to tree
  +    oldInternal->ownerNode = getOwnerDocument();
  +    oldInternal->isOwned(false);
  +    oldInternal->nextSibling = null;
  +    oldInternal->previousSibling = null;
  +
  +    changed();
  +
  +    return oldInternal;
  +};
  +
  +
  +NodeImpl *AttrImpl::replaceChild(NodeImpl *newChild, NodeImpl *oldChild) {
  +    insertBefore(newChild, oldChild);
  +    // changed() already done.
  +    return removeChild(oldChild);
  +}
  +  
  +
  +void AttrImpl::setReadOnly(bool readOnl, bool deep) {
  +    NodeImpl::setReadOnly(readOnl, deep);
  +      
  +    if (deep) {
  +        if (hasStringValue()) {
  +            return;
  +        }
  +        // Recursively set kids
  +        for (ChildNode *mykid = (ChildNode *) value; 
  +             mykid != null; 
  +             mykid = mykid->nextSibling)
  +            if(! (mykid->isEntityReference()))
  +                mykid->setReadOnly(readOnl,true);
  +    }
  +}
  +  
  +  
  +//Introduced in DOM Level 2
  +  
  +void AttrImpl::normalize()
  +{
  +    if (hasStringValue()) {
  +        return;
  +    }
  +    ChildNode *kid, *next;
  +    for (kid = (ChildNode *) value; kid != null; kid = next)
  +    {
  +        next = kid->nextSibling;
  +        
  +        // If kid and next are both Text nodes (but _not_ CDATASection,
  +        // which is a subclass of Text), they can be merged.
  +        if (next != null && 
  +            kid->isTextImpl()   && !(kid->isCDATASectionImpl())  && 
  +            next->isTextImpl()  && !(next->isCDATASectionImpl()) )
  +        {
  +            ((TextImpl *) kid)->appendData(((TextImpl *) next)->getData());
  +            removeChild(next);
  +            if (next->nodeRefCount == 0)
  +                deleteIf(next);
  +            next = kid; // Don't advance; there might be another.
  +        }
  +        
  +        // Otherwise it might be an Element, which is handled recursively  
  +        else
  +            if (kid->isElementImpl())
  +                kid->normalize();
  +    };
  +    
  +    // changed() will have occurred when the removeChild() was done,
  +    // so does not have to be reissued.
  +};
  
  
  
  1.12      +96 -4     xml-xerces/c/src/dom/AttrImpl.hpp
  
  Index: AttrImpl.hpp
  ===================================================================
  RCS file: /home/cvs/xml-xerces/c/src/dom/AttrImpl.hpp,v
  retrieving revision 1.11
  retrieving revision 1.12
  diff -u -r1.11 -r1.12
  --- AttrImpl.hpp	2000/04/27 02:52:42	1.11
  +++ AttrImpl.hpp	2001/01/18 02:49:54	1.12
  @@ -57,8 +57,74 @@
    * <http://www.apache.org/>.
    */
   
  -/*
  - * $Id: AttrImpl.hpp,v 1.11 2000/04/27 02:52:42 lehors Exp $
  +/**
  + * Attribute represents an XML-style attribute of an
  + * Element. Typically, the allowable values are controlled by its
  + * declaration in the Document Type Definition (DTD) governing this
  + * kind of document.
  + * <P>
  + * If the attribute has not been explicitly assigned a value, but has
  + * been declared in the DTD, it will exist and have that default. Only
  + * if neither the document nor the DTD specifies a value will the
  + * Attribute really be considered absent and have no value; in that
  + * case, querying the attribute will return null.
  + * <P>
  + * Attributes may have multiple children that contain their data. (XML
  + * allows attributes to contain entity references, and tokenized
  + * attribute types such as NMTOKENS may have a child for each token.)
  + * For convenience, the Attribute object's getValue() method returns
  + * the string version of the attribute's value.
  + * <P>
  + * Attributes are not children of the Elements they belong to, in the
  + * usual sense, and have no valid Parent reference. However, the spec
  + * says they _do_ belong to a specific Element, and an INUSE exception
  + * is to be thrown if the user attempts to explicitly share them
  + * between elements.
  + * <P>
  + * Note that Elements do not permit attributes to appear to be shared
  + * (see the INUSE exception), so this object's mutability is
  + * officially not an issue.
  + * <p>
  + * Note: The ownerNode attribute is used to store the Element the Attr
  + * node is associated with. Attr nodes do not have parent nodes.
  + * Besides, the getOwnerElement() method can be used to get the element node
  + * this attribute is associated with.
  + * <P>
  + * AttrImpl does not support Namespaces. AttrNSImpl, which inherits from
  + * it, does.
  + *
  + * <p>AttrImpl used to inherit from ParentNode. It now directly inherits from
  + * NodeImpl and provide its own implementation of the ParentNode's behavior.
  + * The reason is that we now try and avoid to always creating a Text node to
  + * hold the value of an attribute. The DOM spec requires it, so we still have
  + * to do it in case getFirstChild() is called for instance. The reason
  + * attribute values are stored as a list of nodes is so that they can carry
  + * more than a simple string. They can also contain EntityReference nodes.
  + * However, most of the times people only have a single string that they only
  + * set and get through Element.set/getAttribute or Attr.set/getValue. In this
  + * new version, the Attr node has a value pointer which can either be the
  + * String directly or a pointer to the first ChildNode. A flag tells which one
  + * it currently is. Note that while we try to stick with the direct String as
  + * much as possible once we've switched to a node there is no going back. This
  + * is because we have no way to know whether the application keeps referring to
  + * the node we once returned.
  + * <p> The gain in memory varies on the density of attributes in the document.
  + * But in the tests I've run I've seen up to 12% of memory gain. And the good
  + * thing is that it also leads to a slight gain in speed because we allocate
  + * fewer objects! I mean, that's until we have to actually create the node...
  + * <p>
  + * To avoid too much duplicated code, I got rid of ParentNode and renamed
  + * ChildAndParentNode, which I never really liked, to ParentNode for
  + * simplicity, this doesn't make much of a difference in memory usage because
  + * there are only very objects that are only a Parent. This is only true now
  + * because AttrImpl now inherits directly from NodeImpl and has its own
  + * implementation of the ParentNode's node behavior. So there is still some
  + * duplicated code there.
  + *
  + * <p><b>WARNING</b>: Some of the code here is partially duplicated in
  + * ParentNode, be careful to keep these two classes in sync!
  + *
  + * $Id: AttrImpl.hpp,v 1.12 2001/01/18 02:49:54 lehors Exp $
    */
   
   //
  @@ -73,16 +139,19 @@
   
   
   #include <util/XercesDefs.hpp>
  -#include "ParentNode.hpp"
  +#include "ChildNode.hpp"
   #include "DOM_Node.hpp"
   
   class ElementImpl;
   
  -class CDOM_EXPORT AttrImpl: public ParentNode {
  +class CDOM_EXPORT AttrImpl: public NodeImpl {
       
   public:
       DOMString name;
   
  +    /** This can either be a DOMString or the first child node (ChildNode*). */
  +    void *value;
  +
   public:
       AttrImpl(DocumentImpl *ownerDocument, const DOMString &aName);
       AttrImpl(const AttrImpl &other, bool deep=false);
  @@ -103,6 +172,29 @@
       //Introduced in DOM Level 2
       ElementImpl *getOwnerElement();
       void setOwnerElement(ElementImpl *ownerElem);    //internal use only
  +
  +
  +    // ParentNode stuff
  +    virtual NodeListImpl *getChildNodes();
  +    virtual NodeImpl * getFirstChild();
  +    virtual NodeImpl * getLastChild();
  +    virtual unsigned int getLength();
  +    virtual bool        hasChildNodes();
  +    virtual NodeImpl    *insertBefore(NodeImpl *newChild, NodeImpl *refChild);
  +    virtual NodeImpl    *item(unsigned int index);
  +    virtual NodeImpl    *removeChild(NodeImpl *oldChild);
  +    virtual NodeImpl    *replaceChild(NodeImpl *newChild, NodeImpl *oldChild);
  +    virtual void        setReadOnly(bool isReadOnly, bool deep);
  +
  +    //Introduced in DOM Level 2
  +    virtual void	normalize();
  +
  +protected:
  +    void makeChildNode();
  +    void cloneChildren(const NodeImpl &other);
  +    ChildNode * lastChild();
  +    void lastChild(ChildNode *);
  +
   };
   
   #endif
  
  
  
  1.3       +1 -10     xml-xerces/c/src/dom/ChildNode.cpp
  
  Index: ChildNode.cpp
  ===================================================================
  RCS file: /home/cvs/xml-xerces/c/src/dom/ChildNode.cpp,v
  retrieving revision 1.2
  retrieving revision 1.3
  diff -u -r1.2 -r1.3
  --- ChildNode.cpp	2000/08/17 22:47:08	1.2
  +++ ChildNode.cpp	2001/01/18 02:49:55	1.3
  @@ -55,7 +55,7 @@
    */
   
   /*
  - * $Id: ChildNode.cpp,v 1.2 2000/08/17 22:47:08 lehors Exp $
  + * $Id: ChildNode.cpp,v 1.3 2001/01/18 02:49:55 lehors Exp $
    */
   
   // This class only adds the ability to have siblings
  @@ -82,15 +82,6 @@
   
   ChildNode::~ChildNode() {
   };
  -
  -void ChildNode::changed()
  -{
  -    //  ++changes; we just let the parent know
  -    NodeImpl *parentNode = getParentNode();
  -    if (parentNode != null) {
  -        parentNode->changed();
  -    }
  -};  
   
   NodeImpl * ChildNode::getNextSibling() {
       return nextSibling;
  
  
  
  1.2       +1 -3      xml-xerces/c/src/dom/ChildNode.hpp
  
  Index: ChildNode.hpp
  ===================================================================
  RCS file: /home/cvs/xml-xerces/c/src/dom/ChildNode.hpp,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- ChildNode.hpp	2000/04/27 02:52:42	1.1
  +++ ChildNode.hpp	2001/01/18 02:49:55	1.2
  @@ -58,7 +58,7 @@
    */
   
   /*
  - * $Id: ChildNode.hpp,v 1.1 2000/04/27 02:52:42 lehors Exp $
  + * $Id: ChildNode.hpp,v 1.2 2001/01/18 02:49:55 lehors Exp $
    */
   
   //
  @@ -87,8 +87,6 @@
       ChildNode(const ChildNode &other);
       virtual ~ChildNode();
       
  -    virtual void changed();
  -
       virtual NodeImpl * getNextSibling();
       virtual NodeImpl * getParentNode();
       virtual NodeImpl*  getPreviousSibling();
  
  
  
  1.33      +24 -7     xml-xerces/c/src/dom/DocumentImpl.cpp
  
  Index: DocumentImpl.cpp
  ===================================================================
  RCS file: /home/cvs/xml-xerces/c/src/dom/DocumentImpl.cpp,v
  retrieving revision 1.32
  retrieving revision 1.33
  diff -u -r1.32 -r1.33
  --- DocumentImpl.cpp	2001/01/17 17:46:01	1.32
  +++ DocumentImpl.cpp	2001/01/18 02:49:55	1.33
  @@ -55,7 +55,7 @@
    */
   
   /*
  - * $Id: DocumentImpl.cpp,v 1.32 2001/01/17 17:46:01 lehors Exp $
  + * $Id: DocumentImpl.cpp,v 1.33 2001/01/18 02:49:55 lehors Exp $
    */
   
   //
  @@ -540,13 +540,30 @@
           }
           break;
       case DOM_Node::ATTRIBUTE_NODE :
  -	if (source->getLocalName() == null)
  -	    newnode = createAttribute(source->getNodeName());
  -	else
  -	    newnode = createAttributeNS(source->getNamespaceURI(),
  +        {
  +            if (source->getLocalName() == null)
  +                newnode = createAttribute(source->getNodeName());
  +            else
  +                newnode = createAttributeNS(source->getNamespaceURI(),
                                           source->getNodeName());
  -	deep = true;
  -        // Kids carry value
  +        // if source is an AttrImpl from this very same implementation
  +        // avoid creating the child nodes if possible
  +//        if (source instanceof AttrImpl) {
  +            AttrImpl *attr = (AttrImpl *) source;
  +            if (attr->hasStringValue()) {
  +                AttrImpl *newattr = (AttrImpl *) newnode;
  +                newattr->setValue(attr->getValue());
  +                deep = false;
  +            }
  +            else {
  +                deep = true;
  +            }
  +//        }
  +//        else {
  +//            // Kids carry value
  +//            deep = true;
  +//        }
  +        }
           break;
       case DOM_Node::TEXT_NODE :
           newnode = createTextNode(source->getNodeValue());
  
  
  
  1.21      +6 -6      xml-xerces/c/src/dom/DocumentTypeImpl.cpp
  
  Index: DocumentTypeImpl.cpp
  ===================================================================
  RCS file: /home/cvs/xml-xerces/c/src/dom/DocumentTypeImpl.cpp,v
  retrieving revision 1.20
  retrieving revision 1.21
  diff -u -r1.20 -r1.21
  --- DocumentTypeImpl.cpp	2000/07/05 23:16:10	1.20
  +++ DocumentTypeImpl.cpp	2001/01/18 02:49:55	1.21
  @@ -55,7 +55,7 @@
    */
   
   /*
  - * $Id: DocumentTypeImpl.cpp,v 1.20 2000/07/05 23:16:10 jpolast Exp $
  + * $Id: DocumentTypeImpl.cpp,v 1.21 2001/01/18 02:49:55 lehors Exp $
    */
   
   #include "DocumentTypeImpl.hpp"
  @@ -67,7 +67,7 @@
   
   DocumentTypeImpl::DocumentTypeImpl(DocumentImpl *ownerDoc,
                                      const DOMString &dtName) 
  -    : ChildAndParentNode(ownerDoc),
  +    : ParentNode(ownerDoc),
       publicId(null), systemId(null), internalSubset(null) //DOM Level 2
   	, intSubsetReading(false)	
   {
  @@ -84,7 +84,7 @@
                                      const DOMString &qualifiedName,
                                      const DOMString &pubId,
                                      const DOMString &sysId)
  -	: ChildAndParentNode(ownerDoc),
  +	: ParentNode(ownerDoc),
       publicId(pubId), systemId(sysId), internalSubset(null)
   	, intSubsetReading(false)
   {
  @@ -99,7 +99,7 @@
   
   
   DocumentTypeImpl::DocumentTypeImpl(const DocumentTypeImpl &other, bool deep)
  -    : ChildAndParentNode(other)
  +    : ParentNode(other)
   {
       name = other.name.clone();
       if (deep)
  @@ -147,7 +147,7 @@
    * set the ownerDocument of this node and its children
    */
   void DocumentTypeImpl::setOwnerDocument(DocumentImpl *doc) {
  -    ChildAndParentNode::setOwnerDocument(doc);
  +    ParentNode::setOwnerDocument(doc);
       entities->setOwnerDocument(doc);
       notations->setOwnerDocument(doc);
       //    elements->setOwnerDocument(doc);
  @@ -201,7 +201,7 @@
   
   void DocumentTypeImpl::setReadOnly(bool readOnl, bool deep)
   {
  -    ChildAndParentNode::setReadOnly(readOnl,deep);
  +    ParentNode::setReadOnly(readOnl,deep);
       entities->setReadOnly(readOnl,true);
       notations->setReadOnly(readOnl,true);
   };
  
  
  
  1.16      +3 -3      xml-xerces/c/src/dom/DocumentTypeImpl.hpp
  
  Index: DocumentTypeImpl.hpp
  ===================================================================
  RCS file: /home/cvs/xml-xerces/c/src/dom/DocumentTypeImpl.hpp,v
  retrieving revision 1.15
  retrieving revision 1.16
  diff -u -r1.15 -r1.16
  --- DocumentTypeImpl.hpp	2000/07/05 23:16:10	1.15
  +++ DocumentTypeImpl.hpp	2001/01/18 02:49:55	1.16
  @@ -58,7 +58,7 @@
    */
   
   /*
  - * $Id: DocumentTypeImpl.hpp,v 1.15 2000/07/05 23:16:10 jpolast Exp $
  + * $Id: DocumentTypeImpl.hpp,v 1.16 2001/01/18 02:49:55 lehors Exp $
    */
   
   //
  @@ -73,11 +73,11 @@
   
   
   #include <util/XercesDefs.hpp>
  -#include "ChildAndParentNode.hpp"
  +#include "ParentNode.hpp"
   
   class NamedNodeMapImpl;
   
  -class CDOM_EXPORT DocumentTypeImpl: public ChildAndParentNode {
  +class CDOM_EXPORT DocumentTypeImpl: public ParentNode {
   private:
       DOMString			name;
       NamedNodeMapImpl	*entities;
  
  
  
  1.29      +5 -5      xml-xerces/c/src/dom/ElementImpl.cpp
  
  Index: ElementImpl.cpp
  ===================================================================
  RCS file: /home/cvs/xml-xerces/c/src/dom/ElementImpl.cpp,v
  retrieving revision 1.28
  retrieving revision 1.29
  diff -u -r1.28 -r1.29
  --- ElementImpl.cpp	2001/01/17 21:15:16	1.28
  +++ ElementImpl.cpp	2001/01/18 02:49:55	1.29
  @@ -55,7 +55,7 @@
    */
   
   /*
  - * $Id: ElementImpl.cpp,v 1.28 2001/01/17 21:15:16 tng Exp $
  + * $Id: ElementImpl.cpp,v 1.29 2001/01/18 02:49:55 lehors Exp $
    */
    
   #include "DeepNodeListImpl.hpp"
  @@ -70,7 +70,7 @@
   
   
   ElementImpl::ElementImpl(DocumentImpl *ownerDoc, const DOMString &eName)
  -    : ChildAndParentNode(ownerDoc)
  +    : ParentNode(ownerDoc)
   {
       name = eName.clone();
       attributes = null;
  @@ -79,7 +79,7 @@
   
   
   ElementImpl::ElementImpl(const ElementImpl &other, bool deep)
  -    : ChildAndParentNode(other)
  +    : ParentNode(other)
   {
       name = other.name.clone();
   	attributes = null;
  @@ -112,7 +112,7 @@
    * set the ownerDocument of this node, its children, and its attributes
    */
   void ElementImpl::setOwnerDocument(DocumentImpl *doc) {
  -    ChildAndParentNode::setOwnerDocument(doc);
  +    ParentNode::setOwnerDocument(doc);
   	if (attributes != null)
   		attributes->setOwnerDocument(doc);
   }
  @@ -278,7 +278,7 @@
   
   void ElementImpl::setReadOnly(bool readOnl, bool deep)
   {
  -    ChildAndParentNode::setReadOnly(readOnl,deep);
  +    ParentNode::setReadOnly(readOnl,deep);
       attributes->setReadOnly(readOnl,true);
   };
   
  
  
  
  1.16      +3 -3      xml-xerces/c/src/dom/ElementImpl.hpp
  
  Index: ElementImpl.hpp
  ===================================================================
  RCS file: /home/cvs/xml-xerces/c/src/dom/ElementImpl.hpp,v
  retrieving revision 1.15
  retrieving revision 1.16
  diff -u -r1.15 -r1.16
  --- ElementImpl.hpp	2000/07/05 23:16:10	1.15
  +++ ElementImpl.hpp	2001/01/18 02:49:55	1.16
  @@ -58,7 +58,7 @@
    */
   
   /*
  - * $Id: ElementImpl.hpp,v 1.15 2000/07/05 23:16:10 jpolast Exp $
  + * $Id: ElementImpl.hpp,v 1.16 2001/01/18 02:49:55 lehors Exp $
    */
   
   //
  @@ -74,11 +74,11 @@
   #include <util/XercesDefs.hpp>
   #include "AttrImpl.hpp"
   #include "AttrMapImpl.hpp"
  -#include "ChildAndParentNode.hpp"
  +#include "ParentNode.hpp"
   
   class DeepNodeListImpl;
   
  -class CDOM_EXPORT ElementImpl: public ChildAndParentNode {
  +class CDOM_EXPORT ElementImpl: public ParentNode {
   protected:
       DOMString name;
       AttrMapImpl *attributes;
  
  
  
  1.17      +4 -4      xml-xerces/c/src/dom/EntityReferenceImpl.cpp
  
  Index: EntityReferenceImpl.cpp
  ===================================================================
  RCS file: /home/cvs/xml-xerces/c/src/dom/EntityReferenceImpl.cpp,v
  retrieving revision 1.16
  retrieving revision 1.17
  diff -u -r1.16 -r1.17
  --- EntityReferenceImpl.cpp	2000/09/19 17:33:15	1.16
  +++ EntityReferenceImpl.cpp	2001/01/18 02:49:55	1.17
  @@ -55,7 +55,7 @@
    */
   
   /*
  - * $Id: EntityReferenceImpl.cpp,v 1.16 2000/09/19 17:33:15 aruna1 Exp $
  + * $Id: EntityReferenceImpl.cpp,v 1.17 2001/01/18 02:49:55 lehors Exp $
    */
   
   /**
  @@ -122,7 +122,7 @@
   
   EntityReferenceImpl::EntityReferenceImpl(DocumentImpl *ownerDoc,
                                            const DOMString &entityName)
  -    : ChildAndParentNode(ownerDoc)
  +    : ParentNode(ownerDoc)
   {
       name = entityName.clone();
       // EntityReference behaves as a read-only node, since its contents
  @@ -135,7 +135,7 @@
   
   EntityReferenceImpl::EntityReferenceImpl(const EntityReferenceImpl &other,
                                            bool deep)
  -    : ChildAndParentNode(other)
  +    : ParentNode(other)
   {
       name = other.name.clone();
       if (deep)
  @@ -196,5 +196,5 @@
   {
       if(readOnl==false)
           throw DOM_DOMException(DOM_DOMException::NO_MODIFICATION_ALLOWED_ERR,null);
  -    ChildAndParentNode::setReadOnly(readOnl,deep);
  +    ParentNode::setReadOnly(readOnl,deep);
   }
  
  
  
  1.14      +3 -3      xml-xerces/c/src/dom/EntityReferenceImpl.hpp
  
  Index: EntityReferenceImpl.hpp
  ===================================================================
  RCS file: /home/cvs/xml-xerces/c/src/dom/EntityReferenceImpl.hpp,v
  retrieving revision 1.13
  retrieving revision 1.14
  diff -u -r1.13 -r1.14
  --- EntityReferenceImpl.hpp	2000/09/19 17:33:15	1.13
  +++ EntityReferenceImpl.hpp	2001/01/18 02:49:55	1.14
  @@ -58,7 +58,7 @@
    */
   
   /*
  - * $Id: EntityReferenceImpl.hpp,v 1.13 2000/09/19 17:33:15 aruna1 Exp $
  + * $Id: EntityReferenceImpl.hpp,v 1.14 2001/01/18 02:49:55 lehors Exp $
    */
   
   //
  @@ -71,9 +71,9 @@
   //
   
   #include <util/XercesDefs.hpp>
  -#include "ChildAndParentNode.hpp"
  +#include "ParentNode.hpp"
   
  -class CDOM_EXPORT EntityReferenceImpl: public ChildAndParentNode
  +class CDOM_EXPORT EntityReferenceImpl: public ParentNode
   {
   private:
       DOMString name;
  
  
  
  1.17      +1 -3      xml-xerces/c/src/dom/Makefile.in
  
  Index: Makefile.in
  ===================================================================
  RCS file: /home/cvs/xml-xerces/c/src/dom/Makefile.in,v
  retrieving revision 1.16
  retrieving revision 1.17
  diff -u -r1.16 -r1.17
  --- Makefile.in	2000/07/28 01:33:32	1.16
  +++ Makefile.in	2001/01/18 02:49:55	1.17
  @@ -54,7 +54,7 @@
   # <http://www.apache.org/>.
   #
   #
  -# $Id: Makefile.in,v 1.16 2000/07/28 01:33:32 aruna1 Exp $
  +# $Id: Makefile.in,v 1.17 2001/01/18 02:49:55 lehors Exp $
   #
   
   PLATFORM = @platform@
  @@ -109,7 +109,6 @@
   	AttrNSImpl.hpp \
   	CDATASectionImpl.hpp \
   	CharacterDataImpl.hpp \
  -	ChildAndParentNode.hpp \
   	ChildNode.hpp \
   	CommentImpl.hpp \
   	DStringPool.hpp \
  @@ -148,7 +147,6 @@
   	AttrNSImpl.$(TO) \
   	CDATASectionImpl.$(TO) \
   	CharacterDataImpl.$(TO) \
  -	ChildAndParentNode.$(TO) \
   	ChildNode.$(TO) \
   	CommentImpl.$(TO) \
   	DOMString.$(TO) \
  
  
  
  1.33      +1 -0      xml-xerces/c/src/dom/NodeImpl.cpp
  
  Index: NodeImpl.cpp
  ===================================================================
  RCS file: /home/cvs/xml-xerces/c/src/dom/NodeImpl.cpp,v
  retrieving revision 1.32
  retrieving revision 1.33
  diff -u -r1.32 -r1.33
  --- NodeImpl.cpp	2001/01/17 17:46:02	1.32
  +++ NodeImpl.cpp	2001/01/18 02:49:55	1.33
  @@ -55,7 +55,7 @@
    */
   
   /*
  - * $Id: NodeImpl.cpp,v 1.32 2001/01/17 17:46:02 lehors Exp $
  + * $Id: NodeImpl.cpp,v 1.33 2001/01/18 02:49:55 lehors Exp $
    */
   
   // This class doesn't support having any children, and implements the behavior
  @@ -89,6 +89,7 @@
   const unsigned short NodeImpl::SETVALUE     = 0x1<<7;
   const unsigned short NodeImpl::ID_ATTR      = 0x1<<8;
   const unsigned short NodeImpl::USERDATA     = 0x1<<9;
  +const unsigned short NodeImpl::HASSTRING    = 0x1<<10;
   
   
   NodeImpl::NodeImpl(DocumentImpl *ownerDoc)
  
  
  
  1.23      +11 -2     xml-xerces/c/src/dom/NodeImpl.hpp
  
  Index: NodeImpl.hpp
  ===================================================================
  RCS file: /home/cvs/xml-xerces/c/src/dom/NodeImpl.hpp,v
  retrieving revision 1.22
  retrieving revision 1.23
  diff -u -r1.22 -r1.23
  --- NodeImpl.hpp	2001/01/17 17:25:53	1.22
  +++ NodeImpl.hpp	2001/01/18 02:49:55	1.23
  @@ -58,7 +58,7 @@
    */
   
   /*
  - * $Id: NodeImpl.hpp,v 1.22 2001/01/17 17:25:53 lehors Exp $
  + * $Id: NodeImpl.hpp,v 1.23 2001/01/18 02:49:55 lehors Exp $
    */
   
   //
  @@ -127,6 +127,7 @@
       static const unsigned short SETVALUE;
       static const unsigned short ID_ATTR;
       static const unsigned short USERDATA;
  +    static const unsigned short HASSTRING;
   
       static int              gLiveNodeImpls; // Counters for debug & tuning.
       static int              gTotalNodeImpls;
  @@ -262,11 +263,11 @@
           flags = (value ? flags | IGNORABLEWS : flags & ~IGNORABLEWS);
       }
   
  -    inline bool setValue() const {
  +    inline bool setValueCalled() const {
           return (flags & SETVALUE) != 0;
       }
   
  -    inline void setValue(bool value) {
  +    inline void setValueCalled(bool value) {
           flags = (value ? flags | SETVALUE : flags & ~SETVALUE);
       }
       
  @@ -284,6 +285,14 @@
   
       inline void hasUserData(bool value) {
           flags = (value ? flags | USERDATA : flags & ~USERDATA);
  +    }
  +    
  +    inline bool hasStringValue() const {
  +        return (flags & HASSTRING) != 0;
  +    }
  +
  +    inline void hasStringValue(bool value) {
  +        flags = (value ? flags | HASSTRING : flags & ~HASSTRING);
       }
   };
   
  
  
  
  1.6       +420 -5    xml-xerces/c/src/dom/ParentNode.cpp
  
  Index: ParentNode.cpp
  ===================================================================
  RCS file: /home/cvs/xml-xerces/c/src/dom/ParentNode.cpp,v
  retrieving revision 1.5
  retrieving revision 1.6
  diff -u -r1.5 -r1.6
  --- ParentNode.cpp	2000/09/06 00:24:14	1.5
  +++ ParentNode.cpp	2001/01/18 02:49:56	1.6
  @@ -55,16 +55,431 @@
    */
   
   /*
  - * $Id: ParentNode.cpp,v 1.5 2000/09/06 00:24:14 andyh Exp $
  + * $Id: ParentNode.cpp,v 1.6 2001/01/18 02:49:56 lehors Exp $
  + *
  + * <p><b>WARNING</b>: Some of the code here is partially duplicated in
  + * AttrImpl, be careful to keep these two classes in sync!
    */
   
  +
   #include "ParentNode.hpp"
   #include "DOM_DOMException.hpp"
  -#include "DocumentImpl.hpp"
   #include "TextImpl.hpp"
  +#include "DocumentImpl.hpp"
   #include "RangeImpl.hpp"
  +
  +
  +ParentNode::ParentNode(DocumentImpl *ownerDoc)
  +    : ChildNode(ownerDoc)
  +{
  +    this->ownerDocument = ownerDoc;
  +    this->firstChild = null;
  +
  +    fCachedLength = -1;
  +    fCachedChild = null;
  +    fCachedChildIndex = -1;
  +};  
  +
  +// This only makes a shallow copy, cloneChildren must also be called for a
  +// deep clone
  +ParentNode::ParentNode(const ParentNode &other)
  +    : ChildNode(other)
  +{
  +    this->ownerDocument = other.ownerDocument;
  +
  +    // Need to break the association w/ original kids
  +    this->firstChild = null;
  +
  +    fCachedLength = -1;
  +    fCachedChild = null;
  +    fCachedChildIndex = -1;
  +};
  +
  +
  +void ParentNode::cloneChildren(const NodeImpl &other) {    
  +  //    for (NodeImpl *mykid = other.getFirstChild(); 
  +    for (NodeImpl *mykid = ((NodeImpl&)other).getFirstChild(); 
  +         mykid != null; 
  +         mykid = mykid->getNextSibling()) {
  +        this->appendChild(mykid->cloneNode(true));
  +    }
  +}
  +
  +DocumentImpl * ParentNode::getOwnerDocument() {
  +    return ownerDocument;
  +}
  +
  +// unlike getOwnerDocument this is not overriden by DocumentImpl to return null
  +DocumentImpl * ParentNode::getDocument() {
  +    return ownerDocument;
  +}
  +
  +void ParentNode::setOwnerDocument(DocumentImpl *doc) {
  +    ownerDocument = doc;
  +    for (NodeImpl *child = firstChild;
  +         child != null; child = child->getNextSibling()) {
  +        child->setOwnerDocument(doc);
  +    }
  +}
  +
  +
  +NodeListImpl *ParentNode::getChildNodes() {
  +    return this;
  +};
  +
  +
  +NodeImpl * ParentNode::getFirstChild() {
  +    return firstChild;
  +}; 
  +
  +
  +NodeImpl * ParentNode::getLastChild()
  +{
  +    return lastChild();
  +}; 
  +
  +ChildNode * ParentNode::lastChild()
  +{
  +    // last child is stored as the previous sibling of first child
  +    return firstChild != null ? firstChild->previousSibling : null;
  +}; 
  +
  +void ParentNode::lastChild(ChildNode *node) {
  +        // store lastChild as previous sibling of first child
  +        if (firstChild != null) {
  +            firstChild->previousSibling = node;
  +        }
  +    }
  +
  +
  +unsigned int ParentNode::getLength() {
  +    if (fCachedLength == -1) { // is the cached length invalid ?
  +        ChildNode *node;
  +        // start from the cached node if we have one
  +        if (fCachedChildIndex != -1 && fCachedChild != null) {
  +            fCachedLength = fCachedChildIndex;
  +            node = fCachedChild;
  +        } else {
  +            node = firstChild;
  +            fCachedLength = 0;
  +        }
  +        while (node != null) {
  +            fCachedLength++;
  +            node = node->nextSibling;
  +        }
  +    }
  +    return fCachedLength;
  +};
  +
  +
  +bool ParentNode::hasChildNodes()
  +{ 
  +    return firstChild!=null;
  +}; 
  +
  +
  +
  +NodeImpl *ParentNode::insertBefore(NodeImpl *newChild, NodeImpl *refChild) {
  +    if (isReadOnly())
  +        throw DOM_DOMException(
  +        DOM_DOMException::NO_MODIFICATION_ALLOWED_ERR, null);
  +    
  +    if (newChild->getOwnerDocument() != ownerDocument)
  +        throw DOM_DOMException(DOM_DOMException::WRONG_DOCUMENT_ERR, null);
  +    
  +    // Convert to internal type, to avoid repeated casting  
  +    ChildNode * newInternal= (ChildNode *)newChild;
  +    
  +    // Prevent cycles in the tree
  +    // newChild cannot be ancestor of this Node, and actually cannot be this
  +    bool treeSafe=true;
  +    for(NodeImpl *a=this;
  +        treeSafe && a!=null;
  +        a=a->getParentNode())
  +        treeSafe=(newInternal!=a);
  +    if(!treeSafe)
  +        throw DOM_DOMException(DOM_DOMException::HIERARCHY_REQUEST_ERR,null);
  +    
  +    // refChild must in fact be a child of this node (or null)
  +    if (refChild!=null && refChild->getParentNode() != this)
  +        throw DOM_DOMException(DOM_DOMException::NOT_FOUND_ERR,null);
  +    
  +    // refChild cannot be same as newChild
  +    if(refChild==newInternal)
  +        throw DOM_DOMException(DOM_DOMException::HIERARCHY_REQUEST_ERR,null);
  +
  +    if (newInternal->isDocumentFragmentImpl())
  +    {
  +        // SLOW BUT SAFE: We could insert the whole subtree without
  +        // juggling so many next/previous pointers. (Wipe out the
  +        // parent's child-list, patch the parent pointers, set the
  +        // ends of the list.) But we know some subclasses have special-
  +        // case behavior they add to insertBefore(), so we don't risk it.
  +        // This approch also takes fewer bytecodes.
  +        
  +        // NOTE: If one of the children is not a legal child of this
  +        // node, throw HIERARCHY_REQUEST_ERR before _any_ of the children
  +        // have been transferred. (Alternative behaviors would be to
  +        // reparent up to the first failure point or reparent all those
  +        // which are acceptable to the target node, neither of which is
  +        // as robust. PR-DOM-0818 isn't entirely clear on which it
  +        // recommends?????
  +        
  +        // No need to check kids for right-document; if they weren't,
  +        // they wouldn't be kids of that DocFrag.
  +        for(NodeImpl *kid=newInternal->getFirstChild(); // Prescan
  +        kid!=null;
  +        kid=kid->getNextSibling())
  +        {
  +            if (!DocumentImpl::isKidOK(this, kid))
  +              throw DOM_DOMException(DOM_DOMException::HIERARCHY_REQUEST_ERR,null);
  +        }                       
  +        while(newInternal->hasChildNodes())     // Move
  +            insertBefore(newInternal->getFirstChild(),refChild);
  +    }
  +    
  +    else if (!DocumentImpl::isKidOK(this, newInternal))
  +        throw DOM_DOMException(DOM_DOMException::HIERARCHY_REQUEST_ERR,null);
  +    
  +    else
  +    {
  +        NodeImpl *oldparent=newInternal->getParentNode();
  +        if(oldparent!=null) {
  +            oldparent->removeChild(newInternal);
  +        }
  +
  +        // Convert to internal type, to avoid repeated casting
  +        ChildNode *refInternal = (ChildNode *)refChild;
  +
  +        // Attach up
  +        newInternal->ownerNode = this;
  +        newInternal->isOwned(true);
  +        
  +        // Attach before and after
  +        // Note: firstChild.previousSibling == lastChild!!
  +        if (firstChild == null) {
  +            // this our first and only child
  +            firstChild = newInternal;
  +            newInternal->isFirstChild(true);
  +            newInternal->previousSibling = newInternal;
  +        } else {
  +            if (refInternal == null) {
  +                // this is an append
  +                ChildNode *lastChild = firstChild->previousSibling;
  +                lastChild->nextSibling = newInternal;
  +                newInternal->previousSibling = lastChild;
  +                firstChild->previousSibling = newInternal;
  +            } else {
  +                // this is an insert
  +                if (refChild == firstChild) {
  +                    // at the head of the list
  +                    firstChild->isFirstChild(false);
  +                    newInternal->nextSibling = firstChild;
  +                    newInternal->previousSibling = firstChild->previousSibling;
  +                    firstChild->previousSibling = newInternal;
  +                    firstChild = newInternal;
  +                    newInternal->isFirstChild(true);
  +                } else {
  +                    // somewhere in the middle
  +                    ChildNode *prev = refInternal->previousSibling;
  +                    newInternal->nextSibling = refInternal;
  +                    prev->nextSibling = newInternal;
  +                    refInternal->previousSibling = newInternal;
  +                    newInternal->previousSibling = prev;
  +                }
  +            }
  +        }
  +
  +        changed();
  +
  +        // update cached length if we have any
  +        if (fCachedLength != -1) {
  +            fCachedLength++;
  +        }
  +        if (fCachedChildIndex != -1) {
  +            // if we happen to insert just before the cached node, update
  +            // the cache to the new node to match the cached index
  +            if (fCachedChild == refInternal) {
  +                fCachedChild = newInternal;
  +            } else {
  +                // otherwise just invalidate the cache
  +                fCachedChildIndex = -1;
  +            }
  +        }
  +
  +        if (this->getOwnerDocument() != null) {
  +            typedef RefVectorOf<RangeImpl> RangeImpls;
  +            RangeImpls* ranges = this->getOwnerDocument()->getRanges();
  +            if ( ranges != null) {
  +                unsigned int sz = ranges->size();
  +                for (unsigned int i =0; i<sz; i++) {
  +                    ranges->elementAt(i)->updateRangeForInsertedNode(newInternal);
  +                }
  +            }
  +        }
  +    }
  +
  +    return newInternal;
  +};
  +  
  +  
  +NodeImpl *ParentNode::item(unsigned int index) {
  +    // short way
  +    if (fCachedChildIndex != -1 && fCachedChild != null) {
  +        if (fCachedChildIndex < index) {
  +            while (fCachedChildIndex < index && fCachedChild != null) {
  +                fCachedChildIndex++;
  +                fCachedChild = fCachedChild->nextSibling;
  +            }
  +        }
  +        else if (fCachedChildIndex > index) {
  +            while (fCachedChildIndex > index && fCachedChild != null) {
  +                fCachedChildIndex--;
  +                fCachedChild = (ChildNode *)fCachedChild->getPreviousSibling();
  +            }
  +        }
  +        return fCachedChild;
  +    }
  +
  +    // long way
  +    fCachedChild = firstChild;
  +    for (fCachedChildIndex = 0; 
  +         fCachedChildIndex < index && fCachedChild != null; 
  +         fCachedChildIndex++) {
  +        fCachedChild = fCachedChild->nextSibling;
  +    }
  +    return fCachedChild;
  +};
  +  
  +  
  +NodeImpl *ParentNode::removeChild(NodeImpl *oldChild) 
  +{
  +    if (isReadOnly())
  +        throw DOM_DOMException(
  +        DOM_DOMException::NO_MODIFICATION_ALLOWED_ERR, null);
  +    
  +    if (oldChild != null && oldChild->getParentNode() != this)
  +        throw DOM_DOMException(DOM_DOMException::NOT_FOUND_ERR, null);
  +    
  +    //fix other ranges for change before deleting the node
  +    if (this->getOwnerDocument() !=  null  ) {
  +        typedef RefVectorOf<RangeImpl> RangeImpls;
  +        RangeImpls* ranges = this->getOwnerDocument()->getRanges();
  +        if (ranges != null) {
  +            unsigned int sz = ranges->size();
  +            if (sz != 0) {
  +                for (unsigned int i =0; i<sz; i++) {
  +                    if (ranges->elementAt(i) != null) 
  +                        ranges->elementAt(i)->updateRangeForDeletedNode(oldChild);
  +                }
  +            }
  +        }
  +    }
  +    
  +    ChildNode * oldInternal = (ChildNode *) oldChild;
  +    
  +    // update cached length if we have any
  +    if (fCachedLength != -1) {
  +        fCachedLength--;
  +    }
  +    if (fCachedChildIndex != -1) {
  +        // if the removed node is the cached node
  +        // move the cache to its (soon former) previous sibling
  +        if (fCachedChild == oldInternal) {
  +            fCachedChildIndex--;
  +            fCachedChild = (ChildNode *)oldInternal->getPreviousSibling();
  +        } else {
  +            // otherwise just invalidate the cache
  +            fCachedChildIndex = -1;
  +        }
  +    }
  +
  +    // Patch linked list around oldChild
  +    // Note: lastChild == firstChild->previousSibling
  +    if (oldInternal == firstChild) {
  +        // removing first child
  +        oldInternal->isFirstChild(false);
  +        firstChild = oldInternal->nextSibling;
  +        if (firstChild != null) {
  +            firstChild->isFirstChild(true);
  +            firstChild->previousSibling = oldInternal->previousSibling;
  +        }
  +    } else {
  +        ChildNode *prev = oldInternal->previousSibling;
  +        ChildNode *next = oldInternal->nextSibling;
  +        prev->nextSibling = next;
  +        if (next == null) {
  +            // removing last child
  +            firstChild->previousSibling = prev;
  +        } else {
  +            // removing some other child in the middle
  +            next->previousSibling = prev;
  +        }
  +    }
  +
  +    // Remove oldInternal's references to tree
  +    oldInternal->ownerNode = ownerDocument;
  +    oldInternal->isOwned(false);
  +    oldInternal->nextSibling = null;
  +    oldInternal->previousSibling = null;
  +
  +    changed();
  +
  +    return oldInternal;
  +};
  +
  +
  +NodeImpl *ParentNode::replaceChild(NodeImpl *newChild, NodeImpl *oldChild)
  +{
  +    insertBefore(newChild, oldChild);
  +    // changed() already done.
  +    return removeChild(oldChild);
  +};
  +  
   
  -#define THIS_CLASS ParentNode
  -#define PARENT_CLASS NodeImpl
  +void ParentNode::setReadOnly(bool readOnl, bool deep)
  +{
  +    NodeImpl::setReadOnly(readOnl, deep);
  +      
  +    if (deep)
  +        // Recursively set kids
  +        for (ChildNode *mykid = firstChild; 
  +             mykid != null; 
  +             mykid = mykid->nextSibling)
  +            if(! (mykid->isEntityReference()))
  +                mykid->setReadOnly(readOnl,true);
  +};
  +  
  +  
  +//Introduced in DOM Level 2
  +  
  +void ParentNode::normalize()
  +{
  +    ChildNode *kid, *next;
  +    for (kid = firstChild; kid != null; kid = next)
  +    {
  +        next = kid->nextSibling;
  +        
  +        // If kid and next are both Text nodes (but _not_ CDATASection,
  +        // which is a subclass of Text), they can be merged.
  +        if (next != null && 
  +            kid->isTextImpl()   && !(kid->isCDATASectionImpl())  && 
  +            next->isTextImpl()  && !(next->isCDATASectionImpl()) )
  +        {
  +            ((TextImpl *) kid)->appendData(((TextImpl *) next)->getData());
  +            removeChild(next);
  +            if (next->nodeRefCount == 0)
  +                deleteIf(next);
  +            next = kid; // Don't advance; there might be another.
  +        }
  +        
  +        // Otherwise it might be an Element, which is handled recursively  
  +        else
  +            if (kid->isElementImpl())
  +                kid->normalize();
  +    };
  +    
  +    // changed() will have occurred when the removeChild() was done,
  +    // so does not have to be reissued.
  +};
   
  -#include "CommonParentNode.cpp"
  
  
  
  1.4       +66 -11    xml-xerces/c/src/dom/ParentNode.hpp
  
  Index: ParentNode.hpp
  ===================================================================
  RCS file: /home/cvs/xml-xerces/c/src/dom/ParentNode.hpp,v
  retrieving revision 1.3
  retrieving revision 1.4
  diff -u -r1.3 -r1.4
  --- ParentNode.hpp	2000/06/22 22:19:43	1.3
  +++ ParentNode.hpp	2001/01/18 02:49:56	1.4
  @@ -1,3 +1,5 @@
  +#ifndef ParentNode_HEADER_GUARD_
  +#define ParentNode_HEADER_GUARD_
   /*
    * The Apache Software License, Version 1.1
    * 
  @@ -64,31 +66,84 @@
   //
   
   /*
  - * $Id: ParentNode.hpp,v 1.3 2000/06/22 22:19:43 aruna1 Exp $
  + * $Id: ParentNode.hpp,v 1.4 2001/01/18 02:49:56 lehors Exp $
    */
   
   /**
  - * ParentNode inherits from NodeImpl and adds the capability of having child
  + * ParentNode inherits from ChildImpl and adds the capability of having child
    * nodes. Not every node in the DOM can have children, so only nodes that can
    * should inherit from this class and pay the price for it.
    * <P>
  + * ParentNode, just like NodeImpl, also implements NodeList, so it can
  + * return itself in response to the getChildNodes() query. This eliminiates
  + * the need for a separate ChildNodeList object. Note that this is an
  + * IMPLEMENTATION DETAIL; applications should _never_ assume that
  + * this identity exists.
  + * <P>
    * While we have a direct reference to the first child, the last child is
    * stored as the previous sibling of the first child. First child nodes are
    * marked as being so, and getNextSibling hides this fact.
  + * <P>Note: Not all parent nodes actually need to also be a child. At some
  + * point we used to have ParentNode inheriting from NodeImpl and another class
  + * called ChildAndParentNode that inherited from ChildNode. But due to the lack
  + * of multiple inheritance a lot of code had to be duplicated which led to a
  + * maintenance nightmare. At the same time only a few nodes (Document,
  + * DocumentFragment, Entity, and Attribute) cannot be a child so the gain is
  + * memory wasn't really worth it. The only type for which this would be the
  + * case is Attribute, but we deal with there in another special way, so this is
  + * not applicable.
  + *
  + * <p><b>WARNING</b>: Some of the code here is partially duplicated in
  + * AttrImpl, be careful to keep these two classes in sync!
    *
    **/
  -#ifndef ParentNode_HEADER_GUARD_
  -#define ParentNode_HEADER_GUARD_
   
   #include <util/XercesDefs.hpp>
   #include "ChildNode.hpp"
  -
  -#define THIS_CLASS ParentNode
  -#define PARENT_CLASS NodeImpl
  -
  -#include "CommonParentNode.hpp"
   
  -#undef THIS_CLASS
  -#undef PARENT_CLASS
  +class CDOM_EXPORT ParentNode: public ChildNode {
  +public:
  +    DocumentImpl            *ownerDocument; // Document this node belongs to
  +
  +    ChildNode                *firstChild;
  +
  +public:
  +    ParentNode(DocumentImpl *ownerDocument);
  +    ParentNode(const ParentNode &other);
  +    
  +    virtual DocumentImpl * getOwnerDocument();
  +    virtual void setOwnerDocument(DocumentImpl *doc);
  +
  +    virtual NodeListImpl *getChildNodes();
  +    virtual NodeImpl * getFirstChild();
  +    virtual NodeImpl * getLastChild();
  +    virtual unsigned int getLength();
  +    virtual bool        hasChildNodes();
  +    virtual NodeImpl    *insertBefore(NodeImpl *newChild, NodeImpl *refChild);
  +    virtual NodeImpl    *item(unsigned int index);
  +    virtual NodeImpl    * removeChild(NodeImpl *oldChild);
  +    virtual NodeImpl    *replaceChild(NodeImpl *newChild, NodeImpl *oldChild);
  +    virtual void        setReadOnly(bool isReadOnly, bool deep);
  +
  +    //Introduced in DOM Level 2
  +    virtual void	normalize();
  +
  +    // NON-DOM
  +    // unlike getOwnerDocument this never returns null, even for Document nodes
  +    virtual DocumentImpl * getDocument();
  +protected:
  +    void cloneChildren(const NodeImpl &other);
  +    ChildNode * lastChild();
  +    void lastChild(ChildNode *);
  +
  +    /** Cached node list length. */
  +    int fCachedLength;
  +
  +    /** Last requested child. */
  +    ChildNode * fCachedChild;
  +
  +    /** Last requested child index. */
  +    int fCachedChildIndex;
  +};
   
   #endif
  
  
  
  1.25      +18 -90    xml-xerces/c/tests/DOM/DOMMemTest/DOMMemTest.cpp
  
  Index: DOMMemTest.cpp
  ===================================================================
  RCS file: /home/cvs/xml-xerces/c/tests/DOM/DOMMemTest/DOMMemTest.cpp,v
  retrieving revision 1.24
  retrieving revision 1.25
  diff -u -r1.24 -r1.25
  --- DOMMemTest.cpp	2000/11/30 19:46:07	1.24
  +++ DOMMemTest.cpp	2001/01/18 02:49:57	1.25
  @@ -65,100 +65,7 @@
   //
   
   /*
  - * $Log: DOMMemTest.cpp,v $
  - * Revision 1.24  2000/11/30 19:46:07  andyh
  - * DOM_Text::splitText(), fix off by one error in the test for index too big error.
  - * Tinny Ng
  - *
  - * Revision 1.23  2000/10/13 22:47:37  andyh
  - * Fix bug (failure to null-terminate result) in XMLString::trim().
  - * Patch contributed by Nadav Aharoni
  - *
  - * Revision 1.22  2000/05/09 00:22:48  andyh
  - * Memory Cleanup.  XMLPlatformUtils::Terminate() deletes all lazily
  - * allocated memory; memory leak checking tools will no longer report
  - * that leaks exist.  (DOM GetElementsByTagID temporarily removed
  - * as part of this.)
  - *
  - * Revision 1.21  2000/04/18 01:07:28  aruna1
  - * Rectified memory leak caused by doctype-getNodeName()
  - *
  - * Revision 1.20  2000/03/11 03:00:13  chchou
  - * Fix bug # 18, remove set method of readonly attributes.
  - * As a result, remove related test cases
  - *
  - * Revision 1.19  2000/03/10 02:15:15  chchou
  - * add null DOM_DocumentType constructor
  - *
  - * Revision 1.18  2000/03/02 19:55:44  roddey
  - * This checkin includes many changes done while waiting for the
  - * 1.1.0 code to be finished. I can't list them all here, but a list is
  - * available elsewhere.
  - *
  - * Revision 1.17  2000/02/29 02:19:12  rahulj
  - * No more compilation errors under HPUX 11.0. We do not build
  - * DOMMemTest as it crashes the aCC compiler.
  - *
  - * Revision 1.16  2000/02/17 21:56:02  andyh
  - * Update to match W3C Dom Level 2 spec changes
  - *
  - * Revision 1.15  2000/02/15 23:15:28  andyh
  - * Add additional tests to DOMMemTest
  - *
  - * Revision 1.13  2000/02/06 07:48:35  rahulj
  - * Year 2K copyright swat.
  - *
  - * Revision 1.12  2000/02/05 01:19:19  andyh
  - * Add more DOMString tests.  Fix limit test error in DOMString::insertData()
  - * Andy Heninger  heninger@us.ibm.com
  - *
  - * Revision 1.11  2000/02/03 23:07:28  andyh
  - * Add several new functions from Robert Weir to DOMString.
  - *
  - * Revision 1.10  2000/01/29 00:39:09  andyh
  - * Redo synchronization in DOMStringHandle allocator.  There
  - * was a bug in the use of Compare and Swap.  Switched to mutexes.
  - *
  - * Changed a few plain deletes to delete [].
  - *
  - * Revision 1.9  2000/01/22 01:38:32  andyh
  - * Remove compiler warnings in DOM impl classes
  - *
  - * Revision 1.8  2000/01/20 20:37:25  andyh
  - * Remove DEVENV_VCPP preprocessor variable everywhere.
  - * It was obsolete, left over from an earlier configuration system.
  - * And it was not set correctly in all projects.
  - *
  - * Should fix build problem reported by some with use of
  - * InterlockedCompareExchange() on Windows with VC6.
  - *
  - * Revision 1.6  2000/01/19 21:40:58  andyh
  - * Remove a few remaining dependencies on the (now defunct)
  - * XML StdOut stream.
  - *
  - * Revision 1.5  2000/01/18 19:57:30  andyh
  - * Add some DOM L2 tests
  - *
  - * Revision 1.4  1999/12/17 02:09:41  andyh
  - * Fix bug in DOMString::insertData() that occured if the source
  - * and destination strings were the same and the orginal buffer had
  - * enough space to hold the result.
  - *
  - * Revision 1.3  1999/12/10 18:35:15  andyh
  - * Removed spurious debug output from DOMMemTest.
  - * Udated MSVC project files for Release build of DOMTest and DOMMemTest,
  - *    these weren't set up before.
  - *
  - * Revision 1.2  1999/12/03 00:02:25  andyh
  - * DOM tests updated to catch DOMException by ref.
  - * Added tests of DOMString::transcode() and append()
  - *
  - * Revision 1.1.1.1  1999/11/09 01:02:43  twl
  - * Initial checkin
  - *
  - * Revision 1.2  1999/11/08 20:42:24  rahul
  - * Swat for adding in Product name and CVS comment log variable.
  - *
  + * $Id: DOMMemTest.cpp,v 1.25 2001/01/18 02:49:57 lehors Exp $
    */
   
   #include <stdio.h>
  @@ -802,6 +709,23 @@
   
           attr01.setValue("Attr03Value1");
           attr01.setValue("Attr03Value2");
  +    }
  +    TESTEPILOG;
  +
  +
  +    //
  +    //  Attr04
  +    //
  +    TESTPROLOG;
  +    {
  +        DOM_Document    doc = DOM_Document::createDocument();
  +        DOM_Element     rootEl  = doc.createElement("RootElement");
  +        doc.appendChild(rootEl);
  +        DOM_Attr        attr01  = doc.createAttribute("Attr04");
  +        rootEl.setAttributeNode(attr01);
  +
  +        attr01.setValue("Attr04Value1");
  +        DOM_Node value = attr01.getFirstChild();
       }
       TESTEPILOG;