1/* 2 * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved. 3 */ 4/* 5 * Licensed to the Apache Software Foundation (ASF) under one or more 6 * contributor license agreements. See the NOTICE file distributed with 7 * this work for additional information regarding copyright ownership. 8 * The ASF licenses this file to You under the Apache License, Version 2.0 9 * (the "License"); you may not use this file except in compliance with 10 * the License. You may obtain a copy of the License at 11 * 12 * http://www.apache.org/licenses/LICENSE-2.0 13 * 14 * Unless required by applicable law or agreed to in writing, software 15 * distributed under the License is distributed on an "AS IS" BASIS, 16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 * See the License for the specific language governing permissions and 18 * limitations under the License. 19 */ 20 21package com.sun.org.apache.xerces.internal.dom; 22 23import java.io.IOException; 24import java.io.ObjectInputStream; 25import java.io.ObjectOutputStream; 26 27import org.w3c.dom.TypeInfo; 28import org.w3c.dom.Attr; 29import org.w3c.dom.DOMException; 30import org.w3c.dom.Element; 31import org.w3c.dom.Node; 32import org.w3c.dom.NodeList; 33import org.w3c.dom.Text; 34 35/** 36 * Attribute represents an XML-style attribute of an 37 * Element. Typically, the allowable values are controlled by its 38 * declaration in the Document Type Definition (DTD) governing this 39 * kind of document. 40 * <P> 41 * If the attribute has not been explicitly assigned a value, but has 42 * been declared in the DTD, it will exist and have that default. Only 43 * if neither the document nor the DTD specifies a value will the 44 * Attribute really be considered absent and have no value; in that 45 * case, querying the attribute will return null. 46 * <P> 47 * Attributes may have multiple children that contain their data. (XML 48 * allows attributes to contain entity references, and tokenized 49 * attribute types such as NMTOKENS may have a child for each token.) 50 * For convenience, the Attribute object's getValue() method returns 51 * the string version of the attribute's value. 52 * <P> 53 * Attributes are not children of the Elements they belong to, in the 54 * usual sense, and have no valid Parent reference. However, the spec 55 * says they _do_ belong to a specific Element, and an INUSE exception 56 * is to be thrown if the user attempts to explicitly share them 57 * between elements. 58 * <P> 59 * Note that Elements do not permit attributes to appear to be shared 60 * (see the INUSE exception), so this object's mutability is 61 * officially not an issue. 62 * <p> 63 * Note: The ownerNode attribute is used to store the Element the Attr 64 * node is associated with. Attr nodes do not have parent nodes. 65 * Besides, the getOwnerElement() method can be used to get the element node 66 * this attribute is associated with. 67 * <P> 68 * AttrImpl does not support Namespaces. AttrNSImpl, which inherits from 69 * it, does. 70 * 71 * <p>AttrImpl used to inherit from ParentNode. It now directly inherits from 72 * NodeImpl and provide its own implementation of the ParentNode's behavior. 73 * The reason is that we now try and avoid to always create a Text node to 74 * hold the value of an attribute. The DOM spec requires it, so we still have 75 * to do it in case getFirstChild() is called for instance. The reason 76 * attribute values are stored as a list of nodes is so that they can carry 77 * more than a simple string. They can also contain EntityReference nodes. 78 * However, most of the times people only have a single string that they only 79 * set and get through Element.set/getAttribute or Attr.set/getValue. In this 80 * new version, the Attr node has a value pointer which can either be the 81 * String directly or a pointer to the first ChildNode. A flag tells which one 82 * it currently is. Note that while we try to stick with the direct String as 83 * much as possible once we've switched to a node there is no going back. This 84 * is because we have no way to know whether the application keeps referring to 85 * the node we once returned. 86 * <p> The gain in memory varies on the density of attributes in the document. 87 * But in the tests I've run I've seen up to 12% of memory gain. And the good 88 * thing is that it also leads to a slight gain in speed because we allocate 89 * fewer objects! I mean, that's until we have to actually create the node... 90 * <p> 91 * To avoid too much duplicated code, I got rid of ParentNode and renamed 92 * ChildAndParentNode, which I never really liked, to ParentNode for 93 * simplicity, this doesn't make much of a difference in memory usage because 94 * there are only very few objects that are only a Parent. This is only true 95 * now because AttrImpl now inherits directly from NodeImpl and has its own 96 * implementation of the ParentNode's node behavior. So there is still some 97 * duplicated code there. 98 * <p> 99 * This class doesn't directly support mutation events, however, it notifies 100 * the document when mutations are performed so that the document class do so. 101 * 102 * <p><b>WARNING</b>: Some of the code here is partially duplicated in 103 * ParentNode, be careful to keep these two classes in sync! 104 * 105 * @xerces.internal 106 * 107 * @see AttrNSImpl 108 * 109 * @author Arnaud Le Hors, IBM 110 * @author Joe Kesselman, IBM 111 * @author Andy Clark, IBM 112 * @since PR-DOM-Level-1-19980818. 113 * 114 */ 115public class AttrImpl 116 extends NodeImpl 117 implements Attr, TypeInfo{ 118 119 // 120 // Constants 121 // 122 123 /** Serialization version. */ 124 static final long serialVersionUID = 7277707688218972102L; 125 126 /** DTD namespace. **/ 127 static final String DTD_URI = "http://www.w3.org/TR/REC-xml"; 128 129 // 130 // Data 131 // 132 133 /** This can either be a String or the first child node. */ 134 protected Object value = null; 135 136 /** Attribute name. */ 137 protected String name; 138 139 /** Type information */ 140 // REVISIT: we are losing the type information in DOM during serialization 141 transient Object type; 142 143 protected TextImpl textNode = null; 144 145 // 146 // Constructors 147 // 148 149 /** 150 * Attribute has no public constructor. Please use the factory 151 * method in the Document class. 152 */ 153 protected AttrImpl(CoreDocumentImpl ownerDocument, String name) { 154 super(ownerDocument); 155 this.name = name; 156 /** False for default attributes. */ 157 isSpecified(true); 158 hasStringValue(true); 159 } 160 161 // for AttrNSImpl 162 protected AttrImpl() {} 163 164 // Support for DOM Level 3 renameNode method. 165 // Note: This only deals with part of the pb. It is expected to be 166 // called after the Attr has been detached for one thing. 167 // CoreDocumentImpl does all the work. 168 void rename(String name) { 169 if (needsSyncData()) { 170 synchronizeData(); 171 } 172 this.name = name; 173 } 174 175 // create a real text node as child if we don't have one yet 176 protected void makeChildNode() { 177 if (hasStringValue()) { 178 if (value != null) { 179 TextImpl text = 180 (TextImpl) ownerDocument().createTextNode((String) value); 181 value = text; 182 text.isFirstChild(true); 183 text.previousSibling = text; 184 text.ownerNode = this; 185 text.isOwned(true); 186 } 187 hasStringValue(false); 188 } 189 } 190 191 /** 192 * NON-DOM 193 * set the ownerDocument of this node and its children 194 */ 195 void setOwnerDocument(CoreDocumentImpl doc) { 196 if (needsSyncChildren()) { 197 synchronizeChildren(); 198 } 199 super.setOwnerDocument(doc); 200 if (!hasStringValue()) { 201 for (ChildNode child = (ChildNode) value; 202 child != null; child = child.nextSibling) { 203 child.setOwnerDocument(doc); 204 } 205 } 206 } 207 208 /** 209 * NON-DOM: set the type of this attribute to be ID type. 210 * 211 * @param id 212 */ 213 public void setIdAttribute(boolean id){ 214 if (needsSyncData()) { 215 synchronizeData(); 216 } 217 isIdAttribute(id); 218 } 219 /** DOM Level 3: isId*/ 220 public boolean isId(){ 221 // REVISIT: should an attribute that is not in the tree return 222 // isID true? 223 return isIdAttribute(); 224 } 225 226 227 // 228 // Node methods 229 // 230 231 public Node cloneNode(boolean deep) { 232 233 if (needsSyncChildren()) { 234 synchronizeChildren(); 235 } 236 AttrImpl clone = (AttrImpl) super.cloneNode(deep); 237 238 // take care of case where there are kids 239 if (!clone.hasStringValue()) { 240 241 // Need to break the association w/ original kids 242 clone.value = null; 243 244 // Cloning an Attribute always clones its children, 245 // since they represent its value, no matter whether this 246 // is a deep clone or not 247 for (Node child = (Node) value; child != null; 248 child = child.getNextSibling()) { 249 clone.appendChild(child.cloneNode(true)); 250 } 251 } 252 clone.isSpecified(true); 253 return clone; 254 } 255 256 /** 257 * A short integer indicating what type of node this is. The named 258 * constants for this value are defined in the org.w3c.dom.Node interface. 259 */ 260 public short getNodeType() { 261 return Node.ATTRIBUTE_NODE; 262 } 263 264 /** 265 * Returns the attribute name 266 */ 267 public String getNodeName() { 268 if (needsSyncData()) { 269 synchronizeData(); 270 } 271 return name; 272 } 273 274 /** 275 * Implicit in the rerouting of getNodeValue to getValue is the 276 * need to redefine setNodeValue, for symmetry's sake. Note that 277 * since we're explicitly providing a value, Specified should be set 278 * true.... even if that value equals the default. 279 */ 280 public void setNodeValue(String value) throws DOMException { 281 setValue(value); 282 } 283 284 /** 285 * @see org.w3c.dom.TypeInfo#getTypeName() 286 */ 287 public String getTypeName() { 288 return (String)type; 289 } 290 291 /** 292 * @see org.w3c.dom.TypeInfo#getTypeNamespace() 293 */ 294 public String getTypeNamespace() { 295 if (type != null) { 296 return DTD_URI; 297 } 298 return null; 299 } 300 301 /** 302 * Method getSchemaTypeInfo. 303 * @return TypeInfo 304 */ 305 public TypeInfo getSchemaTypeInfo(){ 306 return this; 307 } 308 309 /** 310 * In Attribute objects, NodeValue is considered a synonym for 311 * Value. 312 * 313 * @see #getValue() 314 */ 315 public String getNodeValue() { 316 return getValue(); 317 } 318 319 // 320 // Attr methods 321 // 322 323 /** 324 * In Attributes, NodeName is considered a synonym for the 325 * attribute's Name 326 */ 327 public String getName() { 328 329 if (needsSyncData()) { 330 synchronizeData(); 331 } 332 return name; 333 334 } // getName():String 335 336 /** 337 * The DOM doesn't clearly define what setValue(null) means. I've taken it 338 * as "remove all children", which from outside should appear 339 * similar to setting it to the empty string. 340 */ 341 public void setValue(String newvalue) { 342 343 CoreDocumentImpl ownerDocument = ownerDocument(); 344 345 if (ownerDocument.errorChecking && isReadOnly()) { 346 String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "NO_MODIFICATION_ALLOWED_ERR", null); 347 throw new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR, msg); 348 } 349 350 Element ownerElement = getOwnerElement(); 351 String oldvalue = ""; 352 if (needsSyncData()) { 353 synchronizeData(); 354 } 355 if (needsSyncChildren()) { 356 synchronizeChildren(); 357 } 358 if (value != null) { 359 if (ownerDocument.getMutationEvents()) { 360 // Can no longer just discard the kids; they may have 361 // event listeners waiting for them to disconnect. 362 if (hasStringValue()) { 363 oldvalue = (String) value; 364 // create an actual text node as our child so 365 // that we can use it in the event 366 if (textNode == null) { 367 textNode = (TextImpl) 368 ownerDocument.createTextNode((String) value); 369 } 370 else { 371 textNode.data = (String) value; 372 } 373 value = textNode; 374 textNode.isFirstChild(true); 375 textNode.previousSibling = textNode; 376 textNode.ownerNode = this; 377 textNode.isOwned(true); 378 hasStringValue(false); 379 internalRemoveChild(textNode, true); 380 } 381 else { 382 oldvalue = getValue(); 383 while (value != null) { 384 internalRemoveChild((Node) value, true); 385 } 386 } 387 } 388 else { 389 if (hasStringValue()) { 390 oldvalue = (String) value; 391 } 392 else { 393 // simply discard children if any 394 oldvalue = getValue(); 395 // remove ref from first child to last child 396 ChildNode firstChild = (ChildNode) value; 397 firstChild.previousSibling = null; 398 firstChild.isFirstChild(false); 399 firstChild.ownerNode = ownerDocument; 400 } 401 // then remove ref to current value 402 value = null; 403 needsSyncChildren(false); 404 } 405 if (isIdAttribute() && ownerElement != null) { 406 ownerDocument.removeIdentifier(oldvalue); 407 } 408 } 409 410 // Create and add the new one, generating only non-aggregate events 411 // (There are no listeners on the new Text, but there may be 412 // capture/bubble listeners on the Attr. 413 // Note that aggregate events are NOT dispatched here, 414 // since we need to combine the remove and insert. 415 isSpecified(true); 416 if (ownerDocument.getMutationEvents()) { 417 // if there are any event handlers create a real node 418 internalInsertBefore(ownerDocument.createTextNode(newvalue), 419 null, true); 420 hasStringValue(false); 421 // notify document 422 ownerDocument.modifiedAttrValue(this, oldvalue); 423 } else { 424 // directly store the string 425 value = newvalue; 426 hasStringValue(true); 427 changed(); 428 } 429 if (isIdAttribute() && ownerElement != null) { 430 ownerDocument.putIdentifier(newvalue, ownerElement); 431 } 432 433 } // setValue(String) 434 435 /** 436 * The "string value" of an Attribute is its text representation, 437 * which in turn is a concatenation of the string values of its children. 438 */ 439 public String getValue() { 440 441 if (needsSyncData()) { 442 synchronizeData(); 443 } 444 if (needsSyncChildren()) { 445 synchronizeChildren(); 446 } 447 if (value == null) { 448 return ""; 449 } 450 if (hasStringValue()) { 451 return (String) value; 452 } 453 454 ChildNode firstChild = ((ChildNode) value); 455 456 String data = null; 457 if (firstChild.getNodeType() == Node.ENTITY_REFERENCE_NODE){ 458 data = ((EntityReferenceImpl)firstChild).getEntityRefValue(); 459 } 460 else { 461 data = firstChild.getNodeValue(); 462 } 463 464 ChildNode node = firstChild.nextSibling; 465 466 if (node == null || data == null) return (data == null)?"":data; 467 468 StringBuffer value = new StringBuffer(data); 469 while (node != null) { 470 if (node.getNodeType() == Node.ENTITY_REFERENCE_NODE){ 471 data = ((EntityReferenceImpl)node).getEntityRefValue(); 472 if (data == null) return ""; 473 value.append(data); 474 } 475 else { 476 value.append(node.getNodeValue()); 477 } 478 node = node.nextSibling; 479 } 480 return value.toString(); 481 482 } // getValue():String 483 484 485 /** 486 * The "specified" flag is true if and only if this attribute's 487 * value was explicitly specified in the original document. Note that 488 * the implementation, not the user, is in charge of this 489 * property. If the user asserts an Attribute value (even if it ends 490 * up having the same value as the default), it is considered a 491 * specified attribute. If you really want to revert to the default, 492 * delete the attribute from the Element, and the Implementation will 493 * re-assert the default (if any) in its place, with the appropriate 494 * specified=false setting. 495 */ 496 public boolean getSpecified() { 497 498 if (needsSyncData()) { 499 synchronizeData(); 500 } 501 return isSpecified(); 502 503 } // getSpecified():boolean 504 505 // 506 // Attr2 methods 507 // 508 509 /** 510 * Returns the element node that this attribute is associated with, 511 * or null if the attribute has not been added to an element. 512 * 513 * @see #getOwnerElement 514 * 515 * @deprecated Previous working draft of DOM Level 2. New method 516 * is <tt>getOwnerElement()</tt>. 517 */ 518 @Deprecated 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 1240