1 /*
   2  * reserved comment block
   3  * DO NOT REMOVE OR ALTER!
   4  */
   5 /*
   6  * Licensed to the Apache Software Foundation (ASF) under one or more
   7  * contributor license agreements.  See the NOTICE file distributed with
   8  * this work for additional information regarding copyright ownership.
   9  * The ASF licenses this file to You under the Apache License, Version 2.0
  10  * (the "License"); you may not use this file except in compliance with
  11  * the License.  You may obtain a copy of the License at
  12  *
  13  *      http://www.apache.org/licenses/LICENSE-2.0
  14  *
  15  * Unless required by applicable law or agreed to in writing, software
  16  * distributed under the License is distributed on an "AS IS" BASIS,
  17  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  18  * See the License for the specific language governing permissions and
  19  * limitations under the License.
  20  */
  21 
  22 package com.sun.org.apache.xerces.internal.dom;
  23 
  24 import java.io.IOException;
  25 import java.io.ObjectInputStream;
  26 import java.io.ObjectOutputStream;
  27 
  28 import org.w3c.dom.TypeInfo;
  29 import org.w3c.dom.Attr;
  30 import org.w3c.dom.DOMException;
  31 import org.w3c.dom.Element;
  32 import org.w3c.dom.Node;
  33 import org.w3c.dom.NodeList;
  34 import org.w3c.dom.Text;
  35 
  36 /**
  37  * Attribute represents an XML-style attribute of an
  38  * Element. Typically, the allowable values are controlled by its
  39  * declaration in the Document Type Definition (DTD) governing this
  40  * kind of document.
  41  * <P>
  42  * If the attribute has not been explicitly assigned a value, but has
  43  * been declared in the DTD, it will exist and have that default. Only
  44  * if neither the document nor the DTD specifies a value will the
  45  * Attribute really be considered absent and have no value; in that
  46  * case, querying the attribute will return null.
  47  * <P>
  48  * Attributes may have multiple children that contain their data. (XML
  49  * allows attributes to contain entity references, and tokenized
  50  * attribute types such as NMTOKENS may have a child for each token.)
  51  * For convenience, the Attribute object's getValue() method returns
  52  * the string version of the attribute's value.
  53  * <P>
  54  * Attributes are not children of the Elements they belong to, in the
  55  * usual sense, and have no valid Parent reference. However, the spec
  56  * says they _do_ belong to a specific Element, and an INUSE exception
  57  * is to be thrown if the user attempts to explicitly share them
  58  * between elements.
  59  * <P>
  60  * Note that Elements do not permit attributes to appear to be shared
  61  * (see the INUSE exception), so this object's mutability is
  62  * officially not an issue.
  63  * <p>
  64  * Note: The ownerNode attribute is used to store the Element the Attr
  65  * node is associated with. Attr nodes do not have parent nodes.
  66  * Besides, the getOwnerElement() method can be used to get the element node
  67  * this attribute is associated with.
  68  * <P>
  69  * AttrImpl does not support Namespaces. AttrNSImpl, which inherits from
  70  * it, does.
  71  *
  72  * <p>AttrImpl used to inherit from ParentNode. It now directly inherits from
  73  * NodeImpl and provide its own implementation of the ParentNode's behavior.
  74  * The reason is that we now try and avoid to always create a Text node to
  75  * hold the value of an attribute. The DOM spec requires it, so we still have
  76  * to do it in case getFirstChild() is called for instance. The reason
  77  * attribute values are stored as a list of nodes is so that they can carry
  78  * more than a simple string. They can also contain EntityReference nodes.
  79  * However, most of the times people only have a single string that they only
  80  * set and get through Element.set/getAttribute or Attr.set/getValue. In this
  81  * new version, the Attr node has a value pointer which can either be the
  82  * String directly or a pointer to the first ChildNode. A flag tells which one
  83  * it currently is. Note that while we try to stick with the direct String as
  84  * much as possible once we've switched to a node there is no going back. This
  85  * is because we have no way to know whether the application keeps referring to
  86  * the node we once returned.
  87  * <p> The gain in memory varies on the density of attributes in the document.
  88  * But in the tests I've run I've seen up to 12% of memory gain. And the good
  89  * thing is that it also leads to a slight gain in speed because we allocate
  90  * fewer objects! I mean, that's until we have to actually create the node...
  91  * <p>
  92  * To avoid too much duplicated code, I got rid of ParentNode and renamed
  93  * ChildAndParentNode, which I never really liked, to ParentNode for
  94  * simplicity, this doesn't make much of a difference in memory usage because
  95  * there are only very few objects that are only a Parent. This is only true
  96  * now because AttrImpl now inherits directly from NodeImpl and has its own
  97  * implementation of the ParentNode's node behavior. So there is still some
  98  * duplicated code there.
  99  * <p>
 100  * This class doesn't directly support mutation events, however, it notifies
 101  * the document when mutations are performed so that the document class do so.
 102  *
 103  * <p><b>WARNING</b>: Some of the code here is partially duplicated in
 104  * ParentNode, be careful to keep these two classes in sync!
 105  *
 106  * @xerces.internal
 107  *
 108  * @see AttrNSImpl
 109  *
 110  * @author Arnaud  Le Hors, IBM
 111  * @author Joe Kesselman, IBM
 112  * @author Andy Clark, IBM
 113  * @since PR-DOM-Level-1-19980818.
 114  *
 115  */
 116 public class AttrImpl
 117     extends NodeImpl
 118     implements Attr, TypeInfo{
 119 
 120     //
 121     // Constants
 122     //
 123 
 124     /** Serialization version. */
 125     static final long serialVersionUID = 7277707688218972102L;
 126 
 127     /** DTD namespace. **/
 128     static final String DTD_URI = "http://www.w3.org/TR/REC-xml";
 129 
 130     //
 131     // Data
 132     //
 133 
 134     /** This can either be a String or the first child node. */
 135     protected Object value = null;
 136 
 137     /** Attribute name. */
 138     protected String name;
 139 
 140     /** Type information */
 141     // REVISIT: we are losing the type information in DOM during serialization
 142     transient Object type;
 143 
 144     protected TextImpl textNode = null;
 145 
 146     //
 147     // Constructors
 148     //
 149 
 150     /**
 151      * Attribute has no public constructor. Please use the factory
 152      * method in the Document class.
 153      */
 154     protected AttrImpl(CoreDocumentImpl ownerDocument, String name) {
 155         super(ownerDocument);
 156         this.name = name;
 157         /** False for default attributes. */
 158         isSpecified(true);
 159         hasStringValue(true);
 160     }
 161 
 162     // for AttrNSImpl
 163     protected AttrImpl() {}
 164 
 165     // Support for DOM Level 3 renameNode method.
 166     // Note: This only deals with part of the pb. It is expected to be
 167     // called after the Attr has been detached for one thing.
 168     // CoreDocumentImpl does all the work.
 169     void rename(String name) {
 170         if (needsSyncData()) {
 171             synchronizeData();
 172         }
 173         this.name = name;
 174     }
 175 
 176     // create a real text node as child if we don't have one yet
 177     protected void makeChildNode() {
 178         if (hasStringValue()) {
 179             if (value != null) {
 180                 TextImpl text =
 181                     (TextImpl) ownerDocument().createTextNode((String) value);
 182                 value = text;
 183                 text.isFirstChild(true);
 184                 text.previousSibling = text;
 185                 text.ownerNode = this;
 186                 text.isOwned(true);
 187             }
 188             hasStringValue(false);
 189         }
 190     }
 191 
 192     /**
 193      * NON-DOM
 194      * set the ownerDocument of this node and its children
 195      */
 196     void setOwnerDocument(CoreDocumentImpl doc) {
 197         if (needsSyncChildren()) {
 198             synchronizeChildren();
 199         }
 200         super.setOwnerDocument(doc);
 201         if (!hasStringValue()) {
 202             for (ChildNode child = (ChildNode) value;
 203                  child != null; child = child.nextSibling) {
 204                 child.setOwnerDocument(doc);
 205             }
 206         }
 207     }
 208 
 209     /**
 210      * NON-DOM: set the type of this attribute to be ID type.
 211      *
 212      * @param id
 213      */
 214     public void setIdAttribute(boolean id){
 215         if (needsSyncData()) {
 216             synchronizeData();
 217         }
 218         isIdAttribute(id);
 219     }
 220     /** DOM Level 3: isId*/
 221     public boolean isId(){
 222         // REVISIT: should an attribute that is not in the tree return
 223         // isID true?
 224         return isIdAttribute();
 225     }
 226 
 227 
 228     //
 229     // Node methods
 230     //
 231 
 232     public Node cloneNode(boolean deep) {
 233 
 234         if (needsSyncChildren()) {
 235             synchronizeChildren();
 236         }
 237         AttrImpl clone = (AttrImpl) super.cloneNode(deep);
 238 
 239         // take care of case where there are kids
 240         if (!clone.hasStringValue()) {
 241 
 242             // Need to break the association w/ original kids
 243             clone.value = null;
 244 
 245             // Cloning an Attribute always clones its children,
 246             // since they represent its value, no matter whether this
 247             // is a deep clone or not
 248             for (Node child = (Node) value; child != null;
 249                  child = child.getNextSibling()) {
 250                  clone.appendChild(child.cloneNode(true));
 251             }
 252         }
 253         clone.isSpecified(true);
 254         return clone;
 255     }
 256 
 257     /**
 258      * A short integer indicating what type of node this is. The named
 259      * constants for this value are defined in the org.w3c.dom.Node interface.
 260      */
 261     public short getNodeType() {
 262         return Node.ATTRIBUTE_NODE;
 263     }
 264 
 265     /**
 266      * Returns the attribute name
 267      */
 268     public String getNodeName() {
 269         if (needsSyncData()) {
 270             synchronizeData();
 271         }
 272         return name;
 273     }
 274 
 275     /**
 276      * Implicit in the rerouting of getNodeValue to getValue is the
 277      * need to redefine setNodeValue, for symmetry's sake.  Note that
 278      * since we're explicitly providing a value, Specified should be set
 279      * true.... even if that value equals the default.
 280      */
 281     public void setNodeValue(String value) throws DOMException {
 282         setValue(value);
 283     }
 284 
 285     /**
 286      * @see org.w3c.dom.TypeInfo#getTypeName()
 287      */
 288     public String getTypeName() {
 289         return (String)type;
 290     }
 291 
 292     /**
 293      * @see org.w3c.dom.TypeInfo#getTypeNamespace()
 294      */
 295     public String getTypeNamespace() {
 296         if (type != null) {
 297             return DTD_URI;
 298         }
 299         return null;
 300     }
 301 
 302     /**
 303      * Method getSchemaTypeInfo.
 304      * @return TypeInfo
 305      */
 306     public TypeInfo getSchemaTypeInfo(){
 307       return this;
 308     }
 309 
 310     /**
 311      * In Attribute objects, NodeValue is considered a synonym for
 312      * Value.
 313      *
 314      * @see #getValue()
 315      */
 316     public String getNodeValue() {
 317         return getValue();
 318     }
 319 
 320     //
 321     // Attr methods
 322     //
 323 
 324     /**
 325      * In Attributes, NodeName is considered a synonym for the
 326      * attribute's Name
 327      */
 328     public String getName() {
 329 
 330         if (needsSyncData()) {
 331             synchronizeData();
 332         }
 333         return name;
 334 
 335     } // getName():String
 336 
 337     /**
 338      * The DOM doesn't clearly define what setValue(null) means. I've taken it
 339      * as "remove all children", which from outside should appear
 340      * similar to setting it to the empty string.
 341      */
 342     public void setValue(String newvalue) {
 343 
 344         CoreDocumentImpl ownerDocument = ownerDocument();
 345 
 346         if (ownerDocument.errorChecking && isReadOnly()) {
 347             String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "NO_MODIFICATION_ALLOWED_ERR", null);
 348             throw new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR, msg);
 349         }
 350 
 351         Element ownerElement = getOwnerElement();
 352         String oldvalue = "";
 353         if (needsSyncData()) {
 354             synchronizeData();
 355         }
 356         if (needsSyncChildren()) {
 357             synchronizeChildren();
 358         }
 359         if (value != null) {
 360             if (ownerDocument.getMutationEvents()) {
 361                 // Can no longer just discard the kids; they may have
 362                 // event listeners waiting for them to disconnect.
 363                 if (hasStringValue()) {
 364                     oldvalue = (String) value;
 365                     // create an actual text node as our child so
 366                     // that we can use it in the event
 367                     if (textNode == null) {
 368                         textNode = (TextImpl)
 369                             ownerDocument.createTextNode((String) value);
 370                     }
 371                     else {
 372                         textNode.data = (String) value;
 373                     }
 374                     value = textNode;
 375                     textNode.isFirstChild(true);
 376                     textNode.previousSibling = textNode;
 377                     textNode.ownerNode = this;
 378                     textNode.isOwned(true);
 379                     hasStringValue(false);
 380                     internalRemoveChild(textNode, true);
 381                 }
 382                 else {
 383                     oldvalue = getValue();
 384                     while (value != null) {
 385                         internalRemoveChild((Node) value, true);
 386                     }
 387                 }
 388             }
 389             else {
 390                 if (hasStringValue()) {
 391                     oldvalue = (String) value;
 392                 }
 393                 else {
 394                     // simply discard children if any
 395                     oldvalue = getValue();
 396                     // remove ref from first child to last child
 397                     ChildNode firstChild = (ChildNode) value;
 398                     firstChild.previousSibling = null;
 399                     firstChild.isFirstChild(false);
 400                     firstChild.ownerNode = ownerDocument;
 401                 }
 402                 // then remove ref to current value
 403                 value = null;
 404                 needsSyncChildren(false);
 405             }
 406             if (isIdAttribute() && ownerElement != null) {
 407                 ownerDocument.removeIdentifier(oldvalue);
 408             }
 409         }
 410 
 411         // Create and add the new one, generating only non-aggregate events
 412         // (There are no listeners on the new Text, but there may be
 413         // capture/bubble listeners on the Attr.
 414         // Note that aggregate events are NOT dispatched here,
 415         // since we need to combine the remove and insert.
 416         isSpecified(true);
 417         if (ownerDocument.getMutationEvents()) {
 418             // if there are any event handlers create a real node
 419             internalInsertBefore(ownerDocument.createTextNode(newvalue),
 420                                  null, true);
 421             hasStringValue(false);
 422             // notify document
 423             ownerDocument.modifiedAttrValue(this, oldvalue);
 424         } else {
 425             // directly store the string
 426             value = newvalue;
 427             hasStringValue(true);
 428             changed();
 429         }
 430         if (isIdAttribute() && ownerElement != null) {
 431             ownerDocument.putIdentifier(newvalue, ownerElement);
 432         }
 433 
 434     } // setValue(String)
 435 
 436     /**
 437      * The "string value" of an Attribute is its text representation,
 438      * which in turn is a concatenation of the string values of its children.
 439      */
 440     public String getValue() {
 441 
 442         if (needsSyncData()) {
 443             synchronizeData();
 444         }
 445         if (needsSyncChildren()) {
 446             synchronizeChildren();
 447         }
 448         if (value == null) {
 449             return "";
 450         }
 451         if (hasStringValue()) {
 452             return (String) value;
 453         }
 454 
 455         ChildNode firstChild = ((ChildNode) value);
 456 
 457         String data = null;
 458         if (firstChild.getNodeType() == Node.ENTITY_REFERENCE_NODE){
 459                 data = ((EntityReferenceImpl)firstChild).getEntityRefValue();
 460         }
 461         else {
 462                 data =  firstChild.getNodeValue();
 463         }
 464 
 465         ChildNode node = firstChild.nextSibling;
 466 
 467         if (node == null || data == null)  return (data == null)?"":data;
 468 
 469         StringBuffer value = new StringBuffer(data);
 470         while (node != null) {
 471             if (node.getNodeType()  == Node.ENTITY_REFERENCE_NODE){
 472                 data = ((EntityReferenceImpl)node).getEntityRefValue();
 473                 if (data == null) return "";
 474                 value.append(data);
 475             }
 476             else {
 477                 value.append(node.getNodeValue());
 478             }
 479             node = node.nextSibling;
 480         }
 481         return value.toString();
 482 
 483     } // getValue():String
 484 
 485 
 486     /**
 487      * The "specified" flag is true if and only if this attribute's
 488      * value was explicitly specified in the original document. Note that
 489      * the implementation, not the user, is in charge of this
 490      * property. If the user asserts an Attribute value (even if it ends
 491      * up having the same value as the default), it is considered a
 492      * specified attribute. If you really want to revert to the default,
 493      * delete the attribute from the Element, and the Implementation will
 494      * re-assert the default (if any) in its place, with the appropriate
 495      * specified=false setting.
 496      */
 497     public boolean getSpecified() {
 498 
 499         if (needsSyncData()) {
 500             synchronizeData();
 501         }
 502         return isSpecified();
 503 
 504     } // getSpecified():boolean
 505 
 506     //
 507     // Attr2 methods
 508     //
 509 
 510     /**
 511      * Returns the element node that this attribute is associated with,
 512      * or null if the attribute has not been added to an element.
 513      *
 514      * @see #getOwnerElement
 515      *
 516      * @deprecated Previous working draft of DOM Level 2. New method
 517      *             is <tt>getOwnerElement()</tt>.
 518      */
 519     public Element getElement() {
 520         // if we have an owner, ownerNode is our ownerElement, otherwise it's
 521         // our ownerDocument and we don't have an ownerElement
 522         return (Element) (isOwned() ? ownerNode : null);
 523     }
 524 
 525     /**
 526      * Returns the element node that this attribute is associated with,
 527      * or null if the attribute has not been added to an element.
 528      *
 529      * @since WD-DOM-Level-2-19990719
 530      */
 531     public Element getOwnerElement() {
 532         // if we have an owner, ownerNode is our ownerElement, otherwise it's
 533         // our ownerDocument and we don't have an ownerElement
 534         return (Element) (isOwned() ? ownerNode : null);
 535     }
 536 
 537     public void normalize() {
 538 
 539         // No need to normalize if already normalized or
 540         // if value is kept as a String.
 541         if (isNormalized() || hasStringValue())
 542             return;
 543 
 544         Node kid, next;
 545         ChildNode firstChild = (ChildNode)value;
 546         for (kid = firstChild; kid != null; kid = next) {
 547             next = kid.getNextSibling();
 548 
 549             // If kid is a text node, we need to check for one of two
 550             // conditions:
 551             //   1) There is an adjacent text node
 552             //   2) There is no adjacent text node, but kid is
 553             //      an empty text node.
 554             if ( kid.getNodeType() == Node.TEXT_NODE )
 555             {
 556                 // If an adjacent text node, merge it with kid
 557                 if ( next!=null && next.getNodeType() == Node.TEXT_NODE )
 558                 {
 559                     ((Text)kid).appendData(next.getNodeValue());
 560                     removeChild( next );
 561                     next = kid; // Don't advance; there might be another.
 562                 }
 563                 else
 564                 {
 565                     // If kid is empty, remove it
 566                     if ( kid.getNodeValue() == null || kid.getNodeValue().length() == 0 ) {
 567                         removeChild( kid );
 568                     }
 569                 }
 570             }
 571         }
 572 
 573         isNormalized(true);
 574     } // normalize()
 575 
 576     //
 577     // Public methods
 578     //
 579 
 580     /** NON-DOM, for use by parser */
 581     public void setSpecified(boolean arg) {
 582 
 583         if (needsSyncData()) {
 584             synchronizeData();
 585         }
 586         isSpecified(arg);
 587 
 588     } // setSpecified(boolean)
 589 
 590         /**
 591          * NON-DOM: used by the parser
 592          * @param type
 593          */
 594     public void setType (Object type){
 595         this.type = type;
 596     }
 597 
 598     //
 599     // Object methods
 600     //
 601 
 602     /** NON-DOM method for debugging convenience */
 603     public String toString() {
 604         return getName() + "=" + "\"" + getValue() + "\"";
 605     }
 606 
 607     /**
 608      * Test whether this node has any children. Convenience shorthand
 609      * for (Node.getFirstChild()!=null)
 610      */
 611     public boolean hasChildNodes() {
 612         if (needsSyncChildren()) {
 613             synchronizeChildren();
 614         }
 615         return value != null;
 616     }
 617 
 618     /**
 619      * Obtain a NodeList enumerating all children of this node. If there
 620      * are none, an (initially) empty NodeList is returned.
 621      * <p>
 622      * NodeLists are "live"; as children are added/removed the NodeList
 623      * will immediately reflect those changes. Also, the NodeList refers
 624      * to the actual nodes, so changes to those nodes made via the DOM tree
 625      * will be reflected in the NodeList and vice versa.
 626      * <p>
 627      * In this implementation, Nodes implement the NodeList interface and
 628      * provide their own getChildNodes() support. Other DOMs may solve this
 629      * differently.
 630      */
 631     public NodeList getChildNodes() {
 632         // JKESS: KNOWN ISSUE HERE
 633 
 634         if (needsSyncChildren()) {
 635             synchronizeChildren();
 636         }
 637         return this;
 638 
 639     } // getChildNodes():NodeList
 640 
 641     /** The first child of this Node, or null if none. */
 642     public Node getFirstChild() {
 643 
 644         if (needsSyncChildren()) {
 645             synchronizeChildren();
 646         }
 647         makeChildNode();
 648         return (Node) value;
 649 
 650     }   // getFirstChild():Node
 651 
 652     /** The last child of this Node, or null if none. */
 653     public Node getLastChild() {
 654 
 655         if (needsSyncChildren()) {
 656             synchronizeChildren();
 657         }
 658         return lastChild();
 659 
 660     } // getLastChild():Node
 661 
 662     final ChildNode lastChild() {
 663         // last child is stored as the previous sibling of first child
 664         makeChildNode();
 665         return value != null ? ((ChildNode) value).previousSibling : null;
 666     }
 667 
 668     final void lastChild(ChildNode node) {
 669         // store lastChild as previous sibling of first child
 670         if (value != null) {
 671             ((ChildNode) value).previousSibling = node;
 672         }
 673     }
 674 
 675     /**
 676      * Move one or more node(s) to our list of children. Note that this
 677      * implicitly removes them from their previous parent.
 678      *
 679      * @param newChild The Node to be moved to our subtree. As a
 680      * convenience feature, inserting a DocumentNode will instead insert
 681      * all its children.
 682      *
 683      * @param refChild Current child which newChild should be placed
 684      * immediately before. If refChild is null, the insertion occurs
 685      * after all existing Nodes, like appendChild().
 686      *
 687      * @return newChild, in its new state (relocated, or emptied in the case of
 688      * DocumentNode.)
 689      *
 690      * @throws DOMException(HIERARCHY_REQUEST_ERR) if newChild is of a
 691      * type that shouldn't be a child of this node, or if newChild is an
 692      * ancestor of this node.
 693      *
 694      * @throws DOMException(WRONG_DOCUMENT_ERR) if newChild has a
 695      * different owner document than we do.
 696      *
 697      * @throws DOMException(NOT_FOUND_ERR) if refChild is not a child of
 698      * this node.
 699      *
 700      * @throws DOMException(NO_MODIFICATION_ALLOWED_ERR) if this node is
 701      * read-only.
 702      */
 703     public Node insertBefore(Node newChild, Node refChild)
 704         throws DOMException {
 705         // Tail-call; optimizer should be able to do good things with.
 706         return internalInsertBefore(newChild, refChild, false);
 707     } // insertBefore(Node,Node):Node
 708 
 709     /** NON-DOM INTERNAL: Within DOM actions,we sometimes need to be able
 710      * to control which mutation events are spawned. This version of the
 711      * insertBefore operation allows us to do so. It is not intended
 712      * for use by application programs.
 713      */
 714     Node internalInsertBefore(Node newChild, Node refChild, boolean replace)
 715         throws DOMException {
 716 
 717         CoreDocumentImpl ownerDocument = ownerDocument();
 718         boolean errorChecking = ownerDocument.errorChecking;
 719 
 720         if (newChild.getNodeType() == Node.DOCUMENT_FRAGMENT_NODE) {
 721             // SLOW BUT SAFE: We could insert the whole subtree without
 722             // juggling so many next/previous pointers. (Wipe out the
 723             // parent's child-list, patch the parent pointers, set the
 724             // ends of the list.) But we know some subclasses have special-
 725             // case behavior they add to insertBefore(), so we don't risk it.
 726             // This approch also takes fewer bytecodes.
 727 
 728             // NOTE: If one of the children is not a legal child of this
 729             // node, throw HIERARCHY_REQUEST_ERR before _any_ of the children
 730             // have been transferred. (Alternative behaviors would be to
 731             // reparent up to the first failure point or reparent all those
 732             // which are acceptable to the target node, neither of which is
 733             // as robust. PR-DOM-0818 isn't entirely clear on which it
 734             // recommends?????
 735 
 736             // No need to check kids for right-document; if they weren't,
 737             // they wouldn't be kids of that DocFrag.
 738             if (errorChecking) {
 739                 for (Node kid = newChild.getFirstChild(); // Prescan
 740                      kid != null; kid = kid.getNextSibling()) {
 741 
 742                     if (!ownerDocument.isKidOK(this, kid)) {
 743                         String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "HIERARCHY_REQUEST_ERR", null);
 744                         throw new DOMException(DOMException.HIERARCHY_REQUEST_ERR, msg);
 745                     }
 746                 }
 747             }
 748 
 749             while (newChild.hasChildNodes()) {
 750                 insertBefore(newChild.getFirstChild(), refChild);
 751             }
 752             return newChild;
 753         }
 754 
 755         if (newChild == refChild) {
 756             // stupid case that must be handled as a no-op triggering events...
 757             refChild = refChild.getNextSibling();
 758             removeChild(newChild);
 759             insertBefore(newChild, refChild);
 760             return newChild;
 761         }
 762 
 763         if (needsSyncChildren()) {
 764             synchronizeChildren();
 765         }
 766 
 767         if (errorChecking) {
 768             if (isReadOnly()) {
 769                 String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "NO_MODIFICATION_ALLOWED_ERR", null);
 770                 throw new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR, msg);
 771             }
 772             if (newChild.getOwnerDocument() != ownerDocument) {
 773                 String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "WRONG_DOCUMENT_ERR", null);
 774                 throw new DOMException(DOMException.WRONG_DOCUMENT_ERR, msg);
 775             }
 776             if (!ownerDocument.isKidOK(this, newChild)) {
 777                 String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "HIERARCHY_REQUEST_ERR", null);
 778                 throw new DOMException(DOMException.HIERARCHY_REQUEST_ERR, msg);
 779             }
 780             // refChild must be a child of this node (or null)
 781             if (refChild != null && refChild.getParentNode() != this) {
 782                 String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "NOT_FOUND_ERR", null);
 783                 throw new DOMException(DOMException.NOT_FOUND_ERR, msg);
 784             }
 785 
 786             // Prevent cycles in the tree
 787             // newChild cannot be ancestor of this Node,
 788             // and actually cannot be this
 789             boolean treeSafe = true;
 790             for (NodeImpl a = this; treeSafe && a != null; a = a.parentNode())
 791             {
 792                 treeSafe = newChild != a;
 793             }
 794             if (!treeSafe) {
 795                 String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "HIERARCHY_REQUEST_ERR", null);
 796                 throw new DOMException(DOMException.HIERARCHY_REQUEST_ERR, msg);
 797             }
 798         }
 799 
 800         makeChildNode(); // make sure we have a node and not a string
 801 
 802         // notify document
 803         ownerDocument.insertingNode(this, replace);
 804 
 805         // Convert to internal type, to avoid repeated casting
 806         ChildNode newInternal = (ChildNode)newChild;
 807 
 808         Node oldparent = newInternal.parentNode();
 809         if (oldparent != null) {
 810             oldparent.removeChild(newInternal);
 811         }
 812 
 813         // Convert to internal type, to avoid repeated casting
 814         ChildNode refInternal = (ChildNode) refChild;
 815 
 816         // Attach up
 817         newInternal.ownerNode = this;
 818         newInternal.isOwned(true);
 819 
 820         // Attach before and after
 821         // Note: firstChild.previousSibling == lastChild!!
 822         ChildNode firstChild = (ChildNode) value;
 823         if (firstChild == null) {
 824             // this our first and only child
 825             value = newInternal; // firstchild = newInternal;
 826             newInternal.isFirstChild(true);
 827             newInternal.previousSibling = newInternal;
 828         }
 829         else {
 830             if (refInternal == null) {
 831                 // this is an append
 832                 ChildNode lastChild = firstChild.previousSibling;
 833                 lastChild.nextSibling = newInternal;
 834                 newInternal.previousSibling = lastChild;
 835                 firstChild.previousSibling = newInternal;
 836             }
 837             else {
 838                 // this is an insert
 839                 if (refChild == firstChild) {
 840                     // at the head of the list
 841                     firstChild.isFirstChild(false);
 842                     newInternal.nextSibling = firstChild;
 843                     newInternal.previousSibling = firstChild.previousSibling;
 844                     firstChild.previousSibling = newInternal;
 845                     value = newInternal; // firstChild = newInternal;
 846                     newInternal.isFirstChild(true);
 847                 }
 848                 else {
 849                     // somewhere in the middle
 850                     ChildNode prev = refInternal.previousSibling;
 851                     newInternal.nextSibling = refInternal;
 852                     prev.nextSibling = newInternal;
 853                     refInternal.previousSibling = newInternal;
 854                     newInternal.previousSibling = prev;
 855                 }
 856             }
 857         }
 858 
 859         changed();
 860 
 861         // notify document
 862         ownerDocument.insertedNode(this, newInternal, replace);
 863 
 864         checkNormalizationAfterInsert(newInternal);
 865 
 866         return newChild;
 867 
 868     } // internalInsertBefore(Node,Node,int):Node
 869 
 870     /**
 871      * Remove a child from this Node. The removed child's subtree
 872      * remains intact so it may be re-inserted elsewhere.
 873      *
 874      * @return oldChild, in its new state (removed).
 875      *
 876      * @throws DOMException(NOT_FOUND_ERR) if oldChild is not a child of
 877      * this node.
 878      *
 879      * @throws DOMException(NO_MODIFICATION_ALLOWED_ERR) if this node is
 880      * read-only.
 881      */
 882     public Node removeChild(Node oldChild)
 883         throws DOMException {
 884         // Tail-call, should be optimizable
 885         if (hasStringValue()) {
 886             // we don't have any child per say so it can't be one of them!
 887             String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "NOT_FOUND_ERR", null);
 888             throw new DOMException(DOMException.NOT_FOUND_ERR, msg);
 889         }
 890         return internalRemoveChild(oldChild, false);
 891     } // removeChild(Node) :Node
 892 
 893     /** NON-DOM INTERNAL: Within DOM actions,we sometimes need to be able
 894      * to control which mutation events are spawned. This version of the
 895      * removeChild operation allows us to do so. It is not intended
 896      * for use by application programs.
 897      */
 898     Node internalRemoveChild(Node oldChild, boolean replace)
 899         throws DOMException {
 900 
 901         CoreDocumentImpl ownerDocument = ownerDocument();
 902         if (ownerDocument.errorChecking) {
 903             if (isReadOnly()) {
 904                 String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "NO_MODIFICATION_ALLOWED_ERR", null);
 905                 throw new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR, msg);
 906             }
 907             if (oldChild != null && oldChild.getParentNode() != this) {
 908                 String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "NOT_FOUND_ERR", null);
 909                 throw new DOMException(DOMException.NOT_FOUND_ERR, msg);
 910             }
 911         }
 912 
 913         ChildNode oldInternal = (ChildNode) oldChild;
 914 
 915         // notify document
 916         ownerDocument.removingNode(this, oldInternal, replace);
 917 
 918         // Patch linked list around oldChild
 919         // Note: lastChild == firstChild.previousSibling
 920         if (oldInternal == value) { // oldInternal == firstChild
 921             // removing first child
 922             oldInternal.isFirstChild(false);
 923             // next line is: firstChild = oldInternal.nextSibling
 924             value = oldInternal.nextSibling;
 925             ChildNode firstChild = (ChildNode) value;
 926             if (firstChild != null) {
 927                 firstChild.isFirstChild(true);
 928                 firstChild.previousSibling = oldInternal.previousSibling;
 929             }
 930         } else {
 931             ChildNode prev = oldInternal.previousSibling;
 932             ChildNode next = oldInternal.nextSibling;
 933             prev.nextSibling = next;
 934             if (next == null) {
 935                 // removing last child
 936                 ChildNode firstChild = (ChildNode) value;
 937                 firstChild.previousSibling = prev;
 938             } else {
 939                 // removing some other child in the middle
 940                 next.previousSibling = prev;
 941             }
 942         }
 943 
 944         // Save previous sibling for normalization checking.
 945         ChildNode oldPreviousSibling = oldInternal.previousSibling();
 946 
 947         // Remove oldInternal's references to tree
 948         oldInternal.ownerNode       = ownerDocument;
 949         oldInternal.isOwned(false);
 950         oldInternal.nextSibling     = null;
 951         oldInternal.previousSibling = null;
 952 
 953         changed();
 954 
 955         // notify document
 956         ownerDocument.removedNode(this, replace);
 957 
 958         checkNormalizationAfterRemove(oldPreviousSibling);
 959 
 960         return oldInternal;
 961 
 962     } // internalRemoveChild(Node,int):Node
 963 
 964     /**
 965      * Make newChild occupy the location that oldChild used to
 966      * have. Note that newChild will first be removed from its previous
 967      * parent, if any. Equivalent to inserting newChild before oldChild,
 968      * then removing oldChild.
 969      *
 970      * @return oldChild, in its new state (removed).
 971      *
 972      * @throws DOMException(HIERARCHY_REQUEST_ERR) if newChild is of a
 973      * type that shouldn't be a child of this node, or if newChild is
 974      * one of our ancestors.
 975      *
 976      * @throws DOMException(WRONG_DOCUMENT_ERR) if newChild has a
 977      * different owner document than we do.
 978      *
 979      * @throws DOMException(NOT_FOUND_ERR) if oldChild is not a child of
 980      * this node.
 981      *
 982      * @throws DOMException(NO_MODIFICATION_ALLOWED_ERR) if this node is
 983      * read-only.
 984      */
 985     public Node replaceChild(Node newChild, Node oldChild)
 986         throws DOMException {
 987 
 988         makeChildNode();
 989 
 990         // If Mutation Events are being generated, this operation might
 991         // throw aggregate events twice when modifying an Attr -- once
 992         // on insertion and once on removal. DOM Level 2 does not specify
 993         // this as either desirable or undesirable, but hints that
 994         // aggregations should be issued only once per user request.
 995 
 996         // notify document
 997         CoreDocumentImpl ownerDocument = ownerDocument();
 998         ownerDocument.replacingNode(this);
 999 
1000         internalInsertBefore(newChild, oldChild, true);
1001         if (newChild != oldChild) {
1002             internalRemoveChild(oldChild, true);
1003         }
1004 
1005         // notify document
1006         ownerDocument.replacedNode(this);
1007 
1008         return oldChild;
1009     }
1010 
1011     //
1012     // NodeList methods
1013     //
1014 
1015     /**
1016      * NodeList method: Count the immediate children of this node
1017      * @return int
1018      */
1019     public int getLength() {
1020 
1021         if (hasStringValue()) {
1022             return 1;
1023         }
1024         ChildNode node = (ChildNode) value;
1025         int length = 0;
1026         for (; node != null; node = node.nextSibling) {
1027             length++;
1028         }
1029         return length;
1030 
1031     } // getLength():int
1032 
1033     /**
1034      * NodeList method: Return the Nth immediate child of this node, or
1035      * null if the index is out of bounds.
1036      * @return org.w3c.dom.Node
1037      * @param Index int
1038      */
1039     public Node item(int index) {
1040 
1041         if (hasStringValue()) {
1042             if (index != 0 || value == null) {
1043                 return null;
1044             }
1045             else {
1046                 makeChildNode();
1047                 return (Node) value;
1048             }
1049         }
1050         if (index < 0) {
1051             return null;
1052         }
1053         ChildNode node = (ChildNode) value;
1054         for (int i = 0; i < index && node != null; i++) {
1055             node = node.nextSibling;
1056         }
1057         return node;
1058 
1059     } // item(int):Node
1060 
1061     //
1062     // DOM3
1063     //
1064 
1065     /**
1066      * DOM Level 3 WD- Experimental.
1067      * Override inherited behavior from ParentNode to support deep equal.
1068      * isEqualNode is always deep on Attr nodes.
1069      */
1070     public boolean isEqualNode(Node arg) {
1071         return super.isEqualNode(arg);
1072     }
1073 
1074     /**
1075      * Introduced in DOM Level 3. <p>
1076      * Checks if a type is derived from another by restriction. See:
1077      * http://www.w3.org/TR/DOM-Level-3-Core/core.html#TypeInfo-isDerivedFrom
1078      *
1079      * @param ancestorNS
1080      *        The namspace of the ancestor type declaration
1081      * @param ancestorName
1082      *        The name of the ancestor type declaration
1083      * @param type
1084      *        The reference type definition
1085      *
1086      * @return boolean True if the type is derived by restriciton for the
1087      *         reference type
1088      */
1089     public boolean isDerivedFrom(String typeNamespaceArg,
1090                                  String typeNameArg,
1091                                  int derivationMethod) {
1092 
1093         return false;
1094     }
1095 
1096 
1097     //
1098     // Public methods
1099     //
1100 
1101     /**
1102      * Override default behavior so that if deep is true, children are also
1103      * toggled.
1104      * @see Node
1105      * <P>
1106      * Note: this will not change the state of an EntityReference or its
1107      * children, which are always read-only.
1108      */
1109     public void setReadOnly(boolean readOnly, boolean deep) {
1110 
1111         super.setReadOnly(readOnly, deep);
1112 
1113         if (deep) {
1114 
1115             if (needsSyncChildren()) {
1116                 synchronizeChildren();
1117             }
1118 
1119             if (hasStringValue()) {
1120                 return;
1121             }
1122             // Recursively set kids
1123             for (ChildNode mykid = (ChildNode) value;
1124                  mykid != null;
1125                  mykid = mykid.nextSibling) {
1126                 if (mykid.getNodeType() != Node.ENTITY_REFERENCE_NODE) {
1127                     mykid.setReadOnly(readOnly,true);
1128                 }
1129             }
1130         }
1131     } // setReadOnly(boolean,boolean)
1132 
1133     //
1134     // Protected methods
1135     //
1136 
1137     /**
1138      * Override this method in subclass to hook in efficient
1139      * internal data structure.
1140      */
1141     protected void synchronizeChildren() {
1142         // By default just change the flag to avoid calling this method again
1143         needsSyncChildren(false);
1144     }
1145 
1146     /**
1147      * Checks the normalized state of this node after inserting a child.
1148      * If the inserted child causes this node to be unnormalized, then this
1149      * node is flagged accordingly.
1150      * The conditions for changing the normalized state are:
1151      * <ul>
1152      * <li>The inserted child is a text node and one of its adjacent siblings
1153      * is also a text node.
1154      * <li>The inserted child is is itself unnormalized.
1155      * </ul>
1156      *
1157      * @param insertedChild the child node that was inserted into this node
1158      *
1159      * @throws NullPointerException if the inserted child is <code>null</code>
1160      */
1161     void checkNormalizationAfterInsert(ChildNode insertedChild) {
1162         // See if insertion caused this node to be unnormalized.
1163         if (insertedChild.getNodeType() == Node.TEXT_NODE) {
1164             ChildNode prev = insertedChild.previousSibling();
1165             ChildNode next = insertedChild.nextSibling;
1166             // If an adjacent sibling of the new child is a text node,
1167             // flag this node as unnormalized.
1168             if ((prev != null && prev.getNodeType() == Node.TEXT_NODE) ||
1169                 (next != null && next.getNodeType() == Node.TEXT_NODE)) {
1170                 isNormalized(false);
1171             }
1172         }
1173         else {
1174             // If the new child is not normalized,
1175             // then this node is inherently not normalized.
1176             if (!insertedChild.isNormalized()) {
1177                 isNormalized(false);
1178             }
1179         }
1180     } // checkNormalizationAfterInsert(ChildNode)
1181 
1182     /**
1183      * Checks the normalized of this node after removing a child.
1184      * If the removed child causes this node to be unnormalized, then this
1185      * node is flagged accordingly.
1186      * The conditions for changing the normalized state are:
1187      * <ul>
1188      * <li>The removed child had two adjacent siblings that were text nodes.
1189      * </ul>
1190      *
1191      * @param previousSibling the previous sibling of the removed child, or
1192      * <code>null</code>
1193      */
1194     void checkNormalizationAfterRemove(ChildNode previousSibling) {
1195         // See if removal caused this node to be unnormalized.
1196         // If the adjacent siblings of the removed child were both text nodes,
1197         // flag this node as unnormalized.
1198         if (previousSibling != null &&
1199             previousSibling.getNodeType() == Node.TEXT_NODE) {
1200 
1201             ChildNode next = previousSibling.nextSibling;
1202             if (next != null && next.getNodeType() == Node.TEXT_NODE) {
1203                 isNormalized(false);
1204             }
1205         }
1206     } // checkNormalizationAfterRemove(ChildNode)
1207 
1208     //
1209     // Serialization methods
1210     //
1211 
1212     /** Serialize object. */
1213     private void writeObject(ObjectOutputStream out) throws IOException {
1214 
1215         // synchronize chilren
1216         if (needsSyncChildren()) {
1217             synchronizeChildren();
1218         }
1219         // write object
1220         out.defaultWriteObject();
1221 
1222     } // writeObject(ObjectOutputStream)
1223 
1224     /** Deserialize object. */
1225     private void readObject(ObjectInputStream ois)
1226         throws ClassNotFoundException, IOException {
1227 
1228         // perform default deseralization
1229         ois.defaultReadObject();
1230 
1231         // hardset synchildren - so we don't try to sync -
1232         // it does not make any sense to try to synchildren when we just
1233         // deserialize object.
1234         needsSyncChildren(false);
1235 
1236     } // readObject(ObjectInputStream)
1237 
1238 
1239 } // class AttrImpl