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