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