001: /*
002: * The Apache Software License, Version 1.1
003: *
004: *
005: * Copyright (c) 1999 The Apache Software Foundation. All rights
006: * reserved.
007: *
008: * Redistribution and use in source and binary forms, with or without
009: * modification, are permitted provided that the following conditions
010: * are met:
011: *
012: * 1. Redistributions of source code must retain the above copyright
013: * notice, this list of conditions and the following disclaimer.
014: *
015: * 2. Redistributions in binary form must reproduce the above copyright
016: * notice, this list of conditions and the following disclaimer in
017: * the documentation and/or other materials provided with the
018: * distribution.
019: *
020: * 3. The end-user documentation included with the redistribution,
021: * if any, must include the following acknowledgment:
022: * "This product includes software developed by the
023: * Apache Software Foundation (http://www.apache.org/)."
024: * Alternately, this acknowledgment may appear in the software itself,
025: * if and wherever such third-party acknowledgments normally appear.
026: *
027: * 4. The names "Xerces" and "Apache Software Foundation" must
028: * not be used to endorse or promote products derived from this
029: * software without prior written permission. For written
030: * permission, please contact apache@apache.org.
031: *
032: * 5. Products derived from this software may not be called "Apache",
033: * nor may "Apache" appear in their name, without prior written
034: * permission of the Apache Software Foundation.
035: *
036: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
037: * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
038: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
039: * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
040: * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
041: * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
042: * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
043: * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
044: * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
045: * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
046: * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
047: * SUCH DAMAGE.
048: * ====================================================================
049: *
050: * This software consists of voluntary contributions made by many
051: * individuals on behalf of the Apache Software Foundation and was
052: * originally based on software copyright (c) 1999, International
053: * Business Machines, Inc., http://www.apache.org. For more
054: * information on the Apache Software Foundation, please see
055: * <http://www.apache.org/>.
056: */
057:
058: package org.apache.xerces.dom;
059:
060: import org.w3c.dom.Attr;
061: import org.w3c.dom.DOMException;
062: import org.w3c.dom.Element;
063: import org.w3c.dom.NamedNodeMap;
064: import org.w3c.dom.Node;
065: import org.w3c.dom.NodeList;
066: import org.w3c.dom.Text;
067:
068: /**
069: * Elements represent most of the "markup" and structure of the
070: * document. They contain both the data for the element itself
071: * (element name and attributes), and any contained nodes, including
072: * document text (as children).
073: * <P>
074: * Elements may have Attributes associated with them; the API for this is
075: * defined in Node, but the function is implemented here. In general, XML
076: * applications should retrive Attributes as Nodes, since they may contain
077: * entity references and hence be a fairly complex sub-tree. HTML users will
078: * be dealing with simple string values, and convenience methods are provided
079: * to work in terms of Strings.
080: * <P>
081: * ElementImpl does not support Namespaces. ElementNSImpl, which inherits from
082: * it, does.
083: * @see ElementNSImpl
084: *
085: * @author Arnaud Le Hors, IBM
086: * @author Joe Kesselman, IBM
087: * @author Andy Clark, IBM
088: * @author Ralf Pfeiffer, IBM
089: * @version
090: * @since PR-DOM-Level-1-19980818.
091: */
092: public class ElementImpl extends ParentNode implements Element {
093:
094: //
095: // Constants
096: //
097:
098: /** Serialization version. */
099: static final long serialVersionUID = 3717253516652722278L;
100: //
101: // Data
102: //
103:
104: /** Element name. */
105: protected String name;
106:
107: /** Attributes. */
108: protected AttributeMap attributes;
109:
110: //
111: // Constructors
112: //
113:
114: /** Factory constructor. */
115: public ElementImpl(CoreDocumentImpl ownerDoc, String name) {
116: super (ownerDoc);
117: this .name = name;
118: needsSyncData(true); // synchronizeData will initialize attributes
119: }
120:
121: // for ElementNSImpl
122: protected ElementImpl() {
123: }
124:
125: //
126: // Node methods
127: //
128:
129: /**
130: * A short integer indicating what type of node this is. The named
131: * constants for this value are defined in the org.w3c.dom.Node interface.
132: */
133: public short getNodeType() {
134: return Node.ELEMENT_NODE;
135: }
136:
137: /**
138: * Returns the element name
139: */
140: public String getNodeName() {
141: if (needsSyncData()) {
142: synchronizeData();
143: }
144: return name;
145: }
146:
147: /**
148: * Retrieve all the Attributes as a set. Note that this API is inherited
149: * from Node rather than specified on Element; in fact only Elements will
150: * ever have Attributes, but they want to allow folks to "blindly" operate
151: * on the tree as a set of Nodes.
152: */
153: public NamedNodeMap getAttributes() {
154:
155: if (needsSyncData()) {
156: synchronizeData();
157: }
158: if (attributes == null) {
159: attributes = new AttributeMap(this , null);
160: }
161: return attributes;
162:
163: } // getAttributes():NamedNodeMap
164:
165: /**
166: * Return a duplicate copy of this Element. Note that its children
167: * will not be copied unless the "deep" flag is true, but Attributes
168: * are <i>always</i> replicated.
169: *
170: * @see org.w3c.dom.Node#cloneNode(boolean)
171: */
172: public Node cloneNode(boolean deep) {
173:
174: ElementImpl newnode = (ElementImpl) super .cloneNode(deep);
175: // Replicate NamedNodeMap rather than sharing it.
176: if (attributes != null) {
177: newnode.attributes = (AttributeMap) attributes
178: .cloneMap(newnode);
179: }
180: return newnode;
181:
182: } // cloneNode(boolean):Node
183:
184: /**
185: * NON-DOM
186: * set the ownerDocument of this node, its children, and its attributes
187: */
188: void setOwnerDocument(CoreDocumentImpl doc) {
189: super .setOwnerDocument(doc);
190: if (attributes != null) {
191: attributes.setOwnerDocument(doc);
192: }
193: }
194:
195: //
196: // Element methods
197: //
198:
199: /**
200: * Look up a single Attribute by name. Returns the Attribute's
201: * string value, or an empty string (NOT null!) to indicate that the
202: * name did not map to a currently defined attribute.
203: * <p>
204: * Note: Attributes may contain complex node trees. This method
205: * returns the "flattened" string obtained from Attribute.getValue().
206: * If you need the structure information, see getAttributeNode().
207: */
208: public String getAttribute(String name) {
209:
210: if (needsSyncData()) {
211: synchronizeData();
212: }
213: if (attributes == null) {
214: return "";
215: }
216: Attr attr = (Attr) (attributes.getNamedItem(name));
217: return (attr == null) ? "" : attr.getValue();
218:
219: } // getAttribute(String):String
220:
221: /**
222: * Look up a single Attribute by name. Returns the Attribute Node,
223: * so its complete child tree is available. This could be important in
224: * XML, where the string rendering may not be sufficient information.
225: * <p>
226: * If no matching attribute is available, returns null.
227: */
228: public Attr getAttributeNode(String name) {
229:
230: if (needsSyncData()) {
231: synchronizeData();
232: }
233: if (attributes == null) {
234: return null;
235: }
236: return (Attr) attributes.getNamedItem(name);
237:
238: } // getAttributeNode(String):Attr
239:
240: /**
241: * Returns a NodeList of all descendent nodes (children,
242: * grandchildren, and so on) which are Elements and which have the
243: * specified tag name.
244: * <p>
245: * Note: NodeList is a "live" view of the DOM. Its contents will
246: * change as the DOM changes, and alterations made to the NodeList
247: * will be reflected in the DOM.
248: *
249: * @param tagname The type of element to gather. To obtain a list of
250: * all elements no matter what their names, use the wild-card tag
251: * name "*".
252: *
253: * @see DeepNodeListImpl
254: */
255: public NodeList getElementsByTagName(String tagname) {
256: return new DeepNodeListImpl(this , tagname);
257: }
258:
259: /**
260: * Returns the name of the Element. Note that Element.nodeName() is
261: * defined to also return the tag name.
262: * <p>
263: * This is case-preserving in XML. HTML should uppercasify it on the
264: * way in.
265: */
266: public String getTagName() {
267: if (needsSyncData()) {
268: synchronizeData();
269: }
270: return name;
271: }
272:
273: /**
274: * In "normal form" (as read from a source file), there will never be two
275: * Text children in succession. But DOM users may create successive Text
276: * nodes in the course of manipulating the document. Normalize walks the
277: * sub-tree and merges adjacent Texts, as if the DOM had been written out
278: * and read back in again. This simplifies implementation of higher-level
279: * functions that may want to assume that the document is in standard form.
280: * <p>
281: * To normalize a Document, normalize its top-level Element child.
282: * <p>
283: * As of PR-DOM-Level-1-19980818, CDATA -- despite being a subclass of
284: * Text -- is considered "markup" and will _not_ be merged either with
285: * normal Text or with other CDATASections.
286: */
287: public void normalize() {
288: // No need to normalize if already normalized.
289: if (isNormalized()) {
290: return;
291: }
292: if (needsSyncChildren()) {
293: synchronizeChildren();
294: }
295: ChildNode kid, next;
296: for (kid = firstChild; kid != null; kid = next) {
297: next = kid.nextSibling;
298:
299: // If kid is a text node, we need to check for one of two
300: // conditions:
301: // 1) There is an adjacent text node
302: // 2) There is no adjacent text node, but kid is
303: // an empty text node.
304: if (kid.getNodeType() == Node.TEXT_NODE) {
305: // If an adjacent text node, merge it with kid
306: if (next != null
307: && next.getNodeType() == Node.TEXT_NODE) {
308: ((Text) kid).appendData(next.getNodeValue());
309: removeChild(next);
310: next = kid; // Don't advance; there might be another.
311: } else {
312: // If kid is empty, remove it
313: if (kid.getNodeValue().length() == 0)
314: removeChild(kid);
315: }
316: }
317:
318: // Otherwise it might be an Element, which is handled recursively
319: else if (kid.getNodeType() == Node.ELEMENT_NODE) {
320: kid.normalize();
321: }
322: }
323:
324: // We must also normalize all of the attributes
325: if (attributes != null) {
326: for (int i = 0; i < attributes.getLength(); ++i) {
327: Node attr = attributes.item(i);
328: attr.normalize();
329: }
330: }
331:
332: // changed() will have occurred when the removeChild() was done,
333: // so does not have to be reissued.
334:
335: isNormalized(true);
336: } // normalize()
337:
338: /**
339: * Remove the named attribute from this Element. If the removed
340: * Attribute has a default value, it is immediately replaced thereby.
341: * <P>
342: * The default logic is actually implemented in NamedNodeMapImpl.
343: * PR-DOM-Level-1-19980818 doesn't fully address the DTD, so some
344: * of this behavior is likely to change in future versions. ?????
345: * <P>
346: * Note that this call "succeeds" even if no attribute by this name
347: * existed -- unlike removeAttributeNode, which will throw a not-found
348: * exception in that case.
349: *
350: * @throws DOMException(NO_MODIFICATION_ALLOWED_ERR) if the node is
351: * readonly.
352: */
353: public void removeAttribute(String name) {
354:
355: if (ownerDocument.errorChecking && isReadOnly()) {
356: throw new DOMException(
357: DOMException.NO_MODIFICATION_ALLOWED_ERR,
358: "DOM001 Modification not allowed");
359: }
360:
361: if (needsSyncData()) {
362: synchronizeData();
363: }
364:
365: if (attributes == null) {
366: return;
367: }
368:
369: attributes.safeRemoveNamedItem(name);
370:
371: } // removeAttribute(String)
372:
373: /**
374: * Remove the specified attribute/value pair. If the removed
375: * Attribute has a default value, it is immediately replaced.
376: * <p>
377: * NOTE: Specifically removes THIS NODE -- not the node with this
378: * name, nor the node with these contents. If the specific Attribute
379: * object passed in is not stored in this Element, we throw a
380: * DOMException. If you really want to remove an attribute by name,
381: * use removeAttribute().
382: *
383: * @return the Attribute object that was removed.
384: * @throws DOMException(NOT_FOUND_ERR) if oldattr is not an attribute of
385: * this Element.
386: * @throws DOMException(NO_MODIFICATION_ALLOWED_ERR) if the node is
387: * readonly.
388: */
389: public Attr removeAttributeNode(Attr oldAttr) throws DOMException {
390:
391: if (ownerDocument.errorChecking && isReadOnly()) {
392: throw new DOMException(
393: DOMException.NO_MODIFICATION_ALLOWED_ERR,
394: "DOM001 Modification not allowed");
395: }
396:
397: if (needsSyncData()) {
398: synchronizeData();
399: }
400:
401: if (attributes == null) {
402: throw new DOMException(DOMException.NOT_FOUND_ERR,
403: "DOM008 Not found");
404: }
405: return (Attr) attributes.removeNamedItem(oldAttr.getName());
406:
407: } // removeAttributeNode(Attr):Attr
408:
409: /**
410: * Add a new name/value pair, or replace the value of the existing
411: * attribute having that name.
412: *
413: * Note: this method supports only the simplest kind of Attribute,
414: * one whose value is a string contained in a single Text node.
415: * If you want to assert a more complex value (which XML permits,
416: * though HTML doesn't), see setAttributeNode().
417: *
418: * The attribute is created with specified=true, meaning it's an
419: * explicit value rather than inherited from the DTD as a default.
420: * Again, setAttributeNode can be used to achieve other results.
421: *
422: * @throws DOMException(INVALID_NAME_ERR) if the name is not acceptable.
423: * (Attribute factory will do that test for us.)
424: *
425: * @throws DOMException(NO_MODIFICATION_ALLOWED_ERR) if the node is
426: * readonly.
427: */
428: public void setAttribute(String name, String value) {
429:
430: if (ownerDocument.errorChecking && isReadOnly()) {
431: throw new DOMException(
432: DOMException.NO_MODIFICATION_ALLOWED_ERR,
433: "DOM001 Modification not allowed");
434: }
435:
436: if (needsSyncData()) {
437: synchronizeData();
438: }
439:
440: Attr newAttr = getAttributeNode(name);
441: if (newAttr == null) {
442: newAttr = getOwnerDocument().createAttribute(name);
443:
444: if (attributes == null) {
445: attributes = new AttributeMap(this , null);
446: }
447:
448: newAttr.setNodeValue(value);
449: attributes.setNamedItem(newAttr);
450: } else {
451: newAttr.setNodeValue(value);
452: }
453:
454: } // setAttribute(String,String)
455:
456: /**
457: * Add a new attribute/value pair, or replace the value of the
458: * existing attribute with that name.
459: * <P>
460: * This method allows you to add an Attribute that has already been
461: * constructed, and hence avoids the limitations of the simple
462: * setAttribute() call. It can handle attribute values that have
463: * arbitrarily complex tree structure -- in particular, those which
464: * had entity references mixed into their text.
465: *
466: * @throws DOMException(INUSE_ATTRIBUTE_ERR) if the Attribute object
467: * has already been assigned to another Element.
468: */
469: public Attr setAttributeNode(Attr newAttr) throws DOMException {
470:
471: if (needsSyncData()) {
472: synchronizeData();
473: }
474:
475: if (ownerDocument.errorChecking) {
476: if (isReadOnly()) {
477: throw new DOMException(
478: DOMException.NO_MODIFICATION_ALLOWED_ERR,
479: "DOM001 Modification not allowed");
480: }
481:
482: if (newAttr.getOwnerDocument() != ownerDocument) {
483: throw new DOMException(DOMException.WRONG_DOCUMENT_ERR,
484: "DOM005 Wrong document");
485: }
486: }
487:
488: if (attributes == null) {
489: attributes = new AttributeMap(this , null);
490: }
491: // This will throw INUSE if necessary
492: return (Attr) attributes.setNamedItem(newAttr);
493:
494: } // setAttributeNode(Attr):Attr
495:
496: //
497: // DOM2: Namespace methods
498: //
499:
500: /**
501: * Introduced in DOM Level 2. <p>
502: *
503: * Retrieves an attribute value by local name and namespace URI.
504: *
505: * @param namespaceURI
506: * The namespace URI of the attribute to
507: * retrieve.
508: * @param localName The local name of the attribute to retrieve.
509: * @return String The Attr value as a string, or empty string
510: * if that attribute
511: * does not have a specified or default value.
512: * @since WD-DOM-Level-2-19990923
513: */
514: public String getAttributeNS(String namespaceURI, String localName) {
515:
516: if (needsSyncData()) {
517: synchronizeData();
518: }
519:
520: if (attributes == null) {
521: return "";
522: }
523:
524: Attr attr = (Attr) (attributes.getNamedItemNS(namespaceURI,
525: localName));
526: return (attr == null) ? "" : attr.getValue();
527:
528: } // getAttributeNS(String,String):String
529:
530: /**
531: * Introduced in DOM Level 2. <p>
532: *
533: * Adds a new attribute.
534: * If the given namespaceURI is null or an empty string and the
535: * qualifiedName has a prefix that is "xml", the new attribute is bound to
536: * the predefined namespace "http://www.w3.org/XML/1998/namespace"
537: * [Namespaces]. If an attribute with the same local name and namespace
538: * URI is already present on the element, its prefix is changed to be the
539: * prefix part of the qualifiedName, and its value is changed to be the
540: * value parameter. This value is a simple string, it is not parsed as it
541: * is being set. So any markup (such as syntax to be recognized as an
542: * entity reference) is treated as literal text, and needs to be
543: * appropriately escaped by the implementation when it is written out. In
544: * order to assign an attribute value that contains entity references, the
545: * user must create an Attr node plus any Text and EntityReference nodes,
546: * build the appropriate subtree, and use setAttributeNodeNS or
547: * setAttributeNode to assign it as the value of an attribute.
548: *
549: * @param namespaceURI The namespace URI of the attribute to create
550: * or alter.
551: * @param localName The local name of the attribute to create or
552: * alter.
553: * @param value The value to set in string form.
554: * @throws INVALID_CHARACTER_ERR: Raised if the specified
555: * name contains an invalid character.
556: *
557: * @throws NO_MODIFICATION_ALLOWED_ERR: Raised if this
558: * node is readonly.
559: *
560: * @throws NAMESPACE_ERR: Raised if the qualifiedName
561: * has a prefix that is "xml" and the namespaceURI
562: * is neither null nor an empty string nor
563: * "http://www.w3.org/XML/1998/namespace", or if
564: * the qualifiedName has a prefix that is "xmlns"
565: * but the namespaceURI is neither null nor an
566: * empty string, or if if the qualifiedName has a
567: * prefix different from "xml" and "xmlns" and the
568: * namespaceURI is null or an empty string.
569: * @since WD-DOM-Level-2-19990923
570: */
571: public void setAttributeNS(String namespaceURI, String localName,
572: String value) {
573:
574: if (ownerDocument.errorChecking && isReadOnly()) {
575: throw new DOMException(
576: DOMException.NO_MODIFICATION_ALLOWED_ERR,
577: "DOM001 Modification not allowed");
578: }
579:
580: if (needsSyncData()) {
581: synchronizeData();
582: }
583:
584: Attr newAttr = getAttributeNodeNS(namespaceURI, localName);
585: if (newAttr == null) {
586: newAttr = getOwnerDocument().createAttributeNS(
587: namespaceURI, localName);
588:
589: if (attributes == null) {
590: attributes = new AttributeMap(this , null);
591: }
592: newAttr.setNodeValue(value);
593: attributes.setNamedItemNS(newAttr);
594: } else {
595: newAttr.setNodeValue(value);
596: }
597:
598: } // setAttributeNS(String,String,String)
599:
600: /**
601: * Introduced in DOM Level 2. <p>
602: *
603: * Removes an attribute by local name and namespace URI. If the removed
604: * attribute has a default value it is immediately replaced.
605: * The replacing attribute has the same namespace URI and local name,
606: * as well as the original prefix.<p>
607: *
608: * @param namespaceURI The namespace URI of the attribute to remove.
609: *
610: * @param localName The local name of the attribute to remove.
611: * @throws NO_MODIFICATION_ALLOWED_ERR: Raised if this
612: * node is readonly.
613: * @since WD-DOM-Level-2-19990923
614: */
615: public void removeAttributeNS(String namespaceURI, String localName) {
616:
617: if (ownerDocument.errorChecking && isReadOnly()) {
618: throw new DOMException(
619: DOMException.NO_MODIFICATION_ALLOWED_ERR,
620: "DOM001 Modification not allowed");
621: }
622:
623: if (needsSyncData()) {
624: synchronizeData();
625: }
626:
627: if (attributes == null) {
628: return;
629: }
630:
631: attributes.safeRemoveNamedItemNS(namespaceURI, localName);
632:
633: } // removeAttributeNS(String,String)
634:
635: /**
636: * Retrieves an Attr node by local name and namespace URI.
637: *
638: * @param namespaceURI The namespace URI of the attribute to
639: * retrieve.
640: * @param localName The local name of the attribute to retrieve.
641: * @return Attr The Attr node with the specified attribute
642: * local name and namespace
643: * URI or null if there is no such attribute.
644: * @since WD-DOM-Level-2-19990923
645: */
646: public Attr getAttributeNodeNS(String namespaceURI, String localName) {
647:
648: if (needsSyncData()) {
649: synchronizeData();
650: }
651: if (attributes == null) {
652: return null;
653: }
654: return (Attr) attributes
655: .getNamedItemNS(namespaceURI, localName);
656:
657: } // getAttributeNodeNS(String,String):Attr
658:
659: /**
660: * Introduced in DOM Level 2. <p>
661: *
662: * Adds a new attribute. If an attribute with that local name and
663: * namespace URI is already present in the element, it is replaced
664: * by the new one.
665: *
666: * @param Attr The Attr node to add to the attribute list. When
667: * the Node has no namespaceURI, this method behaves
668: * like setAttributeNode.
669: * @return Attr If the newAttr attribute replaces an existing attribute
670: * with the same local name and namespace URI, the *
671: * previously existing Attr node is returned, otherwise
672: * null is returned.
673: * @throws WRONG_DOCUMENT_ERR: Raised if newAttr
674: * was created from a different document than the one that
675: * created the element.
676: *
677: * @throws NO_MODIFICATION_ALLOWED_ERR: Raised if
678: * this node is readonly.
679: *
680: * @throws INUSE_ATTRIBUTE_ERR: Raised if newAttr is
681: * already an attribute of another Element object. The
682: * DOM user must explicitly clone Attr nodes to re-use
683: * them in other elements.
684: * @since WD-DOM-Level-2-19990923
685: */
686: public Attr setAttributeNodeNS(Attr newAttr) throws DOMException {
687:
688: if (needsSyncData()) {
689: synchronizeData();
690: }
691: if (ownerDocument.errorChecking) {
692: if (isReadOnly()) {
693: throw new DOMException(
694: DOMException.NO_MODIFICATION_ALLOWED_ERR,
695: "DOM001 Modification not allowed");
696: }
697: if (newAttr.getOwnerDocument() != ownerDocument) {
698: throw new DOMException(DOMException.WRONG_DOCUMENT_ERR,
699: "DOM005 Wrong document");
700: }
701: }
702:
703: if (attributes == null) {
704: attributes = new AttributeMap(this , null);
705: }
706: // This will throw INUSE if necessary
707: return (Attr) attributes.setNamedItemNS(newAttr);
708:
709: } // setAttributeNodeNS(Attr):Attr
710:
711: /**
712: * Introduced in DOM Level 2.
713: */
714: public boolean hasAttributes() {
715: if (needsSyncData()) {
716: synchronizeData();
717: }
718: return (attributes != null && attributes.getLength() != 0);
719: }
720:
721: /**
722: * Introduced in DOM Level 2.
723: */
724: public boolean hasAttribute(String name) {
725: return getAttributeNode(name) != null;
726: }
727:
728: /**
729: * Introduced in DOM Level 2.
730: */
731: public boolean hasAttributeNS(String namespaceURI, String localName) {
732: return getAttributeNodeNS(namespaceURI, localName) != null;
733: }
734:
735: /**
736: * Introduced in DOM Level 2. <p>
737: *
738: * Returns a NodeList of all the Elements with a given local name and
739: * namespace URI in the order in which they would be encountered in a
740: * preorder traversal of the Document tree, starting from this node.
741: *
742: * @param namespaceURI The namespace URI of the elements to match
743: * on. The special value "*" matches all
744: * namespaces. When it is null or an empty
745: * string, this method behaves like
746: * getElementsByTagName.
747: * @param localName The local name of the elements to match on.
748: * The special value "*" matches all local names.
749: * @return NodeList A new NodeList object containing all the matched
750: * Elements.
751: * @since WD-DOM-Level-2-19990923
752: */
753: public NodeList getElementsByTagNameNS(String namespaceURI,
754: String localName) {
755: return new DeepNodeListImpl(this , namespaceURI, localName);
756: }
757:
758: //
759: // Public methods
760: //
761:
762: /**
763: * NON-DOM: Subclassed to flip the attributes' readonly switch as well.
764: * @see NodeImpl#setReadOnly
765: */
766: public void setReadOnly(boolean readOnly, boolean deep) {
767: super .setReadOnly(readOnly, deep);
768: if (attributes != null) {
769: attributes.setReadOnly(readOnly, true);
770: }
771: }
772:
773: //
774: // Protected methods
775: //
776:
777: /** Synchronizes the data (name and value) for fast nodes. */
778: protected void synchronizeData() {
779:
780: // no need to sync in the future
781: needsSyncData(false);
782:
783: // we don't want to generate any event for this so turn them off
784: boolean orig = ownerDocument.getMutationEvents();
785: ownerDocument.setMutationEvents(false);
786:
787: // attributes
788: setupDefaultAttributes();
789:
790: // set mutation events flag back to its original value
791: ownerDocument.setMutationEvents(orig);
792:
793: } // synchronizeData()
794:
795: /** Setup the default attributes. */
796: protected void setupDefaultAttributes() {
797: NamedNodeMapImpl defaults = getDefaultAttributes();
798: if (defaults != null) {
799: attributes = new AttributeMap(this , defaults);
800: }
801: }
802:
803: /** Reconcile default attributes. */
804: protected void reconcileDefaultAttributes() {
805: NamedNodeMapImpl defaults = getDefaultAttributes();
806: if (defaults != null) {
807: attributes.reconcileDefaults(defaults);
808: }
809: }
810:
811: /** Get the default attributes. */
812: protected NamedNodeMapImpl getDefaultAttributes() {
813:
814: DocumentTypeImpl doctype = (DocumentTypeImpl) ownerDocument
815: .getDoctype();
816: if (doctype == null) {
817: return null;
818: }
819: ElementDefinitionImpl eldef = (ElementDefinitionImpl) doctype
820: .getElements().getNamedItem(getNodeName());
821: if (eldef == null) {
822: return null;
823: }
824: return (NamedNodeMapImpl) eldef.getAttributes();
825:
826: } // getDefaultAttributes()
827:
828: } // class ElementImpl
|