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; 676 677 ((AttrNSImpl) newAttr).name = newName; 678 679 if (!newName.equals(origNodeName)) { 680 // Note: we can't just change the name of the attribute. Names have to be in sorted 681 // order in the attributes vector because a binary search is used to locate them. 682 // If the new name has a different prefix, the list may become unsorted. 683 // Maybe it would be better to resort the list, but the simplest 684 // fix seems to be to remove the old attribute and re-insert it. 685 // -- Norman.Walsh@Sun.COM, 2 Feb 2007 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