View Javadoc
1   /*
2    * Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved.
3    * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4    *
5    * This code is free software; you can redistribute it and/or modify it
6    * under the terms of the GNU General Public License version 2 only, as
7    * published by the Free Software Foundation.  Oracle designates this
8    * particular file as subject to the "Classpath" exception as provided
9    * by Oracle in the LICENSE file that accompanied this code.
10   *
11   * This code is distributed in the hope that it will be useful, but WITHOUT
12   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13   * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14   * version 2 for more details (a copy is included in the LICENSE file that
15   * accompanied this code).
16   *
17   * You should have received a copy of the GNU General Public License version
18   * 2 along with this work; if not, write to the Free Software Foundation,
19   * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20   *
21   * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22   * or visit www.oracle.com if you need additional information or have any
23   * questions.
24   */
25  
26  package javax.imageio.metadata;
27  
28  import java.util.ArrayList;
29  import java.util.Iterator;
30  import java.util.List;
31  
32  import org.w3c.dom.Attr;
33  import org.w3c.dom.Document;
34  import org.w3c.dom.Element;
35  import org.w3c.dom.DOMException;
36  import org.w3c.dom.NamedNodeMap;
37  import org.w3c.dom.Node;
38  import org.w3c.dom.NodeList;
39  import org.w3c.dom.TypeInfo;
40  import org.w3c.dom.UserDataHandler;
41  
42  
43  class IIODOMException extends DOMException {
44  
45      public IIODOMException(short code, String message) {
46          super(code, message);
47      }
48  }
49  
50  class IIONamedNodeMap implements NamedNodeMap {
51  
52      List nodes;
53  
54      public IIONamedNodeMap(List nodes) {
55          this.nodes = nodes;
56      }
57  
58      public int getLength() {
59          return nodes.size();
60      }
61  
62      public Node getNamedItem(String name) {
63          Iterator iter = nodes.iterator();
64          while (iter.hasNext()) {
65              Node node = (Node)iter.next();
66              if (name.equals(node.getNodeName())) {
67                  return node;
68              }
69          }
70  
71          return null;
72      }
73  
74      public Node item(int index) {
75          Node node = (Node)nodes.get(index);
76          return node;
77      }
78  
79      public Node removeNamedItem(java.lang.String name) {
80          throw new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR,
81                                 "This NamedNodeMap is read-only!");
82      }
83  
84      public Node setNamedItem(Node arg) {
85          throw new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR,
86                                 "This NamedNodeMap is read-only!");
87      }
88  
89      /**
90       * Equivalent to <code>getNamedItem(localName)</code>.
91       */
92      public Node getNamedItemNS(String namespaceURI, String localName) {
93          return getNamedItem(localName);
94      }
95  
96      /**
97       * Equivalent to <code>setNamedItem(arg)</code>.
98       */
99      public Node setNamedItemNS(Node arg) {
100         return setNamedItem(arg);
101     }
102 
103     /**
104      * Equivalent to <code>removeNamedItem(localName)</code>.
105      */
106     public Node removeNamedItemNS(String namespaceURI, String localName) {
107         return removeNamedItem(localName);
108     }
109 }
110 
111 class IIONodeList implements NodeList {
112 
113     List nodes;
114 
115     public IIONodeList(List nodes) {
116         this.nodes = nodes;
117     }
118 
119     public int getLength() {
120         return nodes.size();
121     }
122 
123     public Node item(int index) {
124         if (index < 0 || index > nodes.size()) {
125             return null;
126         }
127         return (Node)nodes.get(index);
128     }
129 }
130 
131 class IIOAttr extends IIOMetadataNode implements Attr {
132 
133     Element owner;
134     String name;
135     String value;
136 
137     public IIOAttr(Element owner, String name, String value) {
138         this.owner = owner;
139         this.name = name;
140         this.value = value;
141     }
142 
143     public String getName() {
144         return name;
145     }
146 
147     public String getNodeName() {
148         return name;
149     }
150 
151     public short getNodeType() {
152         return ATTRIBUTE_NODE;
153     }
154 
155     public boolean getSpecified() {
156         return true;
157     }
158 
159     public String getValue() {
160         return value;
161     }
162 
163     public String getNodeValue() {
164         return value;
165     }
166 
167     public void setValue(String value) {
168         this.value = value;
169     }
170 
171     public void setNodeValue(String value) {
172         this.value = value;
173     }
174 
175     public Element getOwnerElement() {
176         return owner;
177     }
178 
179     public void setOwnerElement(Element owner) {
180         this.owner = owner;
181     }
182 
183     /** This method is new in the DOM L3 for Attr interface.
184      * Could throw DOMException here, but its probably OK
185      * to always return false. One reason for this, is we have no good
186      * way to document this exception, since this class, IIOAttr,
187      * is not a public class. The rest of the methods that throw
188      * DOMException are publically documented as such on IIOMetadataNode.
189      * @return false
190      */
191     public boolean isId() {
192         return false;
193     }
194 
195 
196 }
197 
198 /**
199  * A class representing a node in a meta-data tree, which implements
200  * the <a
201  * href="../../../../api/org/w3c/dom/Element.html">
202  * <code>org.w3c.dom.Element</code></a> interface and additionally allows
203  * for the storage of non-textual objects via the
204  * <code>getUserObject</code> and <code>setUserObject</code> methods.
205  *
206  * <p> This class is not intended to be used for general XML
207  * processing. In particular, <code>Element</code> nodes created
208  * within the Image I/O API are not compatible with those created by
209  * Sun's standard implementation of the <code>org.w3.dom</code> API.
210  * In particular, the implementation is tuned for simple uses and may
211  * not perform well for intensive processing.
212  *
213  * <p> Namespaces are ignored in this implementation.  The terms "tag
214  * name" and "node name" are always considered to be synonymous.
215  *
216  * <em>Note:</em>
217  * The DOM Level 3 specification added a number of new methods to the
218  * {@code Node}, {@code Element} and {@code Attr} interfaces that are not
219  * of value to the {@code IIOMetadataNode} implementation or specification.
220  *
221  * Calling such methods on an {@code IIOMetadataNode}, or an {@code Attr}
222  * instance returned from an {@code IIOMetadataNode} will result in a
223  * {@code DOMException} being thrown.
224  *
225  * @see IIOMetadata#getAsTree
226  * @see IIOMetadata#setFromTree
227  * @see IIOMetadata#mergeTree
228  *
229  */
230 public class IIOMetadataNode implements Element, NodeList {
231 
232     /**
233      * The name of the node as a <code>String</code>.
234      */
235     private String nodeName = null;
236 
237     /**
238      * The value of the node as a <code>String</code>.  The Image I/O
239      * API typically does not make use of the node value.
240      */
241     private String nodeValue = null;
242 
243     /**
244      * The <code>Object</code> value associated with this node.
245      */
246     private Object userObject = null;
247 
248     /**
249      * The parent node of this node, or <code>null</code> if this node
250      * forms the root of its own tree.
251      */
252     private IIOMetadataNode parent = null;
253 
254     /**
255      * The number of child nodes.
256      */
257     private int numChildren = 0;
258 
259     /**
260      * The first (leftmost) child node of this node, or
261      * <code>null</code> if this node is a leaf node.
262      */
263     private IIOMetadataNode firstChild = null;
264 
265     /**
266      * The last (rightmost) child node of this node, or
267      * <code>null</code> if this node is a leaf node.
268      */
269     private IIOMetadataNode lastChild = null;
270 
271     /**
272      * The next (right) sibling node of this node, or
273      * <code>null</code> if this node is its parent's last child node.
274      */
275     private IIOMetadataNode nextSibling = null;
276 
277     /**
278      * The previous (left) sibling node of this node, or
279      * <code>null</code> if this node is its parent's first child node.
280      */
281     private IIOMetadataNode previousSibling = null;
282 
283     /**
284      * A <code>List</code> of <code>IIOAttr</code> nodes representing
285      * attributes.
286      */
287     private List attributes = new ArrayList();
288 
289     /**
290      * Constructs an empty <code>IIOMetadataNode</code>.
291      */
292     public IIOMetadataNode() {}
293 
294     /**
295      * Constructs an <code>IIOMetadataNode</code> with a given node
296      * name.
297      *
298      * @param nodeName the name of the node, as a <code>String</code>.
299      */
300     public IIOMetadataNode(String nodeName) {
301         this.nodeName = nodeName;
302     }
303 
304     /**
305      * Check that the node is either <code>null</code> or an
306      * <code>IIOMetadataNode</code>.
307      */
308     private void checkNode(Node node) throws DOMException {
309         if (node == null) {
310             return;
311         }
312         if (!(node instanceof IIOMetadataNode)) {
313             throw new IIODOMException(DOMException.WRONG_DOCUMENT_ERR,
314                                       "Node not an IIOMetadataNode!");
315         }
316     }
317 
318     // Methods from Node
319 
320     /**
321      * Returns the node name associated with this node.
322      *
323      * @return the node name, as a <code>String</code>.
324      */
325     public String getNodeName() {
326         return nodeName;
327     }
328 
329     /**
330      * Returns the value associated with this node.
331      *
332      * @return the node value, as a <code>String</code>.
333      */
334     public String getNodeValue(){
335         return nodeValue;
336     }
337 
338     /**
339      * Sets the <code>String</code> value associated with this node.
340      */
341     public void setNodeValue(String nodeValue) {
342         this.nodeValue = nodeValue;
343     }
344 
345     /**
346      * Returns the node type, which is always
347      * <code>ELEMENT_NODE</code>.
348      *
349      * @return the <code>short</code> value <code>ELEMENT_NODE</code>.
350      */
351     public short getNodeType() {
352         return ELEMENT_NODE;
353     }
354 
355     /**
356      * Returns the parent of this node.  A <code>null</code> value
357      * indicates that the node is the root of its own tree.  To add a
358      * node to an existing tree, use one of the
359      * <code>insertBefore</code>, <code>replaceChild</code>, or
360      * <code>appendChild</code> methods.
361      *
362      * @return the parent, as a <code>Node</code>.
363      *
364      * @see #insertBefore
365      * @see #replaceChild
366      * @see #appendChild
367      */
368     public Node getParentNode() {
369         return parent;
370     }
371 
372     /**
373      * Returns a <code>NodeList</code> that contains all children of this node.
374      * If there are no children, this is a <code>NodeList</code> containing
375      * no nodes.
376      *
377      * @return the children as a <code>NodeList</code>
378      */
379     public NodeList getChildNodes() {
380         return this;
381     }
382 
383     /**
384      * Returns the first child of this node, or <code>null</code> if
385      * the node has no children.
386      *
387      * @return the first child, as a <code>Node</code>, or
388      * <code>null</code>
389      */
390     public Node getFirstChild() {
391         return firstChild;
392     }
393 
394     /**
395      * Returns the last child of this node, or <code>null</code> if
396      * the node has no children.
397      *
398      * @return the last child, as a <code>Node</code>, or
399      * <code>null</code>.
400      */
401     public Node getLastChild() {
402         return lastChild;
403     }
404 
405     /**
406      * Returns the previous sibling of this node, or <code>null</code>
407      * if this node has no previous sibling.
408      *
409      * @return the previous sibling, as a <code>Node</code>, or
410      * <code>null</code>.
411      */
412     public Node getPreviousSibling() {
413         return previousSibling;
414     }
415 
416     /**
417      * Returns the next sibling of this node, or <code>null</code> if
418      * the node has no next sibling.
419      *
420      * @return the next sibling, as a <code>Node</code>, or
421      * <code>null</code>.
422      */
423     public Node getNextSibling() {
424         return nextSibling;
425     }
426 
427     /**
428      * Returns a <code>NamedNodeMap</code> containing the attributes of
429      * this node.
430      *
431      * @return a <code>NamedNodeMap</code> containing the attributes of
432      * this node.
433      */
434     public NamedNodeMap getAttributes() {
435         return new IIONamedNodeMap(attributes);
436     }
437 
438     /**
439      * Returns <code>null</code>, since <code>IIOMetadataNode</code>s
440      * do not belong to any <code>Document</code>.
441      *
442      * @return <code>null</code>.
443      */
444     public Document getOwnerDocument() {
445         return null;
446     }
447 
448     /**
449      * Inserts the node <code>newChild</code> before the existing
450      * child node <code>refChild</code>. If <code>refChild</code> is
451      * <code>null</code>, insert <code>newChild</code> at the end of
452      * the list of children.
453      *
454      * @param newChild the <code>Node</code> to insert.
455      * @param refChild the reference <code>Node</code>.
456      *
457      * @return the node being inserted.
458      *
459      * @exception IllegalArgumentException if <code>newChild</code> is
460      * <code>null</code>.
461      */
462     public Node insertBefore(Node newChild,
463                              Node refChild) {
464         if (newChild == null) {
465             throw new IllegalArgumentException("newChild == null!");
466         }
467 
468         checkNode(newChild);
469         checkNode(refChild);
470 
471         IIOMetadataNode newChildNode = (IIOMetadataNode)newChild;
472         IIOMetadataNode refChildNode = (IIOMetadataNode)refChild;
473 
474         // Siblings, can be null.
475         IIOMetadataNode previous = null;
476         IIOMetadataNode next = null;
477 
478         if (refChild == null) {
479             previous = this.lastChild;
480             next = null;
481             this.lastChild = newChildNode;
482         } else {
483             previous = refChildNode.previousSibling;
484             next = refChildNode;
485         }
486 
487         if (previous != null) {
488             previous.nextSibling = newChildNode;
489         }
490         if (next != null) {
491             next.previousSibling = newChildNode;
492         }
493 
494         newChildNode.parent = this;
495         newChildNode.previousSibling = previous;
496         newChildNode.nextSibling = next;
497 
498         // N.B.: O.K. if refChild == null
499         if (this.firstChild == refChildNode) {
500             this.firstChild = newChildNode;
501         }
502 
503         ++numChildren;
504         return newChildNode;
505     }
506 
507     /**
508      * Replaces the child node <code>oldChild</code> with
509      * <code>newChild</code> in the list of children, and returns the
510      * <code>oldChild</code> node.
511      *
512      * @param newChild the <code>Node</code> to insert.
513      * @param oldChild the <code>Node</code> to be replaced.
514      *
515      * @return the node replaced.
516      *
517      * @exception IllegalArgumentException if <code>newChild</code> is
518      * <code>null</code>.
519      */
520     public Node replaceChild(Node newChild,
521                              Node oldChild) {
522         if (newChild == null) {
523             throw new IllegalArgumentException("newChild == null!");
524         }
525 
526         checkNode(newChild);
527         checkNode(oldChild);
528 
529         IIOMetadataNode newChildNode = (IIOMetadataNode)newChild;
530         IIOMetadataNode oldChildNode = (IIOMetadataNode)oldChild;
531 
532         IIOMetadataNode previous = oldChildNode.previousSibling;
533         IIOMetadataNode next = oldChildNode.nextSibling;
534 
535         if (previous != null) {
536             previous.nextSibling = newChildNode;
537         }
538         if (next != null) {
539             next.previousSibling = newChildNode;
540         }
541 
542         newChildNode.parent = this;
543         newChildNode.previousSibling = previous;
544         newChildNode.nextSibling = next;
545 
546         if (firstChild == oldChildNode) {
547             firstChild = newChildNode;
548         }
549         if (lastChild == oldChildNode) {
550             lastChild = newChildNode;
551         }
552 
553         oldChildNode.parent = null;
554         oldChildNode.previousSibling = null;
555         oldChildNode.nextSibling = null;
556 
557         return oldChildNode;
558     }
559 
560     /**
561      * Removes the child node indicated by <code>oldChild</code> from
562      * the list of children, and returns it.
563      *
564      * @param oldChild the <code>Node</code> to be removed.
565      *
566      * @return the node removed.
567      *
568      * @exception IllegalArgumentException if <code>oldChild</code> is
569      * <code>null</code>.
570      */
571     public Node removeChild(Node oldChild) {
572         if (oldChild == null) {
573             throw new IllegalArgumentException("oldChild == null!");
574         }
575         checkNode(oldChild);
576 
577         IIOMetadataNode oldChildNode = (IIOMetadataNode)oldChild;
578 
579         IIOMetadataNode previous = oldChildNode.previousSibling;
580         IIOMetadataNode next = oldChildNode.nextSibling;
581 
582         if (previous != null) {
583             previous.nextSibling = next;
584         }
585         if (next != null) {
586             next.previousSibling = previous;
587         }
588 
589         if (this.firstChild == oldChildNode) {
590             this.firstChild = next;
591         }
592         if (this.lastChild == oldChildNode) {
593             this.lastChild = previous;
594         }
595 
596         oldChildNode.parent = null;
597         oldChildNode.previousSibling = null;
598         oldChildNode.nextSibling = null;
599 
600         --numChildren;
601         return oldChildNode;
602     }
603 
604     /**
605      * Adds the node <code>newChild</code> to the end of the list of
606      * children of this node.
607      *
608      * @param newChild the <code>Node</code> to insert.
609      *
610      * @return the node added.
611      *
612      * @exception IllegalArgumentException if <code>newChild</code> is
613      * <code>null</code>.
614      */
615     public Node appendChild(Node newChild) {
616         if (newChild == null) {
617             throw new IllegalArgumentException("newChild == null!");
618         }
619         checkNode(newChild);
620 
621         // insertBefore will increment numChildren
622         return insertBefore(newChild, null);
623     }
624 
625     /**
626      * Returns <code>true</code> if this node has child nodes.
627      *
628      * @return <code>true</code> if this node has children.
629      */
630     public boolean hasChildNodes() {
631         return numChildren > 0;
632     }
633 
634     /**
635      * Returns a duplicate of this node.  The duplicate node has no
636      * parent (<code>getParentNode</code> returns <code>null</code>).
637      * If a shallow clone is being performed (<code>deep</code> is
638      * <code>false</code>), the new node will not have any children or
639      * siblings.  If a deep clone is being performed, the new node
640      * will form the root of a complete cloned subtree.
641      *
642      * @param deep if <code>true</code>, recursively clone the subtree
643      * under the specified node; if <code>false</code>, clone only the
644      * node itself.
645      *
646      * @return the duplicate node.
647      */
648     public Node cloneNode(boolean deep) {
649         IIOMetadataNode newNode = new IIOMetadataNode(this.nodeName);
650         newNode.setUserObject(getUserObject());
651         // Attributes
652 
653         if (deep) {
654             for (IIOMetadataNode child = firstChild;
655                  child != null;
656                  child = child.nextSibling) {
657                 newNode.appendChild(child.cloneNode(true));
658             }
659         }
660 
661         return newNode;
662     }
663 
664     /**
665      * Does nothing, since <code>IIOMetadataNode</code>s do not
666      * contain <code>Text</code> children.
667      */
668     public void normalize() {
669     }
670 
671     /**
672      * Returns <code>false</code> since DOM features are not
673      * supported.
674      *
675      * @return <code>false</code>.
676      *
677      * @param feature a <code>String</code>, which is ignored.
678      * @param version a <code>String</code>, which is ignored.
679      */
680     public boolean isSupported(String feature, String version) {
681         return false;
682     }
683 
684     /**
685      * Returns <code>null</code>, since namespaces are not supported.
686      */
687     public String getNamespaceURI() throws DOMException {
688         return null;
689     }
690 
691     /**
692      * Returns <code>null</code>, since namespaces are not supported.
693      *
694      * @return <code>null</code>.
695      *
696      * @see #setPrefix
697      */
698     public String getPrefix() {
699         return null;
700     }
701 
702     /**
703      * Does nothing, since namespaces are not supported.
704      *
705      * @param prefix a <code>String</code>, which is ignored.
706      *
707      * @see #getPrefix
708      */
709     public void setPrefix(String prefix) {
710     }
711 
712     /**
713      * Equivalent to <code>getNodeName</code>.
714      *
715      * @return the node name, as a <code>String</code>.
716      */
717     public String getLocalName() {
718         return nodeName;
719     }
720 
721     // Methods from Element
722 
723 
724     /**
725      * Equivalent to <code>getNodeName</code>.
726      *
727      * @return the node name, as a <code>String</code>
728      */
729     public String getTagName() {
730         return nodeName;
731     }
732 
733     /**
734      * Retrieves an attribute value by name.
735      * @param name The name of the attribute to retrieve.
736      * @return The <code>Attr</code> value as a string, or the empty string
737      * if that attribute does not have a specified or default value.
738      */
739     public String getAttribute(String name) {
740         Attr attr = getAttributeNode(name);
741         if (attr == null) {
742             return "";
743         }
744         return attr.getValue();
745     }
746 
747     /**
748      * Equivalent to <code>getAttribute(localName)</code>.
749      *
750      * @see #setAttributeNS
751      */
752     public String getAttributeNS(String namespaceURI, String localName) {
753         return getAttribute(localName);
754     }
755 
756     public void setAttribute(String name, String value) {
757         // Name must be valid unicode chars
758         boolean valid = true;
759         char[] chs = name.toCharArray();
760         for (int i=0;i<chs.length;i++) {
761             if (chs[i] >= 0xfffe) {
762                 valid = false;
763                 break;
764             }
765         }
766         if (!valid) {
767             throw new IIODOMException(DOMException.INVALID_CHARACTER_ERR,
768                                       "Attribute name is illegal!");
769         }
770         removeAttribute(name, false);
771         attributes.add(new IIOAttr(this, name, value));
772     }
773 
774     /**
775      * Equivalent to <code>setAttribute(qualifiedName, value)</code>.
776      *
777      * @see #getAttributeNS
778      */
779     public void setAttributeNS(String namespaceURI,
780                                String qualifiedName, String value) {
781         setAttribute(qualifiedName, value);
782     }
783 
784     public void removeAttribute(String name) {
785         removeAttribute(name, true);
786     }
787 
788     private void removeAttribute(String name, boolean checkPresent) {
789         int numAttributes = attributes.size();
790         for (int i = 0; i < numAttributes; i++) {
791             IIOAttr attr = (IIOAttr)attributes.get(i);
792             if (name.equals(attr.getName())) {
793                 attr.setOwnerElement(null);
794                 attributes.remove(i);
795                 return;
796             }
797         }
798 
799         // If we get here, the attribute doesn't exist
800         if (checkPresent) {
801             throw new IIODOMException(DOMException.NOT_FOUND_ERR,
802                                       "No such attribute!");
803         }
804     }
805 
806     /**
807      * Equivalent to <code>removeAttribute(localName)</code>.
808      */
809     public void removeAttributeNS(String namespaceURI,
810                                   String localName) {
811         removeAttribute(localName);
812     }
813 
814     public Attr getAttributeNode(String name) {
815         Node node = getAttributes().getNamedItem(name);
816         return (Attr)node;
817     }
818 
819     /**
820      * Equivalent to <code>getAttributeNode(localName)</code>.
821      *
822      * @see #setAttributeNodeNS
823      */
824    public Attr getAttributeNodeNS(String namespaceURI,
825                                    String localName) {
826         return getAttributeNode(localName);
827     }
828 
829     public Attr setAttributeNode(Attr newAttr) throws DOMException {
830         Element owner = newAttr.getOwnerElement();
831         if (owner != null) {
832             if (owner == this) {
833                 return null;
834             } else {
835                 throw new DOMException(DOMException.INUSE_ATTRIBUTE_ERR,
836                                        "Attribute is already in use");
837             }
838         }
839 
840         IIOAttr attr;
841         if (newAttr instanceof IIOAttr) {
842             attr = (IIOAttr)newAttr;
843             attr.setOwnerElement(this);
844         } else {
845             attr = new IIOAttr(this,
846                                newAttr.getName(),
847                                newAttr.getValue());
848         }
849 
850         Attr oldAttr = getAttributeNode(attr.getName());
851         if (oldAttr != null) {
852             removeAttributeNode(oldAttr);
853         }
854 
855         attributes.add(attr);
856 
857         return oldAttr;
858     }
859 
860     /**
861      * Equivalent to <code>setAttributeNode(newAttr)</code>.
862      *
863      * @see #getAttributeNodeNS
864      */
865     public Attr setAttributeNodeNS(Attr newAttr) {
866         return setAttributeNode(newAttr);
867     }
868 
869     public Attr removeAttributeNode(Attr oldAttr) {
870         removeAttribute(oldAttr.getName());
871         return oldAttr;
872     }
873 
874     public NodeList getElementsByTagName(String name) {
875         List l = new ArrayList();
876         getElementsByTagName(name, l);
877         return new IIONodeList(l);
878     }
879 
880     private void getElementsByTagName(String name, List l) {
881         if (nodeName.equals(name)) {
882             l.add(this);
883         }
884 
885         Node child = getFirstChild();
886         while (child != null) {
887             ((IIOMetadataNode)child).getElementsByTagName(name, l);
888             child = child.getNextSibling();
889         }
890     }
891 
892     /**
893      * Equivalent to <code>getElementsByTagName(localName)</code>.
894      */
895     public NodeList getElementsByTagNameNS(String namespaceURI,
896                                            String localName) {
897         return getElementsByTagName(localName);
898     }
899 
900     public boolean hasAttributes() {
901         return attributes.size() > 0;
902     }
903 
904     public boolean hasAttribute(String name) {
905         return getAttributeNode(name) != null;
906     }
907 
908     /**
909      * Equivalent to <code>hasAttribute(localName)</code>.
910      */
911     public boolean hasAttributeNS(String namespaceURI,
912                                   String localName) {
913         return hasAttribute(localName);
914     }
915 
916     // Methods from NodeList
917 
918     public int getLength() {
919         return numChildren;
920     }
921 
922     public Node item(int index) {
923         if (index < 0) {
924             return null;
925         }
926 
927         Node child = getFirstChild();
928         while (child != null && index-- > 0) {
929             child = child.getNextSibling();
930         }
931         return child;
932     }
933 
934     /**
935      * Returns the <code>Object</code> value associated with this node.
936      *
937      * @return the user <code>Object</code>.
938      *
939      * @see #setUserObject
940      */
941     public Object getUserObject() {
942         return userObject;
943     }
944 
945     /**
946      * Sets the value associated with this node.
947      *
948      * @param userObject the user <code>Object</code>.
949      *
950      * @see #getUserObject
951      */
952     public void setUserObject(Object userObject) {
953         this.userObject = userObject;
954     }
955 
956     // Start of dummy methods for DOM L3.
957 
958     /**
959      * This DOM Level 3 method is not supported for {@code IIOMetadataNode}
960      * and will throw a {@code DOMException}.
961      * @throws DOMException - always.
962      */
963     public void setIdAttribute(String name,
964                                boolean isId)
965                                throws DOMException {
966         throw new DOMException(DOMException.NOT_SUPPORTED_ERR,
967                                "Method not supported");
968     }
969 
970     /**
971      * This DOM Level 3 method is not supported for {@code IIOMetadataNode}
972      * and will throw a {@code DOMException}.
973      * @throws DOMException - always.
974      */
975     public void setIdAttributeNS(String namespaceURI,
976                                  String localName,
977                                  boolean isId)
978                                  throws DOMException {
979         throw new DOMException(DOMException.NOT_SUPPORTED_ERR,
980                                "Method not supported");
981     }
982 
983     /**
984      * This DOM Level 3 method is not supported for {@code IIOMetadataNode}
985      * and will throw a {@code DOMException}.
986      * @throws DOMException - always.
987      */
988     public void setIdAttributeNode(Attr idAttr,
989                                    boolean isId)
990                                    throws DOMException {
991         throw new DOMException(DOMException.NOT_SUPPORTED_ERR,
992                                "Method not supported");
993     }
994 
995     /**
996      * This DOM Level 3 method is not supported for {@code IIOMetadataNode}
997      * and will throw a {@code DOMException}.
998      * @throws DOMException - always.
999      */
1000     public TypeInfo getSchemaTypeInfo() throws DOMException {
1001         throw new DOMException(DOMException.NOT_SUPPORTED_ERR,
1002                                "Method not supported");
1003     }
1004 
1005     /**
1006      * This DOM Level 3 method is not supported for {@code IIOMetadataNode}
1007      * and will throw a {@code DOMException}.
1008      * @throws DOMException - always.
1009      */
1010     public Object setUserData(String key,
1011                               Object data,
1012                               UserDataHandler handler) throws DOMException {
1013         throw new DOMException(DOMException.NOT_SUPPORTED_ERR,
1014                                "Method not supported");
1015     }
1016 
1017     /**
1018      * This DOM Level 3 method is not supported for {@code IIOMetadataNode}
1019      * and will throw a {@code DOMException}.
1020      * @throws DOMException - always.
1021      */
1022     public Object getUserData(String key) throws DOMException {
1023         throw new DOMException(DOMException.NOT_SUPPORTED_ERR,
1024                                "Method not supported");
1025     }
1026 
1027     /**
1028      * This DOM Level 3 method is not supported for {@code IIOMetadataNode}
1029      * and will throw a {@code DOMException}.
1030      * @throws DOMException - always.
1031      */
1032     public Object getFeature(String feature, String version)
1033                               throws DOMException {
1034         throw new DOMException(DOMException.NOT_SUPPORTED_ERR,
1035                                "Method not supported");
1036     }
1037 
1038     /**
1039      * This DOM Level 3 method is not supported for {@code IIOMetadataNode}
1040      * and will throw a {@code DOMException}.
1041      * @throws DOMException - always.
1042      */
1043     public boolean isSameNode(Node node) throws DOMException {
1044         throw new DOMException(DOMException.NOT_SUPPORTED_ERR,
1045                                "Method not supported");
1046     }
1047 
1048     /**
1049      * This DOM Level 3 method is not supported for {@code IIOMetadataNode}
1050      * and will throw a {@code DOMException}.
1051      * @throws DOMException - always.
1052      */
1053     public boolean isEqualNode(Node node) throws DOMException {
1054         throw new DOMException(DOMException.NOT_SUPPORTED_ERR,
1055                                "Method not supported");
1056     }
1057 
1058     /**
1059      * This DOM Level 3 method is not supported for {@code IIOMetadataNode}
1060      * and will throw a {@code DOMException}.
1061      * @throws DOMException - always.
1062      */
1063     public String lookupNamespaceURI(String prefix) throws DOMException {
1064         throw new DOMException(DOMException.NOT_SUPPORTED_ERR,
1065                                "Method not supported");
1066     }
1067 
1068     /**
1069      * This DOM Level 3 method is not supported for {@code IIOMetadataNode}
1070      * and will throw a {@code DOMException}.
1071      * @throws DOMException - always.
1072      */
1073     public boolean isDefaultNamespace(String namespaceURI)
1074                                                throws DOMException {
1075         throw new DOMException(DOMException.NOT_SUPPORTED_ERR,
1076                                "Method not supported");
1077     }
1078 
1079     /**
1080      * This DOM Level 3 method is not supported for {@code IIOMetadataNode}
1081      * and will throw a {@code DOMException}.
1082      * @throws DOMException - always.
1083      */
1084     public String lookupPrefix(String namespaceURI) throws DOMException {
1085         throw new DOMException(DOMException.NOT_SUPPORTED_ERR,
1086                                "Method not supported");
1087     }
1088 
1089     /**
1090      * This DOM Level 3 method is not supported for {@code IIOMetadataNode}
1091      * and will throw a {@code DOMException}.
1092      * @throws DOMException - always.
1093      */
1094     public String getTextContent() throws DOMException {
1095         throw new DOMException(DOMException.NOT_SUPPORTED_ERR,
1096                                "Method not supported");
1097     }
1098 
1099     /**
1100      * This DOM Level 3 method is not supported for {@code IIOMetadataNode}
1101      * and will throw a {@code DOMException}.
1102      * @throws DOMException - always.
1103      */
1104     public void setTextContent(String textContent) throws DOMException {
1105         throw new DOMException(DOMException.NOT_SUPPORTED_ERR,
1106                                "Method not supported");
1107     }
1108 
1109     /**
1110      * This DOM Level 3 method is not supported for {@code IIOMetadataNode}
1111      * and will throw a {@code DOMException}.
1112      * @throws DOMException - always.
1113      */
1114     public short compareDocumentPosition(Node other)
1115                                          throws DOMException {
1116         throw new DOMException(DOMException.NOT_SUPPORTED_ERR,
1117                                "Method not supported");
1118     }
1119 
1120     /**
1121      * This DOM Level 3 method is not supported for {@code IIOMetadataNode}
1122      * and will throw a {@code DOMException}.
1123      * @throws DOMException - always.
1124      */
1125     public String getBaseURI() throws DOMException {
1126         throw new DOMException(DOMException.NOT_SUPPORTED_ERR,
1127                                "Method not supported");
1128     }
1129     //End of dummy methods for DOM L3.
1130 
1131 
1132 }