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