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/12 01:10:37 UTC

cvs commit: xml-xerces/java/src/org/apache/xerces/dom AttrImpl.java AttrNSImpl.java DeferredAttrImpl.java DeferredAttrNSImpl.java DocumentImpl.java DocumentTypeImpl.java ElementImpl.java EntityReferenceImpl.java NodeImpl.java ParentNode.java ChildAndParentNode.java

lehors      01/01/11 16:10:36

  Modified:    java/src/org/apache/xerces/dom AttrImpl.java AttrNSImpl.java
                        DeferredAttrImpl.java DeferredAttrNSImpl.java
                        DocumentImpl.java DocumentTypeImpl.java
                        ElementImpl.java EntityReferenceImpl.java
                        NodeImpl.java ParentNode.java
  Removed:     java/src/org/apache/xerces/dom ChildAndParentNode.java
  Log:
  This commit brinds yet another optimization to this DOM implementation.
  The whole idea is to 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.
  
  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...
  
  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.
  
  Revision  Changes    Path
  1.24      +858 -16   xml-xerces/java/src/org/apache/xerces/dom/AttrImpl.java
  
  Index: AttrImpl.java
  ===================================================================
  RCS file: /home/cvs/xml-xerces/java/src/org/apache/xerces/dom/AttrImpl.java,v
  retrieving revision 1.23
  retrieving revision 1.24
  diff -u -r1.23 -r1.24
  --- AttrImpl.java	2000/11/22 02:22:02	1.23
  +++ AttrImpl.java	2001/01/12 00:10:17	1.24
  @@ -57,6 +57,8 @@
   
   package org.apache.xerces.dom;
   
  +import java.io.*;
  +
   import org.w3c.dom.*;
   import org.w3c.dom.events.MutationEvent;
   import org.apache.xerces.dom.events.MutationEventImpl;
  @@ -96,13 +98,46 @@
    * <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...
  + *
  + * 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.
  + *
    * @see AttrNSImpl
    *
  + * @author Arnaud  Le Hors, IBM
  + * @author Joe Kesselman, IBM
  + * @author Andy Clark, IBM
    * @version
  - * @since  PR-DOM-Level-1-19980818.
  + * @since PR-DOM-Level-1-19980818.
  + *
    */
   public class AttrImpl
  -    extends ParentNode
  +    extends NodeImpl
       implements Attr {
   
       //
  @@ -116,9 +151,14 @@
       // Data
       //
   
  +    /** This can either be a String or the first child node. */
  +    protected Object value = null;
  +
       /** Attribute name. */
       protected String name;
   
  +    protected static TextImpl textNode = null;
  +
       //
       // Constructors
       //
  @@ -132,17 +172,49 @@
           this.name = name;
           /** False for default attributes. */
           isSpecified(true);
  +        hasStringValue(true);
       }
   
       // for AttrNS
       protected AttrImpl() {}
   
  +    // create a real text node as child if we don't have one yet
  +    protected void makeChildNode() {
  +        if (hasStringValue()) {
  +            if (value != null) {
  +                TextImpl text =
  +                    (TextImpl) ownerDocument().createTextNode((String) value);
  +                value = text;
  +                text.isFirstChild(true);
  +                text.previousSibling = text;
  +                text.ownerNode = this;
  +                text.isOwned(true);
  +            }
  +            hasStringValue(false);
  +        }
  +    }
  +
       //
       // Node methods
       //
       
       public Node cloneNode(boolean deep) {
           AttrImpl clone = (AttrImpl) super.cloneNode(deep);
  +
  +        // take care of case where there are kids
  +    	if (!clone.hasStringValue()) {
  +
  +            // Need to break the association w/ original kids
  +            clone.value = null;
  +
  +            // Then, if deep, clone the kids too.
  +            if (deep) {
  +                for (Node child = (Node) value; child != null;
  +                     child = child.getNextSibling()) {
  +                    clone.appendChild(child.cloneNode(true));
  +                }
  +            }
  +        }
           clone.isSpecified(true);
           return clone;
       }
  @@ -207,16 +279,16 @@
        * as "remove all children", which from outside should appear
        * similar to setting it to the empty string.
        */
  -    public void setValue(String value) {
  +    public void setValue(String newvalue) {
   
       	if (isReadOnly()) {
  -    		throw new DOMException(
  -    			DOMException.NO_MODIFICATION_ALLOWED_ERR, 
  -    			"DOM001 Modification not allowed");
  +            throw new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR, 
  +                                   "DOM001 Modification not allowed");
           }
       		
           LCount lc=null;
           String oldvalue="";
  +        DocumentImpl ownerDocument = ownerDocument();
           if(MUTATIONEVENTS && ownerDocument.mutationEvents)
           {
               // MUTATION PREPROCESSING AND PRE-EVENTS:
  @@ -237,20 +309,44 @@
               // event listeners waiting for them to disconnect.
               if (needsSyncChildren()) {
                   synchronizeChildren();
  +            }
  +            if (value != null) {
  +                if (hasStringValue()) {
  +                    // temporarily sets an actual text node as our child so
  +                    // that we can use it in the event
  +                    if (textNode == null) {
  +                        textNode = (TextImpl)
  +                            ownerDocument.createTextNode((String) value);
  +                    }
  +                    else {
  +                        textNode.data = (String) value;
  +                    }
  +                    value = textNode;
  +                    textNode.isFirstChild(true);
  +                    textNode.previousSibling = textNode;
  +                    textNode.ownerNode = this;
  +                    textNode.isOwned(true);
  +                    hasStringValue(false);
  +                    internalRemoveChild(textNode, MUTATION_LOCAL);
  +                }
  +                else {
  +                    while (value != null) {
  +                        internalRemoveChild((Node) value, MUTATION_LOCAL);
  +                    }
  +                }
               }
  -            while(firstChild!=null)
  -                internalRemoveChild(firstChild,MUTATION_LOCAL);
           }
           else
           {
  -            // simply discard children
  -            if (firstChild != null) {
  +            // simply discard children if any
  +            if (!hasStringValue() && value != null) {
                   // remove ref from first child to last child
  +                ChildNode firstChild = (ChildNode) value;
                   firstChild.previousSibling = null;
                   firstChild.isFirstChild(false);
  -                // then remove ref to first child
  -                firstChild   = null;
               }
  +            // then remove ref to current value
  +            value = null;
               needsSyncChildren(false);
           }
   
  @@ -260,9 +356,17 @@
           // Note that aggregate events are NOT dispatched here,
           // since we need to combine the remove and insert.
       	isSpecified(true);
  -        if (value != null) {
  -            internalInsertBefore(ownerDocument.createTextNode(value),null,
  -                                 MUTATION_LOCAL);
  +        if (newvalue != null) {
  +            if(MUTATIONEVENTS && ownerDocument.mutationEvents) {
  +                // if there are any event handlers create a real node
  +                internalInsertBefore(ownerDocument.createTextNode(newvalue),
  +                                     null, MUTATION_LOCAL);
  +                hasStringValue(false);
  +            } else {
  +                // directly store the string
  +                value = newvalue;
  +                hasStringValue(true);
  +            }
           }
   		
       	changed(); // ***** Is this redundant?
  @@ -284,9 +388,13 @@
           if (needsSyncChildren()) {
               synchronizeChildren();
           }
  -        if (firstChild == null) {
  +        if (value == null) {
               return "";
           }
  +        if (hasStringValue()) {
  +            return (String) value;
  +        }
  +        ChildNode firstChild = ((ChildNode) value);
           ChildNode node = firstChild.nextSibling;
           if (node == null) {
               return firstChild.getNodeValue();
  @@ -353,7 +461,12 @@
       
       public void normalize() {
   
  +        if (hasStringValue()) {
  +            return;
  +        }
  +
           Node kid, next;
  +        ChildNode firstChild = (ChildNode)value;
           for (kid = firstChild; kid != null; kid = next) {
               next = kid.getNextSibling();
   
  @@ -404,5 +517,734 @@
       public String toString() {
       	return getName() + "=" + "\"" + getValue() + "\"";
       }
  +
  +    /**
  +     * Test whether this node has any children. Convenience shorthand
  +     * for (Node.getFirstChild()!=null)
  +     */
  +    public boolean hasChildNodes() {
  +        if (needsSyncChildren()) {
  +            synchronizeChildren();
  +        }
  +        return value != null;
  +    }
  +
  +    /**
  +     * Obtain a NodeList enumerating all children of this node. If there
  +     * are none, an (initially) empty NodeList is returned.
  +     * <p>
  +     * NodeLists are "live"; as children are added/removed the NodeList
  +     * will immediately reflect those changes. Also, the NodeList refers
  +     * to the actual nodes, so changes to those nodes made via the DOM tree
  +     * will be reflected in the NodeList and vice versa.
  +     * <p>
  +     * In this implementation, Nodes implement the NodeList interface and
  +     * provide their own getChildNodes() support. Other DOMs may solve this
  +     * differently.
  +     */
  +    public NodeList getChildNodes() {
  +        // JKESS: KNOWN ISSUE HERE 
  +
  +        if (needsSyncChildren()) {
  +            synchronizeChildren();
  +        }
  +        return this;
  +
  +    } // getChildNodes():NodeList
  +
  +    /** The first child of this Node, or null if none. */
  +    public Node getFirstChild() {
  +
  +        if (needsSyncChildren()) {
  +            synchronizeChildren();
  +        }
  +        makeChildNode();
  +    	return (Node) value;
  +
  +    }   // getFirstChild():Node
  +
  +    /** The last child of this Node, or null if none. */
  +    public Node getLastChild() {
  +
  +        if (needsSyncChildren()) {
  +            synchronizeChildren();
  +        }
  +        return lastChild();
  +
  +    } // getLastChild():Node
  +
  +    final ChildNode lastChild() {
  +        // last child is stored as the previous sibling of first child
  +        makeChildNode();
  +        return value != null ? ((ChildNode) value).previousSibling : null;
  +    }
  +
  +    final void lastChild(ChildNode node) {
  +        // store lastChild as previous sibling of first child
  +        if (value != null) {
  +            ((ChildNode) value).previousSibling = node;
  +        }
  +    }
  +
  +    /**
  +     * Move one or more node(s) to our list of children. Note that this
  +     * implicitly removes them from their previous parent.
  +     *
  +     * @param newChild The Node to be moved to our subtree. As a
  +     * convenience feature, inserting a DocumentNode will instead insert
  +     * all its children.
  +     *
  +     * @param refChild Current child which newChild should be placed
  +     * immediately before. If refChild is null, the insertion occurs
  +     * after all existing Nodes, like appendChild().
  +     *
  +     * @returns newChild, in its new state (relocated, or emptied in the
  +     * case of DocumentNode.)
  +     *
  +     * @throws DOMException(HIERARCHY_REQUEST_ERR) if newChild is of a
  +     * type that shouldn't be a child of this node, or if newChild is an
  +     * ancestor of this node.
  +     *
  +     * @throws DOMException(WRONG_DOCUMENT_ERR) if newChild has a
  +     * different owner document than we do.
  +     *
  +     * @throws DOMException(NOT_FOUND_ERR) if refChild is not a child of
  +     * this node.
  +     *
  +     * @throws DOMException(NO_MODIFICATION_ALLOWED_ERR) if this node is
  +     * read-only.
  +     */
  +    public Node insertBefore(Node newChild, Node refChild) 
  +        throws DOMException {
  +        // Tail-call; optimizer should be able to do good things with.
  +        return internalInsertBefore(newChild,refChild,MUTATION_ALL);
  +    } // insertBefore(Node,Node):Node
  +     
  +    /** NON-DOM INTERNAL: Within DOM actions,we sometimes need to be able
  +     * to control which mutation events are spawned. This version of the
  +     * insertBefore operation allows us to do so. It is not intended
  +     * for use by application programs.
  +     */
  +    Node internalInsertBefore(Node newChild, Node refChild,int mutationMask) 
  +        throws DOMException {
  +
  +    	if (isReadOnly())
  +            throw new DOMException(
  +                        DOMException.NO_MODIFICATION_ALLOWED_ERR, 
  +                        "DOM001 Modification not allowed");
  +
  +        DocumentImpl ownerDocument = ownerDocument();
  +        boolean errorChecking = ownerDocument.errorChecking;
  +    	if (errorChecking && newChild.getOwnerDocument() != ownerDocument) {
  +            throw new DOMException(DOMException.WRONG_DOCUMENT_ERR, 
  +                                       "DOM005 Wrong document");
  +        }
  +
  +        if (needsSyncChildren()) {
  +            synchronizeChildren();
  +        }
  +
  +        if (errorChecking) {
  +            // Prevent cycles in the tree
  +            boolean treeSafe = true;
  +            for (NodeImpl a = parentNode();
  +                 treeSafe && a != null;
  +                 a = a.parentNode()) {
  +                treeSafe = newChild != a;
  +            }
  +            if(!treeSafe) {
  +                throw new DOMException(DOMException.HIERARCHY_REQUEST_ERR, 
  +                                           "DOM006 Hierarchy request error");
  +            }
  +
  +            // refChild must in fact be a child of this node (or null)
  +            if(refChild != null && refChild.getParentNode() != this) {
  +                throw new DOMException(DOMException.NOT_FOUND_ERR,
  +                                           "DOM008 Not found");
  +            }
  +        }
  +        
  +        if (newChild.getNodeType() == Node.DOCUMENT_FRAGMENT_NODE) {
  +            // 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 (Node kid = newChild.getFirstChild(); // Prescan
  +                 kid != null;
  +                 kid = kid.getNextSibling()) {
  +
  +                if (errorChecking && !ownerDocument.isKidOK(this, kid)) {
  +                    throw new DOMException(
  +                                           DOMException.HIERARCHY_REQUEST_ERR, 
  +                                           "DOM006 Hierarchy request error");
  +                }
  +            }
  +
  +            while (newChild.hasChildNodes()) {
  +                insertBefore(newChild.getFirstChild(), refChild);
  +            }
  +        }
  +        else if (errorChecking &&
  +                 (!(newChild instanceof ChildNode)
  +                  ||
  +                  !ownerDocument.isKidOK(this, newChild))) {
  +            throw new DOMException(DOMException.HIERARCHY_REQUEST_ERR, 
  +                                       "DOM006 Hierarchy request error");
  +        }
  +        else {
  +            makeChildNode(); // make sure we have a node and not a string
  +
  +            // Convert to internal type, to avoid repeated casting
  +            ChildNode newInternal = (ChildNode)newChild;
  +
  +            EnclosingAttr enclosingAttr=null;
  +            if(MUTATIONEVENTS && ownerDocument.mutationEvents
  +               && (mutationMask&MUTATION_AGGREGATE)!=0)
  +            {
  +                // MUTATION PREPROCESSING
  +                // No direct pre-events, but if we're within the scope 
  +    	        // of an Attr and DOMAttrModified was requested,
  +                // we need to preserve its previous value.
  +                LCount lc=LCount.lookup(MutationEventImpl.DOM_ATTR_MODIFIED);
  +                if(lc.captures+lc.bubbles+lc.defaults>0)
  +                {
  +                    enclosingAttr=getEnclosingAttr();
  +                }
  +            }
  +
  +            Node oldparent = newInternal.parentNode();
  +            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(MUTATIONEVENTS && ownerDocument.mutationEvents)
  +            {
  +                // MUTATION POST-EVENTS:
  +                // "Local" events (non-aggregated)
  +                if( (mutationMask&MUTATION_LOCAL) != 0)
  +                {
  +                    // New child is told it was inserted, and where
  +                    LCount lc =
  +                        LCount.lookup(MutationEventImpl.DOM_NODE_INSERTED);
  +                    if(lc.captures+lc.bubbles+lc.defaults>0)
  +                    {
  +                        MutationEvent me= new MutationEventImpl();
  +                        me.initMutationEvent(
  +                                          MutationEventImpl.DOM_NODE_INSERTED,
  +                                          true,false,this,null,
  +                                          null,null,(short)0);
  +                        newInternal.dispatchEvent(me);
  +                    }
  +
  +                    // If within the Document, tell the subtree it's been added
  +                    // to the Doc.
  +                    lc=LCount.lookup(
  +                            MutationEventImpl.DOM_NODE_INSERTED_INTO_DOCUMENT);
  +                    if(lc.captures+lc.bubbles+lc.defaults>0)
  +                    {
  +                        NodeImpl eventAncestor=this;
  +                        if(enclosingAttr!=null) 
  +                            eventAncestor=
  +                              (NodeImpl)(enclosingAttr.node.getOwnerElement());
  +                        if(eventAncestor!=null) // Might have been orphan Attr
  +                        {
  +                            NodeImpl p=eventAncestor;
  +                            while(p!=null)
  +                            {
  +                                eventAncestor=p; // Last non-null ancestor
  +                                // In this context, ancestry includes
  +                                // walking back from Attr to Element
  +                                if(p.getNodeType()==ATTRIBUTE_NODE)
  +                                    p=(ElementImpl)
  +                                        ((AttrImpl)p).getOwnerElement();
  +                                else
  +                                    p=p.parentNode();
  +                            }
  +                            if(eventAncestor.getNodeType()==Node.DOCUMENT_NODE)
  +                            {
  +                                MutationEvent me= new MutationEventImpl();
  +                                me.initMutationEvent(MutationEventImpl
  +                                              .DOM_NODE_INSERTED_INTO_DOCUMENT,
  +                                                     false,false,null,null,
  +                                                     null,null,(short)0);
  +                                dispatchEventToSubtree(newInternal,me);
  +                            }
  +                        }
  +                    }
  +                }
  +
  +                // Subroutine: Transmit DOMAttrModified and DOMSubtreeModified
  +                // (Common to most kinds of mutation)
  +                if( (mutationMask&MUTATION_AGGREGATE) != 0)
  +                    dispatchAggregateEvents(enclosingAttr);
  +            }
  +        }
  +        return newChild;
  +
  +    } // internalInsertBefore(Node,Node,int):Node
  +
  +    /**
  +     * Remove a child from this Node. The removed child's subtree
  +     * remains intact so it may be re-inserted elsewhere.
  +     *
  +     * @return oldChild, in its new state (removed).
  +     *
  +     * @throws DOMException(NOT_FOUND_ERR) if oldChild is not a child of
  +     * this node.
  +     *
  +     * @throws DOMException(NO_MODIFICATION_ALLOWED_ERR) if this node is
  +     * read-only.
  +     */
  +    public Node removeChild(Node oldChild) 
  +        throws DOMException {
  +        // Tail-call, should be optimizable
  +        if (hasStringValue()) {
  +            // we don't have any child per say so it can't be one of them!
  +            throw new DOMException(DOMException.NOT_FOUND_ERR, 
  +                                   "DOM008 Not found");
  +        }
  +        return internalRemoveChild(oldChild,MUTATION_ALL);
  +    } // removeChild(Node) :Node
  +     
  +    /** NON-DOM INTERNAL: Within DOM actions,we sometimes need to be able
  +     * to control which mutation events are spawned. This version of the
  +     * removeChild operation allows us to do so. It is not intended
  +     * for use by application programs.
  +     */
  +    Node internalRemoveChild(Node oldChild,int mutationMask)
  +        throws DOMException {
  +
  +        if (isReadOnly()) {
  +            throw new DOMException(
  +                DOMException.NO_MODIFICATION_ALLOWED_ERR, 
  +                "DOM001 Modification not allowed");
  +        }
  +         
  +        DocumentImpl ownerDocument = ownerDocument();
  +        if (ownerDocument.errorChecking && 
  +            oldChild != null && oldChild.getParentNode() != this) {
  +            System.err.println("oldChild: " + oldChild +
  +                               " parentNode: " + oldChild.getParentNode() +
  +                               " this: " + this + 
  +                               " value: " + value);
  +            throw new DOMException(DOMException.NOT_FOUND_ERR, 
  +                                   "DOM008 Not found");
  +        }
  +
  +        // notify document
  +        ownerDocument.removedChildNode(oldChild);
  +
  +        ChildNode oldInternal = (ChildNode) oldChild;
  +
  +        EnclosingAttr enclosingAttr=null;
  +        if(MUTATIONEVENTS && ownerDocument.mutationEvents)
  +        {
  +            // MUTATION PREPROCESSING AND PRE-EVENTS:
  +            // If we're within the scope of an Attr and DOMAttrModified 
  +            // was requested, we need to preserve its previous value for
  +            // that event.
  +            LCount lc=LCount.lookup(MutationEventImpl.DOM_ATTR_MODIFIED);
  +            if(lc.captures+lc.bubbles+lc.defaults>0)
  +            {
  +                enclosingAttr=getEnclosingAttr();
  +            }
  +            
  +            if( (mutationMask&MUTATION_LOCAL) != 0)
  +            {
  +                // Child is told that it is about to be removed
  +                lc=LCount.lookup(MutationEventImpl.DOM_NODE_REMOVED);
  +                if(lc.captures+lc.bubbles+lc.defaults>0)
  +                {
  +                    MutationEvent me= new MutationEventImpl();
  +                    me.initMutationEvent(MutationEventImpl.DOM_NODE_REMOVED,
  +                                         true,false,this,null,
  +                                         null,null,(short)0);
  +                    oldInternal.dispatchEvent(me);
  +                }
  +            
  +                // If within Document, child's subtree is informed that it's
  +                // losing that status
  +                lc=LCount.lookup(
  +                             MutationEventImpl.DOM_NODE_REMOVED_FROM_DOCUMENT);
  +                if(lc.captures+lc.bubbles+lc.defaults>0)
  +                {
  +                    NodeImpl eventAncestor=this;
  +                    if(enclosingAttr!=null) 
  +                        eventAncestor=
  +                            (NodeImpl) enclosingAttr.node.getOwnerElement();
  +                    if(eventAncestor!=null) // Might have been orphan Attr
  +                    {
  +                        for(NodeImpl p=eventAncestor.parentNode();
  +                            p!=null;
  +                            p=p.parentNode())
  +                        {
  +                            eventAncestor=p; // Last non-null ancestor
  +                        }
  +                        if(eventAncestor.getNodeType()==Node.DOCUMENT_NODE)
  +                        {
  +                            MutationEvent me= new MutationEventImpl();
  +                            me.initMutationEvent(MutationEventImpl
  +                                               .DOM_NODE_REMOVED_FROM_DOCUMENT,
  +                                                 false,false,
  +                                                 null,null,null,null,(short)0);
  +                            dispatchEventToSubtree(oldInternal,me);
  +                        }
  +                    }
  +                }
  +            }
  +        } // End mutation preprocessing
  +
  +        // Patch linked list around oldChild
  +        // Note: lastChild == firstChild.previousSibling
  +        if (oldInternal == value) { // oldInternal == firstChild
  +            // 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       = ownerDocument;
  +        oldInternal.isOwned(false);
  +        oldInternal.nextSibling     = null;
  +        oldInternal.previousSibling = null;
  +
  +        changed();
  +
  +        if(MUTATIONEVENTS && ownerDocument.mutationEvents)
  +        {
  +            // MUTATION POST-EVENTS:
  +            // Subroutine: Transmit DOMAttrModified and DOMSubtreeModified,
  +            // if required. (Common to most kinds of mutation)
  +            if( (mutationMask&MUTATION_AGGREGATE) != 0)
  +                dispatchAggregateEvents(enclosingAttr);
  +        } // End mutation postprocessing
  +
  +        return oldInternal;
  +
  +    } // internalRemoveChild(Node,int):Node
  +
  +    /**
  +     * Make newChild occupy the location that oldChild used to
  +     * have. Note that newChild will first be removed from its previous
  +     * parent, if any. Equivalent to inserting newChild before oldChild,
  +     * then removing oldChild.
  +     *
  +     * @returns oldChild, in its new state (removed).
  +     *
  +     * @throws DOMException(HIERARCHY_REQUEST_ERR) if newChild is of a
  +     * type that shouldn't be a child of this node, or if newChild is
  +     * one of our ancestors.
  +     *
  +     * @throws DOMException(WRONG_DOCUMENT_ERR) if newChild has a
  +     * different owner document than we do.
  +     *
  +     * @throws DOMException(NOT_FOUND_ERR) if oldChild is not a child of
  +     * this node.
  +     *
  +     * @throws DOMException(NO_MODIFICATION_ALLOWED_ERR) if this node is
  +     * read-only.
  +     */
  +    public Node replaceChild(Node newChild, Node oldChild)
  +        throws DOMException {
  +
  +        makeChildNode();
  +
  +        // If Mutation Events are being generated, this operation might
  +        // throw aggregate events twice when modifying an Attr -- once 
  +        // on insertion and once on removal. DOM Level 2 does not specify 
  +        // this as either desirable or undesirable, but hints that
  +        // aggregations should be issued only once per user request.
  +
  +        EnclosingAttr enclosingAttr=null;
  +        DocumentImpl ownerDocument = ownerDocument();
  +        if(MUTATIONEVENTS && ownerDocument.mutationEvents)
  +        {
  +            // MUTATION PREPROCESSING AND PRE-EVENTS:
  +            // If we're within the scope of an Attr and DOMAttrModified 
  +            // was requested, we need to preserve its previous value for
  +            // that event.
  +            LCount lc=LCount.lookup(MutationEventImpl.DOM_ATTR_MODIFIED);
  +            if(lc.captures+lc.bubbles+lc.defaults>0)
  +            {
  +                enclosingAttr=getEnclosingAttr();
  +            }
  +        } // End mutation preprocessing
  +
  +        internalInsertBefore(newChild, oldChild,MUTATION_LOCAL);
  +        internalRemoveChild(oldChild,MUTATION_LOCAL);
  +
  +        if(MUTATIONEVENTS && ownerDocument.mutationEvents)
  +        {
  +            dispatchAggregateEvents(enclosingAttr);
  +        }
  +
  +        return oldChild;
  +    }
  +
  +    //
  +    // NodeList methods
  +    //
  +
  +    /**
  +     * NodeList method: Count the immediate children of this node
  +     * @return int
  +     */
  +    public int getLength() {
  +
  +        if (hasStringValue()) {
  +            return 1;
  +        }
  +        ChildNode node = (ChildNode) value;
  +        int length = 0;
  +        for (; node != null; node = node.nextSibling) {
  +            length++;
  +        }
  +        return length;
  +
  +    } // getLength():int
  +
  +    /**
  +     * NodeList method: Return the Nth immediate child of this node, or
  +     * null if the index is out of bounds.
  +     * @return org.w3c.dom.Node
  +     * @param Index int
  +     */
  +    public Node item(int index) {
  +
  +        if (hasStringValue()) {
  +            if (index != 0 || value == null) {
  +                return null;
  +            }
  +            else {
  +                makeChildNode();
  +                return (Node) value;
  +            }
  +        }
  +        ChildNode nodeListNode = (ChildNode) value;
  +        for (int nodeListIndex = 0; 
  +             nodeListIndex < index && nodeListNode != null; 
  +             nodeListIndex++) {
  +            nodeListNode = nodeListNode.nextSibling;
  +        }
  +        return nodeListNode;
  +
  +    } // item(int):Node
  +
  +    //
  +    // DOM2: methods, getters, setters
  +    //
  +
  +    //
  +    // Public methods
  +    //
  +
  +    /**
  +     * Override default behavior so that if deep is true, children are also
  +     * toggled.
  +     * @see Node
  +     * <P>
  +     * Note: this will not change the state of an EntityReference or its
  +     * children, which are always read-only.
  +     */
  +    public void setReadOnly(boolean readOnly, boolean deep) {
  +
  +        super.setReadOnly(readOnly, deep);
  +
  +        if (deep) {
  +
  +            if (needsSyncChildren()) {
  +                synchronizeChildren();
  +            }
  +
  +            if (hasStringValue()) {
  +                return;
  +            }
  +            // Recursively set kids
  +            for (ChildNode mykid = (ChildNode) value;
  +                 mykid != null;
  +                 mykid = mykid.nextSibling) {
  +                if (mykid.getNodeType() != Node.ENTITY_REFERENCE_NODE) {
  +                    mykid.setReadOnly(readOnly,true);
  +                }
  +            }
  +        }
  +    } // setReadOnly(boolean,boolean)
  +
  +    //
  +    // Protected methods
  +    //
  +
  +    /**
  +     * Override this method in subclass to hook in efficient
  +     * internal data structure.
  +     */
  +    protected void synchronizeChildren() {
  +        // By default just change the flag to avoid calling this method again
  +        needsSyncChildren(false);
  +    }
  +
  +    /**
  +     * Synchronizes the node's children with the internal structure.
  +     * Fluffing the children at once solves a lot of work to keep
  +     * the two structures in sync. The problem gets worse when
  +     * editing the tree -- this makes it a lot easier.
  +     * Even though this is only used in deferred classes this method is
  +     * put here so that it can be shared by all deferred classes.
  +     */
  +    protected final void synchronizeChildren(int nodeIndex) {
  +
  +        // we don't want to generate any event for this so turn them off
  +        DeferredDocumentImpl ownerDocument =
  +            (DeferredDocumentImpl) ownerDocument();
  +        boolean orig = ownerDocument.mutationEvents;
  +        ownerDocument.mutationEvents = false;
  +
  +        // no need to sync in the future
  +        needsSyncChildren(false);
  +
  +        // create children and link them as siblings or simply store the value
  +        // as a String if all we have is one piece of text
  +        int last = ownerDocument.getLastChild(nodeIndex);
  +        int prev = ownerDocument.getPrevSibling(last);
  +        if (prev == -1) {
  +            value = ownerDocument.getNodeValueString(last);
  +            hasStringValue(true);
  +        }
  +        else {
  +            ChildNode firstNode = null;
  +            ChildNode lastNode = null;
  +            for (int index = last; index != -1;
  +                 index = ownerDocument.getPrevSibling(index)) {
  +
  +                ChildNode node = (ChildNode)ownerDocument.getNodeObject(index);
  +                if (lastNode == null) {
  +                    lastNode = node;
  +                }
  +                else {
  +                    firstNode.previousSibling = node;
  +                }
  +                node.ownerNode = this;
  +                node.isOwned(true);
  +                node.nextSibling = firstNode;
  +                firstNode = node;
  +            }
  +            if (lastNode != null) {
  +                value = firstNode; // firstChild = firstNode
  +                firstNode.isFirstChild(true);
  +                lastChild(lastNode);
  +            }
  +            hasStringValue(false);
  +        }
  +
  +        // set mutation events flag back to its original value
  +        ownerDocument.mutationEvents = orig;
  +
  +    } // synchronizeChildren()
  +
  +    //
  +    // Serialization methods
  +    //
  +
  +    /** Serialize object. */
  +    private void writeObject(ObjectOutputStream out) throws IOException {
  +
  +        // synchronize chilren
  +        if (needsSyncChildren()) {
  +            synchronizeChildren();
  +        }
  +        // write object
  +        out.defaultWriteObject();
  +
  +    } // writeObject(ObjectOutputStream)
  +
  +    /** Deserialize object. */
  +    private void readObject(ObjectInputStream ois)
  +        throws ClassNotFoundException, IOException {
  +
  +        // perform default deseralization
  +        ois.defaultReadObject();
  +
  +        // hardset synchildren - so we don't try to sync- it does not make any sense
  +        // to try to synchildren when we just desealize object.
  +
  +        needsSyncChildren(false);
  +
  +    } // readObject(ObjectInputStream)
   
   } // class AttrImpl
  
  
  
  1.18      +5 -2      xml-xerces/java/src/org/apache/xerces/dom/AttrNSImpl.java
  
  Index: AttrNSImpl.java
  ===================================================================
  RCS file: /home/cvs/xml-xerces/java/src/org/apache/xerces/dom/AttrNSImpl.java,v
  retrieving revision 1.17
  retrieving revision 1.18
  diff -u -r1.17 -r1.18
  --- AttrNSImpl.java	2000/10/02 21:58:11	1.17
  +++ AttrNSImpl.java	2001/01/12 00:10:19	1.18
  @@ -1,4 +1,4 @@
  -/* $Id: AttrNSImpl.java,v 1.17 2000/10/02 21:58:11 lehors Exp $ */
  +/* $Id: AttrNSImpl.java,v 1.18 2001/01/12 00:10:19 lehors Exp $ */
   /*
    * The Apache Software License, Version 1.1
    *
  @@ -68,6 +68,9 @@
    * The qualified name is the node name, and we store localName which is also
    * used in all queries. On the other hand we recompute the prefix when
    * necessary.
  + * @author Arnaud  Le Hors, IBM
  + * @author Andy Clark, IBM
  + * @author Ralf Pfeiffer, IBM
    */
   public class AttrNSImpl
       extends AttrImpl {
  @@ -211,7 +214,7 @@
       	    throw new DOMException(DOMException.NAMESPACE_ERR, 
   				       "DOM003 Namespace error");
       	}
  -	if (ownerDocument.errorChecking && !DocumentImpl.isXMLName(prefix)) {
  +	if (ownerDocument().errorChecking && !DocumentImpl.isXMLName(prefix)) {
       	    throw new DOMException(DOMException.INVALID_CHARACTER_ERR, 
       	                               "DOM002 Illegal character");
           }
  
  
  
  1.10      +3 -1      xml-xerces/java/src/org/apache/xerces/dom/DeferredAttrImpl.java
  
  Index: DeferredAttrImpl.java
  ===================================================================
  RCS file: /home/cvs/xml-xerces/java/src/org/apache/xerces/dom/DeferredAttrImpl.java,v
  retrieving revision 1.9
  retrieving revision 1.10
  diff -u -r1.9 -r1.10
  --- DeferredAttrImpl.java	2000/07/07 00:36:09	1.9
  +++ DeferredAttrImpl.java	2001/01/12 00:10:21	1.10
  @@ -100,6 +100,8 @@
    * @see DeferredAttrNSImpl
    *
    *
  + * @author Andy Clark, IBM
  + * @author Arnaud  Le Hors, IBM
    * @version
    * @since  PR-DOM-Level-1-19980818.
    */
  @@ -159,7 +161,7 @@
   
           // fluff data
           DeferredDocumentImpl ownerDocument =
  -            (DeferredDocumentImpl) this.ownerDocument;
  +            (DeferredDocumentImpl) ownerDocument();
           int elementTypeName = ownerDocument.getNodeName(fNodeIndex);
           StringPool pool = ownerDocument.getStringPool();
           name = pool.toString(elementTypeName);
  
  
  
  1.11      +4 -2      xml-xerces/java/src/org/apache/xerces/dom/DeferredAttrNSImpl.java
  
  Index: DeferredAttrNSImpl.java
  ===================================================================
  RCS file: /home/cvs/xml-xerces/java/src/org/apache/xerces/dom/DeferredAttrNSImpl.java,v
  retrieving revision 1.10
  retrieving revision 1.11
  diff -u -r1.10 -r1.11
  --- DeferredAttrNSImpl.java	2000/07/07 00:36:09	1.10
  +++ DeferredAttrNSImpl.java	2001/01/12 00:10:22	1.11
  @@ -61,7 +61,7 @@
    * DeferredAttrImpl.java at the same time.
    */
   
  -/* $Id: DeferredAttrNSImpl.java,v 1.10 2000/07/07 00:36:09 lehors Exp $ */
  +/* $Id: DeferredAttrNSImpl.java,v 1.11 2001/01/12 00:10:22 lehors Exp $ */
   
   
   package org.apache.xerces.dom;
  @@ -73,6 +73,8 @@
   /**
    * DeferredAttrNSImpl is to AttrNSImpl, what DeferredAttrImpl is to
    * AttrImpl. 
  + * @author Andy Clark, IBM
  + * @author Arnaud  Le Hors, IBM
    * @see DeferredAttrImpl
    */
   public final class DeferredAttrNSImpl
  @@ -131,7 +133,7 @@
   
           // fluff data
           DeferredDocumentImpl ownerDocument =
  -	    (DeferredDocumentImpl) this.ownerDocument;
  +	    (DeferredDocumentImpl) ownerDocument();
           int attrQName = ownerDocument.getNodeName(fNodeIndex);
           StringPool pool = ownerDocument.getStringPool();
           name = pool.toString(attrQName);
  
  
  
  1.43      +26 -6     xml-xerces/java/src/org/apache/xerces/dom/DocumentImpl.java
  
  Index: DocumentImpl.java
  ===================================================================
  RCS file: /home/cvs/xml-xerces/java/src/org/apache/xerces/dom/DocumentImpl.java,v
  retrieving revision 1.42
  retrieving revision 1.43
  diff -u -r1.42 -r1.43
  --- DocumentImpl.java	2000/11/17 15:38:19	1.42
  +++ DocumentImpl.java	2001/01/12 00:10:23	1.43
  @@ -91,6 +91,10 @@
    * <b>Note:</b> When any node in the document is serialized, the
    * entire document is serialized along with it.
    *
  + * @author Arnaud  Le Hors, IBM
  + * @author Joe Kesselman, IBM
  + * @author Andy Clark, IBM
  + * @author Ralf Pfeiffer, IBM
    * @version
    * @since  PR-DOM-Level-1-19980818.
    */
  @@ -839,13 +843,29 @@
            	        newnode = createAttribute(source.getNodeName());
            	    } else {
             	        newnode = createAttributeNS(source.getNamespaceURI(),
  -          					source.getNodeName());
  +                                                    source.getNodeName());
            	    }
  -               } else {
  -                   newnode = createAttribute(source.getNodeName());
  -               }
  -                deep = true;
  -		// Kids carry value
  +                }
  +                else {
  +                    newnode = createAttribute(source.getNodeName());
  +                }
  +                // 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((String) attr.value);
  +                        deep = false;
  +                    }
  +                    else {
  +                        deep = true;
  +                    }
  +                }
  +                else {
  +                    // Kids carry value
  +                    deep = true;
  +                }
   		break;
               }
   
  
  
  
  1.16      +4 -1      xml-xerces/java/src/org/apache/xerces/dom/DocumentTypeImpl.java
  
  Index: DocumentTypeImpl.java
  ===================================================================
  RCS file: /home/cvs/xml-xerces/java/src/org/apache/xerces/dom/DocumentTypeImpl.java,v
  retrieving revision 1.15
  retrieving revision 1.16
  diff -u -r1.15 -r1.16
  --- DocumentTypeImpl.java	2000/12/30 00:28:47	1.15
  +++ DocumentTypeImpl.java	2001/01/12 00:10:23	1.16
  @@ -76,11 +76,14 @@
    * and using Element and Attribute information. Nor was the linkage
    * between Entities and Entity References nailed down solidly.
    *
  + * @author Arnaud  Le Hors, IBM
  + * @author Joe Kesselman, IBM
  + * @author Andy Clark, IBM
    * @version
    * @since  PR-DOM-Level-1-19980818.
    */
   public class DocumentTypeImpl 
  -    extends ChildAndParentNode
  +    extends ParentNode
       implements DocumentType {
   
       //
  
  
  
  1.30      +5 -1      xml-xerces/java/src/org/apache/xerces/dom/ElementImpl.java
  
  Index: ElementImpl.java
  ===================================================================
  RCS file: /home/cvs/xml-xerces/java/src/org/apache/xerces/dom/ElementImpl.java,v
  retrieving revision 1.29
  retrieving revision 1.30
  diff -u -r1.29 -r1.30
  --- ElementImpl.java	2000/11/22 02:22:01	1.29
  +++ ElementImpl.java	2001/01/12 00:10:24	1.30
  @@ -80,11 +80,15 @@
    * it, does.
    * @see ElementNSImpl
    *
  + * @author Arnaud  Le Hors, IBM
  + * @author Joe Kesselman, IBM
  + * @author Andy Clark, IBM
  + * @author Ralf Pfeiffer, IBM
    * @version
    * @since  PR-DOM-Level-1-19980818.
    */
   public class ElementImpl
  -    extends ChildAndParentNode
  +    extends ParentNode
       implements Element {
   
       //
  
  
  
  1.10      +5 -1      xml-xerces/java/src/org/apache/xerces/dom/EntityReferenceImpl.java
  
  Index: EntityReferenceImpl.java
  ===================================================================
  RCS file: /home/cvs/xml-xerces/java/src/org/apache/xerces/dom/EntityReferenceImpl.java,v
  retrieving revision 1.9
  retrieving revision 1.10
  diff -u -r1.9 -r1.10
  --- EntityReferenceImpl.java	2000/07/07 00:36:13	1.9
  +++ EntityReferenceImpl.java	2001/01/12 00:10:25	1.10
  @@ -107,11 +107,15 @@
    * structure-change-monitoring code I implemented to support
    * DeepNodeList.
    * 
  + * @author Arnaud  Le Hors, IBM
  + * @author Joe Kesselman, IBM
  + * @author Andy Clark, IBM
  + * @author Ralf Pfeiffer, IBM
    * @version
    * @since  PR-DOM-Level-1-19980818.
    */
   public class EntityReferenceImpl 
  -    extends ChildAndParentNode
  +    extends ParentNode
       implements EntityReference {
   
       //
  
  
  
  1.36      +11 -0     xml-xerces/java/src/org/apache/xerces/dom/NodeImpl.java
  
  Index: NodeImpl.java
  ===================================================================
  RCS file: /home/cvs/xml-xerces/java/src/org/apache/xerces/dom/NodeImpl.java,v
  retrieving revision 1.35
  retrieving revision 1.36
  diff -u -r1.35 -r1.36
  --- NodeImpl.java	2000/11/13 18:15:06	1.35
  +++ NodeImpl.java	2001/01/12 00:10:25	1.36
  @@ -100,6 +100,8 @@
    * And when a node doesn't have an owner, ownerNode refers to its
    * ownerDocument.
    *
  + * @author Arnaud  Le Hors, IBM
  + * @author Joe Kesselman, IBM
    * @version
    * @since  PR-DOM-Level-1-19980818.
    */
  @@ -138,6 +140,7 @@
       protected final static short SPECIFIED    = 0x1<<5;
       protected final static short IGNORABLEWS  = 0x1<<6;
       protected final static short SETVALUE     = 0x1<<7;
  +    protected final static short HASSTRING    = 0x1<<8;
   
       //
       // Constructors
  @@ -1365,6 +1368,14 @@
   
       final void setValueCalled(boolean value) {
           flags = (short) (value ? flags | SETVALUE : flags & ~SETVALUE);
  +    }
  +
  +    final boolean hasStringValue() {
  +        return (flags & HASSTRING) != 0;
  +    }
  +
  +    final void hasStringValue(boolean value) {
  +        flags = (short) (value ? flags | HASSTRING : flags & ~HASSTRING);
       }
   
       //
  
  
  
  1.17      +16 -4     xml-xerces/java/src/org/apache/xerces/dom/ParentNode.java
  
  Index: ParentNode.java
  ===================================================================
  RCS file: /home/cvs/xml-xerces/java/src/org/apache/xerces/dom/ParentNode.java,v
  retrieving revision 1.16
  retrieving revision 1.17
  diff -u -r1.16 -r1.17
  --- ParentNode.java	2000/11/13 18:15:06	1.16
  +++ ParentNode.java	2001/01/12 00:10:26	1.17
  @@ -1,4 +1,4 @@
  -/* $Id: ParentNode.java,v 1.16 2000/11/13 18:15:06 lehors Exp $ */
  +/* $Id: ParentNode.java,v 1.17 2001/01/12 00:10:26 lehors Exp $ */
   /*
    * The Apache Software License, Version 1.1
    *
  @@ -66,7 +66,7 @@
   import org.apache.xerces.dom.events.*;
   
   /**
  - * 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>
  @@ -79,10 +79,22 @@
    * 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.
  + *
  + * @author Arnaud  Le Hors, IBM
  + * @author Joe Kesselman, IBM
  + * @author Andy Clark, IBM
    */
   public abstract class ParentNode
  -    extends NodeImpl {
  +    extends ChildNode {
   
       /** Serialization version. */
       static final long serialVersionUID = 2815829867152120872L;