1 /* 2 * reserved comment block 3 * DO NOT REMOVE OR ALTER! 4 */ 5 /* 6 * Copyright 1999-2002,2004 The Apache Software Foundation. 7 * 8 * Licensed under the Apache License, Version 2.0 (the "License"); 9 * you may not use this file except in compliance with the License. 10 * 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 21 package com.sun.org.apache.xerces.internal.dom; 22 23 import org.w3c.dom.Attr; 24 import org.w3c.dom.DOMException; 25 import org.w3c.dom.Element; 26 import org.w3c.dom.NamedNodeMap; 27 import org.w3c.dom.Node; 28 import org.w3c.dom.NodeList; 29 import org.w3c.dom.Text; 30 31 import org.w3c.dom.TypeInfo; 32 import com.sun.org.apache.xerces.internal.util.URI; 33 34 /** 35 * Elements represent most of the "markup" and structure of the 36 * document. They contain both the data for the element itself 37 * (element name and attributes), and any contained nodes, including 38 * document text (as children). 39 * <P> 40 * Elements may have Attributes associated with them; the API for this is 41 * defined in Node, but the function is implemented here. In general, XML 42 * applications should retrive Attributes as Nodes, since they may contain 43 * entity references and hence be a fairly complex sub-tree. HTML users will 44 * be dealing with simple string values, and convenience methods are provided 45 * to work in terms of Strings. 46 * <P> 47 * ElementImpl does not support Namespaces. ElementNSImpl, which inherits from 48 * it, does. 49 * @see ElementNSImpl 50 * 51 * @xerces.internal 52 * 53 * @author Arnaud Le Hors, IBM 54 * @author Joe Kesselman, IBM 55 * @author Andy Clark, IBM 56 * @author Ralf Pfeiffer, IBM 57 * @since PR-DOM-Level-1-19980818. 58 */ 59 public class ElementImpl 60 extends ParentNode 61 implements Element, TypeInfo { 62 63 // 64 // Constants 65 // 66 67 /** Serialization version. */ 68 static final long serialVersionUID = 3717253516652722278L; 69 // 70 // Data 71 // 72 73 /** Element name. */ 74 protected String name; 75 76 /** Attributes. */ 77 protected AttributeMap attributes; 78 79 // 80 // Constructors 81 // 82 83 /** Factory constructor. */ 84 public ElementImpl(CoreDocumentImpl ownerDoc, String name) { 85 super(ownerDoc); 86 this.name = name; 87 needsSyncData(true); // synchronizeData will initialize attributes 88 } 89 90 // for ElementNSImpl 91 protected ElementImpl() {} 92 93 // Support for DOM Level 3 renameNode method. 94 // Note: This only deals with part of the pb. CoreDocumentImpl 95 // does all the work. 96 void rename(String name) { 97 if (needsSyncData()) { 98 synchronizeData(); 99 } 100 this.name = name; 101 reconcileDefaultAttributes(); 102 } 103 104 // 105 // Node methods 106 // 107 108 109 /** 110 * A short integer indicating what type of node this is. The named 111 * constants for this value are defined in the org.w3c.dom.Node interface. 112 */ 113 public short getNodeType() { 114 return Node.ELEMENT_NODE; 115 } 116 117 /** 118 * Returns the element name 119 */ 120 public String getNodeName() { 121 if (needsSyncData()) { 122 synchronizeData(); 123 } 124 return name; 125 } 126 127 /** 128 * Retrieve all the Attributes as a set. Note that this API is inherited 129 * from Node rather than specified on Element; in fact only Elements will 130 * ever have Attributes, but they want to allow folks to "blindly" operate 131 * on the tree as a set of Nodes. 132 */ 133 public NamedNodeMap getAttributes() { 134 135 if (needsSyncData()) { 136 synchronizeData(); 137 } 138 if (attributes == null) { 139 attributes = new AttributeMap(this, null); 140 } 141 return attributes; 142 143 } // getAttributes():NamedNodeMap 144 145 /** 146 * Return a duplicate copy of this Element. Note that its children 147 * will not be copied unless the "deep" flag is true, but Attributes 148 * are <i>always</i> replicated. 149 * 150 * @see org.w3c.dom.Node#cloneNode(boolean) 151 */ 152 public Node cloneNode(boolean deep) { 153 154 ElementImpl newnode = (ElementImpl) super.cloneNode(deep); 155 // Replicate NamedNodeMap rather than sharing it. 156 if (attributes != null) { 157 newnode.attributes = (AttributeMap) attributes.cloneMap(newnode); 158 } 159 return newnode; 160 161 } // cloneNode(boolean):Node 162 163 /** 164 * DOM Level 3 WD - Experimental. 165 * Retrieve baseURI 166 */ 167 public String getBaseURI() { 168 169 if (needsSyncData()) { 170 synchronizeData(); 171 } 172 // Absolute base URI is computed according to 173 // XML Base (http://www.w3.org/TR/xmlbase/#granularity) 174 // 1. The base URI specified by an xml:base attribute on the element, 175 // if one exists 176 if (attributes != null) { 177 Attr attrNode = (Attr)attributes.getNamedItem("xml:base"); 178 if (attrNode != null) { 179 String uri = attrNode.getNodeValue(); 180 if (uri.length() != 0 ) {// attribute value is always empty string 181 try { 182 uri = new URI(uri).toString(); 183 } 184 catch (com.sun.org.apache.xerces.internal.util.URI.MalformedURIException e) { 185 // This may be a relative URI. 186 187 // Make any parentURI into a URI object to use with the URI(URI, String) constructor 188 String parentBaseURI = (this.ownerNode != null) ? this.ownerNode.getBaseURI() : null; 189 if (parentBaseURI != null){ 190 try{ 191 uri = new URI(new URI(parentBaseURI), uri).toString(); 192 } 193 catch (com.sun.org.apache.xerces.internal.util.URI.MalformedURIException ex){ 194 // This should never happen: parent should have checked the URI and returned null if invalid. 195 return null; 196 } 197 return uri; 198 } 199 return null; 200 } 201 return uri; 202 } 203 } 204 } 205 206 // 2.the base URI of the element's parent element within the 207 // document or external entity, if one exists 208 // 3. the base URI of the document entity or external entity 209 // containing the element 210 211 // ownerNode serves as a parent or as document 212 String baseURI = (this.ownerNode != null) ? this.ownerNode.getBaseURI() : null ; 213 //base URI of parent element is not null 214 if(baseURI != null){ 215 try { 216 //return valid absolute base URI 217 return new URI(baseURI).toString(); 218 } 219 catch (com.sun.org.apache.xerces.internal.util.URI.MalformedURIException e){ 220 return null; 221 } 222 } 223 return null; 224 } //getBaseURI 225 226 227 228 /** 229 * NON-DOM 230 * set the ownerDocument of this node, its children, and its attributes 231 */ 232 void setOwnerDocument(CoreDocumentImpl doc) { 233 super.setOwnerDocument(doc); 234 if (attributes != null) { 235 attributes.setOwnerDocument(doc); 236 } 237 } 238 239 // 240 // Element methods 241 // 242 243 /** 244 * Look up a single Attribute by name. Returns the Attribute's 245 * string value, or an empty string (NOT null!) to indicate that the 246 * name did not map to a currently defined attribute. 247 * <p> 248 * Note: Attributes may contain complex node trees. This method 249 * returns the "flattened" string obtained from Attribute.getValue(). 250 * If you need the structure information, see getAttributeNode(). 251 */ 252 public String getAttribute(String name) { 253 254 if (needsSyncData()) { 255 synchronizeData(); 256 } 257 if (attributes == null) { 258 return ""; 259 } 260 Attr attr = (Attr)(attributes.getNamedItem(name)); 261 return (attr == null) ? "" : attr.getValue(); 262 263 } // getAttribute(String):String 264 265 266 /** 267 * Look up a single Attribute by name. Returns the Attribute Node, 268 * so its complete child tree is available. This could be important in 269 * XML, where the string rendering may not be sufficient information. 270 * <p> 271 * If no matching attribute is available, returns null. 272 */ 273 public Attr getAttributeNode(String name) { 274 275 if (needsSyncData()) { 276 synchronizeData(); 277 } 278 if (attributes == null) { 279 return null; 280 } 281 return (Attr)attributes.getNamedItem(name); 282 283 } // getAttributeNode(String):Attr 284 285 286 /** 287 * Returns a NodeList of all descendent nodes (children, 288 * grandchildren, and so on) which are Elements and which have the 289 * specified tag name. 290 * <p> 291 * Note: NodeList is a "live" view of the DOM. Its contents will 292 * change as the DOM changes, and alterations made to the NodeList 293 * will be reflected in the DOM. 294 * 295 * @param tagname The type of element to gather. To obtain a list of 296 * all elements no matter what their names, use the wild-card tag 297 * name "*". 298 * 299 * @see DeepNodeListImpl 300 */ 301 public NodeList getElementsByTagName(String tagname) { 302 return new DeepNodeListImpl(this,tagname); 303 } 304 305 /** 306 * Returns the name of the Element. Note that Element.nodeName() is 307 * defined to also return the tag name. 308 * <p> 309 * This is case-preserving in XML. HTML should uppercasify it on the 310 * way in. 311 */ 312 public String getTagName() { 313 if (needsSyncData()) { 314 synchronizeData(); 315 } 316 return name; 317 } 318 319 /** 320 * In "normal form" (as read from a source file), there will never be two 321 * Text children in succession. But DOM users may create successive Text 322 * nodes in the course of manipulating the document. Normalize walks the 323 * sub-tree and merges adjacent Texts, as if the DOM had been written out 324 * and read back in again. This simplifies implementation of higher-level 325 * functions that may want to assume that the document is in standard form. 326 * <p> 327 * To normalize a Document, normalize its top-level Element child. 328 * <p> 329 * As of PR-DOM-Level-1-19980818, CDATA -- despite being a subclass of 330 * Text -- is considered "markup" and will _not_ be merged either with 331 * normal Text or with other CDATASections. 332 */ 333 public void normalize() { 334 // No need to normalize if already normalized. 335 if (isNormalized()) { 336 return; 337 } 338 if (needsSyncChildren()) { 339 synchronizeChildren(); 340 } 341 ChildNode kid, next; 342 for (kid = firstChild; kid != null; kid = next) { 343 next = kid.nextSibling; 344 345 // If kid is a text node, we need to check for one of two 346 // conditions: 347 // 1) There is an adjacent text node 348 // 2) There is no adjacent text node, but kid is 349 // an empty text node. 350 if ( kid.getNodeType() == Node.TEXT_NODE ) 351 { 352 // If an adjacent text node, merge it with kid 353 if ( next!=null && next.getNodeType() == Node.TEXT_NODE ) 354 { 355 ((Text)kid).appendData(next.getNodeValue()); 356 removeChild( next ); 357 next = kid; // Don't advance; there might be another. 358 } 359 else 360 { 361 // If kid is empty, remove it 362 if ( kid.getNodeValue() == null || kid.getNodeValue().length() == 0 ) { 363 removeChild( kid ); 364 } 365 } 366 } 367 368 // Otherwise it might be an Element, which is handled recursively 369 else if (kid.getNodeType() == Node.ELEMENT_NODE) { 370 kid.normalize(); 371 } 372 } 373 374 // We must also normalize all of the attributes 375 if ( attributes!=null ) 376 { 377 for( int i=0; i<attributes.getLength(); ++i ) 378 { 379 Node attr = attributes.item(i); 380 attr.normalize(); 381 } 382 } 383 384 // changed() will have occurred when the removeChild() was done, 385 // so does not have to be reissued. 386 387 isNormalized(true); 388 } // normalize() 389 390 /** 391 * Remove the named attribute from this Element. If the removed 392 * Attribute has a default value, it is immediately replaced thereby. 393 * <P> 394 * The default logic is actually implemented in NamedNodeMapImpl. 395 * PR-DOM-Level-1-19980818 doesn't fully address the DTD, so some 396 * of this behavior is likely to change in future versions. ????? 397 * <P> 398 * Note that this call "succeeds" even if no attribute by this name 399 * existed -- unlike removeAttributeNode, which will throw a not-found 400 * exception in that case. 401 * 402 * @throws DOMException(NO_MODIFICATION_ALLOWED_ERR) if the node is 403 * readonly. 404 */ 405 public void removeAttribute(String name) { 406 407 if (ownerDocument.errorChecking && isReadOnly()) { 408 String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "NO_MODIFICATION_ALLOWED_ERR", null); 409 throw new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR, msg); 410 } 411 412 if (needsSyncData()) { 413 synchronizeData(); 414 } 415 416 if (attributes == null) { 417 return; 418 } 419 420 attributes.safeRemoveNamedItem(name); 421 422 } // removeAttribute(String) 423 424 425 /** 426 * Remove the specified attribute/value pair. If the removed 427 * Attribute has a default value, it is immediately replaced. 428 * <p> 429 * NOTE: Specifically removes THIS NODE -- not the node with this 430 * name, nor the node with these contents. If the specific Attribute 431 * object passed in is not stored in this Element, we throw a 432 * DOMException. If you really want to remove an attribute by name, 433 * use removeAttribute(). 434 * 435 * @return the Attribute object that was removed. 436 * @throws DOMException(NOT_FOUND_ERR) if oldattr is not an attribute of 437 * this Element. 438 * @throws DOMException(NO_MODIFICATION_ALLOWED_ERR) if the node is 439 * readonly. 440 */ 441 public Attr removeAttributeNode(Attr oldAttr) 442 throws DOMException { 443 444 if (ownerDocument.errorChecking && isReadOnly()) { 445 String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "NO_MODIFICATION_ALLOWED_ERR", null); 446 throw new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR, msg); 447 } 448 449 if (needsSyncData()) { 450 synchronizeData(); 451 } 452 453 if (attributes == null) { 454 String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "NOT_FOUND_ERR", null); 455 throw new DOMException(DOMException.NOT_FOUND_ERR, msg); 456 } 457 return (Attr) attributes.removeItem(oldAttr, true); 458 459 } // removeAttributeNode(Attr):Attr 460 461 462 /** 463 * Add a new name/value pair, or replace the value of the existing 464 * attribute having that name. 465 * 466 * Note: this method supports only the simplest kind of Attribute, 467 * one whose value is a string contained in a single Text node. 468 * If you want to assert a more complex value (which XML permits, 469 * though HTML doesn't), see setAttributeNode(). 470 * 471 * The attribute is created with specified=true, meaning it's an 472 * explicit value rather than inherited from the DTD as a default. 473 * Again, setAttributeNode can be used to achieve other results. 474 * 475 * @throws DOMException(INVALID_NAME_ERR) if the name is not acceptable. 476 * (Attribute factory will do that test for us.) 477 * 478 * @throws DOMException(NO_MODIFICATION_ALLOWED_ERR) if the node is 479 * readonly. 480 */ 481 public void setAttribute(String name, String value) { 482 483 if (ownerDocument.errorChecking && isReadOnly()) { 484 String msg = 485 DOMMessageFormatter.formatMessage( 486 DOMMessageFormatter.DOM_DOMAIN, 487 "NO_MODIFICATION_ALLOWED_ERR", 488 null); 489 throw new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR, msg); 490 } 491 492 if (needsSyncData()) { 493 synchronizeData(); 494 } 495 496 Attr newAttr = getAttributeNode(name); 497 if (newAttr == null) { 498 newAttr = getOwnerDocument().createAttribute(name); 499 500 if (attributes == null) { 501 attributes = new AttributeMap(this, null); 502 } 503 504 newAttr.setNodeValue(value); 505 attributes.setNamedItem(newAttr); 506 } 507 else { 508 newAttr.setNodeValue(value); 509 } 510 511 } // setAttribute(String,String) 512 513 /** 514 * Add a new attribute/value pair, or replace the value of the 515 * existing attribute with that name. 516 * <P> 517 * This method allows you to add an Attribute that has already been 518 * constructed, and hence avoids the limitations of the simple 519 * setAttribute() call. It can handle attribute values that have 520 * arbitrarily complex tree structure -- in particular, those which 521 * had entity references mixed into their text. 522 * 523 * @throws DOMException(INUSE_ATTRIBUTE_ERR) if the Attribute object 524 * has already been assigned to another Element. 525 */ 526 public Attr setAttributeNode(Attr newAttr) 527 throws DOMException 528 { 529 530 if (needsSyncData()) { 531 synchronizeData(); 532 } 533 534 if (ownerDocument.errorChecking) { 535 if (isReadOnly()) { 536 String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "NO_MODIFICATION_ALLOWED_ERR", null); 537 throw new DOMException( 538 DOMException.NO_MODIFICATION_ALLOWED_ERR, 539 msg); 540 } 541 542 if (newAttr.getOwnerDocument() != ownerDocument) { 543 String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "WRONG_DOCUMENT_ERR", null); 544 throw new DOMException(DOMException.WRONG_DOCUMENT_ERR, msg); 545 } 546 } 547 548 if (attributes == null) { 549 attributes = new AttributeMap(this, null); 550 } 551 // This will throw INUSE if necessary 552 return (Attr) attributes.setNamedItem(newAttr); 553 554 } // setAttributeNode(Attr):Attr 555 556 // 557 // DOM2: Namespace methods 558 // 559 560 /** 561 * Introduced in DOM Level 2. <p> 562 * 563 * Retrieves an attribute value by local name and namespace URI. 564 * 565 * @param namespaceURI 566 * The namespace URI of the attribute to 567 * retrieve. 568 * @param localName The local name of the attribute to retrieve. 569 * @return String The Attr value as a string, or empty string 570 * if that attribute 571 * does not have a specified or default value. 572 * @since WD-DOM-Level-2-19990923 573 */ 574 public String getAttributeNS(String namespaceURI, String localName) { 575 576 if (needsSyncData()) { 577 synchronizeData(); 578 } 579 580 if (attributes == null) { 581 return ""; 582 } 583 584 Attr attr = (Attr)(attributes.getNamedItemNS(namespaceURI, localName)); 585 return (attr == null) ? "" : attr.getValue(); 586 587 } // getAttributeNS(String,String):String 588 589 /** 590 * Introduced in DOM Level 2. <p> 591 * 592 * Adds a new attribute. 593 * If the given namespaceURI is null or an empty string and the 594 * qualifiedName has a prefix that is "xml", the new attribute is bound to 595 * the predefined namespace "http://www.w3.org/XML/1998/namespace" 596 * [Namespaces]. If an attribute with the same local name and namespace 597 * URI is already present on the element, its prefix is changed to be the 598 * prefix part of the qualifiedName, and its value is changed to be the 599 * value parameter. This value is a simple string, it is not parsed as it 600 * is being set. So any markup (such as syntax to be recognized as an 601 * entity reference) is treated as literal text, and needs to be 602 * appropriately escaped by the implementation when it is written out. In 603 * order to assign an attribute value that contains entity references, the 604 * user must create an Attr node plus any Text and EntityReference nodes, 605 * build the appropriate subtree, and use setAttributeNodeNS or 606 * setAttributeNode to assign it as the value of an attribute. 607 * 608 * @param namespaceURI The namespace URI of the attribute to create 609 * or alter. 610 * @param qualifiedName The qualified name of the attribute to create or 611 * alter. 612 * @param value The value to set in string form. 613 * @throws INVALID_CHARACTER_ERR: Raised if the specified 614 * name contains an invalid character. 615 * 616 * @throws NO_MODIFICATION_ALLOWED_ERR: Raised if this 617 * node is readonly. 618 * 619 * @throws NAMESPACE_ERR: Raised if the qualifiedName 620 * has a prefix that is "xml" and the namespaceURI 621 * is neither null nor an empty string nor 622 * "http://www.w3.org/XML/1998/namespace", or if 623 * the qualifiedName has a prefix that is "xmlns" 624 * but the namespaceURI is neither null nor an 625 * empty string, or if if the qualifiedName has a 626 * prefix different from "xml" and "xmlns" and the 627 * namespaceURI is null or an empty string. 628 * @since WD-DOM-Level-2-19990923 629 */ 630 public void setAttributeNS(String namespaceURI,String qualifiedName, 631 String value) { 632 if (ownerDocument.errorChecking && isReadOnly()) { 633 String msg = 634 DOMMessageFormatter.formatMessage( 635 DOMMessageFormatter.DOM_DOMAIN, 636 "NO_MODIFICATION_ALLOWED_ERR", 637 null); 638 throw new DOMException( 639 DOMException.NO_MODIFICATION_ALLOWED_ERR, 640 msg); 641 } 642 if (needsSyncData()) { 643 synchronizeData(); 644 } 645 int index = qualifiedName.indexOf(':'); 646 String prefix, localName; 647 if (index < 0) { 648 prefix = null; 649 localName = qualifiedName; 650 } 651 else { 652 prefix = qualifiedName.substring(0, index); 653 localName = qualifiedName.substring(index + 1); 654 } 655 Attr newAttr = getAttributeNodeNS(namespaceURI, localName); 656 if (newAttr == null) { 657 // REVISIT: this is not efficient, we are creating twice the same 658 // strings for prefix and localName. 659 newAttr = getOwnerDocument().createAttributeNS( 660 namespaceURI, 661 qualifiedName); 662 if (attributes == null) { 663 attributes = new AttributeMap(this, null); 664 } 665 newAttr.setNodeValue(value); 666 attributes.setNamedItemNS(newAttr); 667 } 668 else { 669 if (newAttr instanceof AttrNSImpl){ 670 String origNodeName = ((AttrNSImpl) newAttr).name; 671 String newName = (prefix!=null) ? (prefix+":"+localName) : localName; 672 673 ((AttrNSImpl) newAttr).name = newName; 674 675 if (!newName.equals(origNodeName)) { 676 // Note: we can't just change the name of the attribute. Names have to be in sorted 677 // order in the attributes vector because a binary search is used to locate them. 678 // If the new name has a different prefix, the list may become unsorted. 679 // Maybe it would be better to resort the list, but the simplest 680 // fix seems to be to remove the old attribute and re-insert it. 681 // -- Norman.Walsh@Sun.COM, 2 Feb 2007 682 newAttr = (Attr) attributes.removeItem(newAttr, false); 683 attributes.addItem(newAttr); 684 } 685 } 686 else { 687 // This case may happen if user calls: 688 // elem.setAttribute("name", "value"); 689 // elem.setAttributeNS(null, "name", "value"); 690 // This case is not defined by the DOM spec, we choose 691 // to create a new attribute in this case and remove an old one from the tree 692 // note this might cause events to be propagated or user data to be lost 693 newAttr = new AttrNSImpl((CoreDocumentImpl)getOwnerDocument(), namespaceURI, qualifiedName, localName); 694 attributes.setNamedItemNS(newAttr); 695 } 696 697 newAttr.setNodeValue(value); 698 } 699 700 } // setAttributeNS(String,String,String) 701 702 703 /** 704 * Introduced in DOM Level 2. <p> 705 * 706 * Removes an attribute by local name and namespace URI. If the removed 707 * attribute has a default value it is immediately replaced. 708 * The replacing attribute has the same namespace URI and local name, 709 * as well as the original prefix.<p> 710 * 711 * @param namespaceURI The namespace URI of the attribute to remove. 712 * 713 * @param localName The local name of the attribute to remove. 714 * @throws NO_MODIFICATION_ALLOWED_ERR: Raised if this 715 * node is readonly. 716 * @since WD-DOM-Level-2-19990923 717 */ 718 public void removeAttributeNS(String namespaceURI, String localName) { 719 720 if (ownerDocument.errorChecking && isReadOnly()) { 721 String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "NO_MODIFICATION_ALLOWED_ERR", null); 722 throw new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR, msg); 723 } 724 725 if (needsSyncData()) { 726 synchronizeData(); 727 } 728 729 if (attributes == null) { 730 return; 731 } 732 733 attributes.safeRemoveNamedItemNS(namespaceURI, localName); 734 735 } // removeAttributeNS(String,String) 736 737 /** 738 * Retrieves an Attr node by local name and namespace URI. 739 * 740 * @param namespaceURI The namespace URI of the attribute to 741 * retrieve. 742 * @param localName The local name of the attribute to retrieve. 743 * @return Attr The Attr node with the specified attribute 744 * local name and namespace 745 * URI or null if there is no such attribute. 746 * @since WD-DOM-Level-2-19990923 747 */ 748 public Attr getAttributeNodeNS(String namespaceURI, String localName){ 749 750 if (needsSyncData()) { 751 synchronizeData(); 752 } 753 if (attributes == null) { 754 return null; 755 } 756 return (Attr)attributes.getNamedItemNS(namespaceURI, localName); 757 758 } // getAttributeNodeNS(String,String):Attr 759 760 /** 761 * Introduced in DOM Level 2. <p> 762 * 763 * Adds a new attribute. If an attribute with that local name and 764 * namespace URI is already present in the element, it is replaced 765 * by the new one. 766 * 767 * @param Attr The Attr node to add to the attribute list. When 768 * the Node has no namespaceURI, this method behaves 769 * like setAttributeNode. 770 * @return Attr If the newAttr attribute replaces an existing attribute 771 * with the same local name and namespace URI, the * 772 * previously existing Attr node is returned, otherwise 773 * null is returned. 774 * @throws WRONG_DOCUMENT_ERR: Raised if newAttr 775 * was created from a different document than the one that 776 * created the element. 777 * 778 * @throws NO_MODIFICATION_ALLOWED_ERR: Raised if 779 * this node is readonly. 780 * 781 * @throws INUSE_ATTRIBUTE_ERR: Raised if newAttr is 782 * already an attribute of another Element object. The 783 * DOM user must explicitly clone Attr nodes to re-use 784 * them in other elements. 785 * @since WD-DOM-Level-2-19990923 786 */ 787 public Attr setAttributeNodeNS(Attr newAttr) 788 throws DOMException 789 { 790 791 if (needsSyncData()) { 792 synchronizeData(); 793 } 794 if (ownerDocument.errorChecking) { 795 if (isReadOnly()) { 796 String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "NO_MODIFICATION_ALLOWED_ERR", null); 797 throw new DOMException( 798 DOMException.NO_MODIFICATION_ALLOWED_ERR, 799 msg); 800 } 801 if (newAttr.getOwnerDocument() != ownerDocument) { 802 String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "WRONG_DOCUMENT_ERR", null); 803 throw new DOMException(DOMException.WRONG_DOCUMENT_ERR, msg); 804 } 805 } 806 807 if (attributes == null) { 808 attributes = new AttributeMap(this, null); 809 } 810 // This will throw INUSE if necessary 811 return (Attr) attributes.setNamedItemNS(newAttr); 812 813 } // setAttributeNodeNS(Attr):Attr 814 815 /** 816 * NON-DOM: sets attribute node for this element 817 */ 818 protected int setXercesAttributeNode (Attr attr){ 819 820 if (needsSyncData()) { 821 synchronizeData(); 822 } 823 824 if (attributes == null) { 825 attributes = new AttributeMap(this, null); 826 } 827 return attributes.addItem(attr); 828 829 } 830 831 /** 832 * NON-DOM: get inded of an attribute 833 */ 834 protected int getXercesAttribute(String namespaceURI, String localName){ 835 836 if (needsSyncData()) { 837 synchronizeData(); 838 } 839 if (attributes == null) { 840 return -1; 841 } 842 return attributes.getNamedItemIndex(namespaceURI, localName); 843 844 } 845 846 /** 847 * Introduced in DOM Level 2. 848 */ 849 public boolean hasAttributes() { 850 if (needsSyncData()) { 851 synchronizeData(); 852 } 853 return (attributes != null && attributes.getLength() != 0); 854 } 855 856 /** 857 * Introduced in DOM Level 2. 858 */ 859 public boolean hasAttribute(String name) { 860 return getAttributeNode(name) != null; 861 } 862 863 /** 864 * Introduced in DOM Level 2. 865 */ 866 public boolean hasAttributeNS(String namespaceURI, String localName) { 867 return getAttributeNodeNS(namespaceURI, localName) != null; 868 } 869 870 /** 871 * Introduced in DOM Level 2. <p> 872 * 873 * Returns a NodeList of all the Elements with a given local name and 874 * namespace URI in the order in which they would be encountered in a 875 * preorder traversal of the Document tree, starting from this node. 876 * 877 * @param namespaceURI The namespace URI of the elements to match 878 * on. The special value "*" matches all 879 * namespaces. When it is null or an empty 880 * string, this method behaves like 881 * getElementsByTagName. 882 * @param localName The local name of the elements to match on. 883 * The special value "*" matches all local names. 884 * @return NodeList A new NodeList object containing all the matched 885 * Elements. 886 * @since WD-DOM-Level-2-19990923 887 */ 888 public NodeList getElementsByTagNameNS(String namespaceURI, 889 String localName) { 890 return new DeepNodeListImpl(this, namespaceURI, localName); 891 } 892 893 /** 894 * DOM Level 3 WD- Experimental. 895 * Override inherited behavior from NodeImpl and ParentNode to check on 896 * attributes 897 */ 898 public boolean isEqualNode(Node arg) { 899 if (!super.isEqualNode(arg)) { 900 return false; 901 } 902 boolean hasAttrs = hasAttributes(); 903 if (hasAttrs != ((Element) arg).hasAttributes()) { 904 return false; 905 } 906 if (hasAttrs) { 907 NamedNodeMap map1 = getAttributes(); 908 NamedNodeMap map2 = ((Element) arg).getAttributes(); 909 int len = map1.getLength(); 910 if (len != map2.getLength()) { 911 return false; 912 } 913 for (int i = 0; i < len; i++) { 914 Node n1 = map1.item(i); 915 if (n1.getLocalName() == null) { // DOM Level 1 Node 916 Node n2 = map2.getNamedItem(n1.getNodeName()); 917 if (n2 == null || !((NodeImpl) n1).isEqualNode(n2)) { 918 return false; 919 } 920 } 921 else { 922 Node n2 = map2.getNamedItemNS(n1.getNamespaceURI(), 923 n1.getLocalName()); 924 if (n2 == null || !((NodeImpl) n1).isEqualNode(n2)) { 925 return false; 926 } 927 } 928 } 929 } 930 return true; 931 } 932 933 /** 934 * DOM Level 3: register the given attribute node as an ID attribute 935 */ 936 public void setIdAttributeNode(Attr at, boolean makeId) { 937 if (needsSyncData()) { 938 synchronizeData(); 939 } 940 if (ownerDocument.errorChecking) { 941 if (isReadOnly()) { 942 String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "NO_MODIFICATION_ALLOWED_ERR", null); 943 throw new DOMException( 944 DOMException.NO_MODIFICATION_ALLOWED_ERR, 945 msg); 946 } 947 948 if (at.getOwnerElement() != this) { 949 String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "NOT_FOUND_ERR", null); 950 throw new DOMException(DOMException.NOT_FOUND_ERR, msg); 951 } 952 } 953 ((AttrImpl) at).isIdAttribute(makeId); 954 if (!makeId) { 955 ownerDocument.removeIdentifier(at.getValue()); 956 } 957 else { 958 ownerDocument.putIdentifier(at.getValue(), this); 959 } 960 } 961 962 /** 963 * DOM Level 3: register the given attribute node as an ID attribute 964 */ 965 public void setIdAttribute(String name, boolean makeId) { 966 if (needsSyncData()) { 967 synchronizeData(); 968 } 969 Attr at = getAttributeNode(name); 970 971 if( at == null){ 972 String msg = DOMMessageFormatter.formatMessage( 973 DOMMessageFormatter.DOM_DOMAIN, 974 "NOT_FOUND_ERR", null); 975 throw new DOMException(DOMException.NOT_FOUND_ERR, msg); 976 } 977 978 if (ownerDocument.errorChecking) { 979 if (isReadOnly()) { 980 String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "NO_MODIFICATION_ALLOWED_ERR", null); 981 throw new DOMException( 982 DOMException.NO_MODIFICATION_ALLOWED_ERR, 983 msg); 984 } 985 986 if (at.getOwnerElement() != this) { 987 String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "NOT_FOUND_ERR", null); 988 throw new DOMException(DOMException.NOT_FOUND_ERR, msg); 989 } 990 } 991 992 ((AttrImpl) at).isIdAttribute(makeId); 993 if (!makeId) { 994 ownerDocument.removeIdentifier(at.getValue()); 995 } 996 else { 997 ownerDocument.putIdentifier(at.getValue(), this); 998 } 999 } 1000 1001 /** 1002 * DOM Level 3: register the given attribute node as an ID attribute 1003 */ 1004 public void setIdAttributeNS(String namespaceURI, String localName, 1005 boolean makeId) { 1006 if (needsSyncData()) { 1007 synchronizeData(); 1008 } 1009 //if namespace uri is empty string, set it to 'null' 1010 if (namespaceURI != null) { 1011 namespaceURI = (namespaceURI.length() == 0)? null : namespaceURI; 1012 } 1013 Attr at = getAttributeNodeNS(namespaceURI, localName); 1014 1015 if( at == null){ 1016 String msg = DOMMessageFormatter.formatMessage( 1017 DOMMessageFormatter.DOM_DOMAIN, 1018 "NOT_FOUND_ERR", null); 1019 throw new DOMException(DOMException.NOT_FOUND_ERR, msg); 1020 } 1021 1022 if (ownerDocument.errorChecking) { 1023 if (isReadOnly()) { 1024 String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "NO_MODIFICATION_ALLOWED_ERR", null); 1025 throw new DOMException( 1026 DOMException.NO_MODIFICATION_ALLOWED_ERR, 1027 msg); 1028 } 1029 1030 if (at.getOwnerElement() != this) { 1031 String msg = DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN, "NOT_FOUND_ERR", null); 1032 throw new DOMException(DOMException.NOT_FOUND_ERR, msg); 1033 } 1034 } 1035 ((AttrImpl) at).isIdAttribute(makeId); 1036 if (!makeId) { 1037 ownerDocument.removeIdentifier(at.getValue()); 1038 } 1039 else { 1040 ownerDocument.putIdentifier(at.getValue(), this); 1041 } 1042 } 1043 1044 /** 1045 * @see org.w3c.dom.TypeInfo#getTypeName() 1046 */ 1047 public String getTypeName() { 1048 return null; 1049 } 1050 1051 /** 1052 * @see org.w3c.dom.TypeInfo#getTypeNamespace() 1053 */ 1054 public String getTypeNamespace() { 1055 return null; 1056 } 1057 1058 /** 1059 * Introduced in DOM Level 3. <p> 1060 * Checks if a type is derived from another by restriction. See: 1061 * http://www.w3.org/TR/DOM-Level-3-Core/core.html#TypeInfo-isDerivedFrom 1062 * 1063 * @param ancestorNS 1064 * The namspace of the ancestor type declaration 1065 * @param ancestorName 1066 * The name of the ancestor type declaration 1067 * @param type 1068 * The reference type definition 1069 * 1070 * @return boolean True if the type is derived by restriciton for the 1071 * reference type 1072 */ 1073 public boolean isDerivedFrom(String typeNamespaceArg, 1074 String typeNameArg, 1075 int derivationMethod) { 1076 1077 return false; 1078 } 1079 1080 /** 1081 * Method getSchemaTypeInfo. 1082 * @return TypeInfo 1083 */ 1084 public TypeInfo getSchemaTypeInfo(){ 1085 if(needsSyncData()) { 1086 synchronizeData(); 1087 } 1088 return this; 1089 } 1090 1091 // 1092 // Public methods 1093 // 1094 1095 /** 1096 * NON-DOM: Subclassed to flip the attributes' readonly switch as well. 1097 * @see NodeImpl#setReadOnly 1098 */ 1099 public void setReadOnly(boolean readOnly, boolean deep) { 1100 super.setReadOnly(readOnly,deep); 1101 if (attributes != null) { 1102 attributes.setReadOnly(readOnly,true); 1103 } 1104 } 1105 1106 1107 1108 // 1109 // Protected methods 1110 // 1111 1112 /** Synchronizes the data (name and value) for fast nodes. */ 1113 protected void synchronizeData() { 1114 1115 // no need to sync in the future 1116 needsSyncData(false); 1117 1118 // we don't want to generate any event for this so turn them off 1119 boolean orig = ownerDocument.getMutationEvents(); 1120 ownerDocument.setMutationEvents(false); 1121 1122 // attributes 1123 setupDefaultAttributes(); 1124 1125 // set mutation events flag back to its original value 1126 ownerDocument.setMutationEvents(orig); 1127 1128 } // synchronizeData() 1129 1130 // support for DOM Level 3 renameNode method 1131 // @param el The element from which to take the attributes 1132 void moveSpecifiedAttributes(ElementImpl el) { 1133 if (needsSyncData()) { 1134 synchronizeData(); 1135 } 1136 if (el.hasAttributes()) { 1137 if (attributes == null) { 1138 attributes = new AttributeMap(this, null); 1139 } 1140 attributes.moveSpecifiedAttributes(el.attributes); 1141 } 1142 } 1143 1144 /** Setup the default attributes. */ 1145 protected void setupDefaultAttributes() { 1146 NamedNodeMapImpl defaults = getDefaultAttributes(); 1147 if (defaults != null) { 1148 attributes = new AttributeMap(this, defaults); 1149 } 1150 } 1151 1152 /** Reconcile default attributes. */ 1153 protected void reconcileDefaultAttributes() { 1154 if (attributes != null) { 1155 NamedNodeMapImpl defaults = getDefaultAttributes(); 1156 attributes.reconcileDefaults(defaults); 1157 } 1158 } 1159 1160 /** Get the default attributes. */ 1161 protected NamedNodeMapImpl getDefaultAttributes() { 1162 1163 DocumentTypeImpl doctype = 1164 (DocumentTypeImpl) ownerDocument.getDoctype(); 1165 if (doctype == null) { 1166 return null; 1167 } 1168 ElementDefinitionImpl eldef = 1169 (ElementDefinitionImpl)doctype.getElements() 1170 .getNamedItem(getNodeName()); 1171 if (eldef == null) { 1172 return null; 1173 } 1174 return (NamedNodeMapImpl) eldef.getAttributes(); 1175 1176 } // getDefaultAttributes() 1177 1178 } // class ElementImpl