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