/**
   * Adds a node using its nodeName attribute. As the nodeName attribute is used to derive the name
   * which the node must be stored under, multiple nodes of certain types (those that have a
   * "special" string value) cannot be stored as the names would clash. This is seen as preferable
   * to allowing nodes to be aliased.
   *
   * @see mf.org.w3c.dom.NamedNodeMap#setNamedItem
   * @return If the new Node replaces an existing node the replaced Node is returned, otherwise null
   *     is returned.
   * @param arg A node to store in a named node map. The node will later be accessible using the
   *     value of the namespaceURI and localName attribute of the node. If a node with those
   *     namespace URI and local name is already present in the map, it is replaced by the new one.
   * @exception mf.org.w3c.dom.DOMException The exception description.
   */
  public Node setNamedItem(Node arg) throws DOMException {

    CoreDocumentImpl ownerDocument = ownerNode.ownerDocument();
    if (ownerDocument.errorChecking) {
      if (isReadOnly()) {
        String msg =
            DOMMessageFormatter.formatMessage(
                DOMMessageFormatter.DOM_DOMAIN, "NO_MODIFICATION_ALLOWED_ERR", null);
        throw new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR, msg);
      }
      if (arg.getOwnerDocument() != ownerDocument) {
        String msg =
            DOMMessageFormatter.formatMessage(
                DOMMessageFormatter.DOM_DOMAIN, "WRONG_DOCUMENT_ERR", null);
        throw new DOMException(DOMException.WRONG_DOCUMENT_ERR, msg);
      }
    }

    int i = findNamePoint(arg.getNodeName(), 0);
    NodeImpl previous = null;
    if (i >= 0) {
      previous = (NodeImpl) nodes.get(i);
      nodes.set(i, arg);
    } else {
      i = -1 - i; // Insert point (may be end of list)
      if (null == nodes) {
        nodes = new ArrayList(5);
      }
      nodes.add(i, arg);
    }
    return previous;
  } // setNamedItem(Node):Node
 protected int addItem(Node arg) {
   int i = findNamePoint(arg.getNamespaceURI(), arg.getLocalName());
   if (i >= 0) {
     nodes.set(i, arg);
   } else {
     // If we can't find by namespaceURI, localName, then we find by
     // nodeName so we know where to insert.
     i = findNamePoint(arg.getNodeName(), 0);
     if (i >= 0) {
       nodes.add(i, arg);
     } else {
       i = -1 - i; // Insert point (may be end of list)
       if (null == nodes) {
         nodes = new ArrayList(5);
       }
       nodes.add(i, arg);
     }
   }
   return i;
 }
  /**
   * Iterative tree-walker. When you have a Parent link, there's often no need to resort to
   * recursion. NOTE THAT only Element nodes are matched since we're specifically supporting
   * getElementsByTagName().
   */
  protected Node nextMatchingElementAfter(Node current) {

    Node next;
    while (current != null) {
      // Look down to first child.
      if (current.hasChildNodes()) {
        current = (current.getFirstChild());
      }

      // Look right to sibling (but not from root!)
      else if (current != rootNode && null != (next = current.getNextSibling())) {
        current = next;
      }

      // Look up and right (but not past root!)
      else {
        next = null;
        for (;
            current != rootNode; // Stop when we return to starting point
            current = current.getParentNode()) {

          next = current.getNextSibling();
          if (next != null) break;
        }
        current = next;
      }

      // Have we found an Element with the right tagName?
      // ("*" matches anything.)
      if (current != rootNode && current != null && current.getNodeType() == Node.ELEMENT_NODE) {
        String name = ((ElementImpl) current).getAttribute("name");
        if (name.equals("*") || name.equals(tagName)) return current;
      }

      // Otherwise continue walking the tree
    }

    // Fell out of tree-walk; no more instances found
    return null;
  } // nextMatchingElementAfter(int):Node