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