src/java.xml/share/classes/com/sun/org/apache/xerces/internal/dom/ElementImpl.java

Print this page


   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