/**
   * Returns the Attr[]s to be output for the given element. <br>
   * The code of this method is a copy of {@link #handleAttributes(Element, NameSpaceSymbTable)},
   * whereas it takes into account that subtree-c14n is -- well -- subtree-based. So if the element
   * in question isRoot of c14n, it's parent is not in the node set, as well as all other ancestors.
   *
   * @param element
   * @param ns
   * @return the Attr[]s to be output
   * @throws CanonicalizationException
   */
  @Override
  protected Iterator<Attr> handleAttributesSubtree(Element element, NameSpaceSymbTable ns)
      throws CanonicalizationException {
    if (!element.hasAttributes() && !firstCall) {
      return null;
    }
    // result will contain the attrs which have to be output
    final SortedSet<Attr> result = this.result;
    result.clear();

    if (element.hasAttributes()) {
      NamedNodeMap attrs = element.getAttributes();
      int attrsLength = attrs.getLength();

      for (int i = 0; i < attrsLength; i++) {
        Attr attribute = (Attr) attrs.item(i);
        String NUri = attribute.getNamespaceURI();
        String NName = attribute.getLocalName();
        String NValue = attribute.getValue();

        if (!XMLNS_URI.equals(NUri)) {
          // It's not a namespace attr node. Add to the result and continue.
          result.add(attribute);
        } else if (!(XML.equals(NName) && XML_LANG_URI.equals(NValue))) {
          // The default mapping for xml must not be output.
          Node n = ns.addMappingAndRender(NName, NValue, attribute);

          if (n != null) {
            // Render the ns definition
            result.add((Attr) n);
            if (C14nHelper.namespaceIsRelative(attribute)) {
              Object exArgs[] = {element.getTagName(), NName, attribute.getNodeValue()};
              throw new CanonicalizationException("c14n.Canonicalizer.RelativeNamespace", exArgs);
            }
          }
        }
      }
    }

    if (firstCall) {
      // It is the first node of the subtree
      // Obtain all the namespaces defined in the parents, and added to the output.
      ns.getUnrenderedNodes(result);
      // output the attributes in the xml namespace.
      xmlattrStack.getXmlnsAttr(result);
      firstCall = false;
    }

    return result.iterator();
  }
  /**
   * Returns the Attr[]s to be output for the given element. <br>
   * IMPORTANT: This method expects to work on a modified DOM tree, i.e. a DOM which has been
   * prepared using {@link org.apache.xml.security.utils.XMLUtils#circumventBug2650(
   * org.w3c.dom.Document)}.
   *
   * @param element
   * @param ns
   * @return the Attr[]s to be output
   * @throws CanonicalizationException
   */
  @Override
  protected Iterator<Attr> handleAttributes(Element element, NameSpaceSymbTable ns)
      throws CanonicalizationException {
    // result will contain the attrs which have to be output
    xmlattrStack.push(ns.getLevel());
    boolean isRealVisible = isVisibleDO(element, ns.getLevel()) == 1;
    final SortedSet<Attr> result = this.result;
    result.clear();

    if (element.hasAttributes()) {
      NamedNodeMap attrs = element.getAttributes();
      int attrsLength = attrs.getLength();

      for (int i = 0; i < attrsLength; i++) {
        Attr attribute = (Attr) attrs.item(i);
        String NUri = attribute.getNamespaceURI();
        String NName = attribute.getLocalName();
        String NValue = attribute.getValue();

        if (!XMLNS_URI.equals(NUri)) {
          // A non namespace definition node.
          if (XML_LANG_URI.equals(NUri)) {
            if (NName.equals("id")) {
              if (isRealVisible) {
                // treat xml:id like any other attribute
                // (emit it, but don't inherit it)
                result.add(attribute);
              }
            } else {
              xmlattrStack.addXmlnsAttr(attribute);
            }
          } else if (isRealVisible) {
            // The node is visible add the attribute to the list of output attributes.
            result.add(attribute);
          }
        } else if (!XML.equals(NName) || !XML_LANG_URI.equals(NValue)) {
          /* except omit namespace node with local name xml, which defines
           * the xml prefix, if its string value is
           * http://www.w3.org/XML/1998/namespace.
           */
          // add the prefix binding to the ns symb table.
          if (isVisible(attribute)) {
            if (isRealVisible || !ns.removeMappingIfRender(NName)) {
              // The xpath select this node output it if needed.
              Node n = ns.addMappingAndRender(NName, NValue, attribute);
              if (n != null) {
                result.add((Attr) n);
                if (C14nHelper.namespaceIsRelative(attribute)) {
                  Object exArgs[] = {element.getTagName(), NName, attribute.getNodeValue()};
                  throw new CanonicalizationException(
                      "c14n.Canonicalizer.RelativeNamespace", exArgs);
                }
              }
            }
          } else {
            if (isRealVisible && !XMLNS.equals(NName)) {
              ns.removeMapping(NName);
            } else {
              ns.addMapping(NName, NValue, attribute);
            }
          }
        }
      }
    }

    if (isRealVisible) {
      // The element is visible, handle the xmlns definition
      Attr xmlns = element.getAttributeNodeNS(XMLNS_URI, XMLNS);
      Node n = null;
      if (xmlns == null) {
        // No xmlns def just get the already defined.
        n = ns.getMapping(XMLNS);
      } else if (!isVisible(xmlns)) {
        // There is a definition but the xmlns is not selected by the xpath.
        // then xmlns=""
        n = ns.addMappingAndRender(XMLNS, "", nullNode);
      }
      // output the xmlns def if needed.
      if (n != null) {
        result.add((Attr) n);
      }
      // Float all xml:* attributes of the unselected parent elements to this one.
      xmlattrStack.getXmlnsAttr(result);
      ns.getUnrenderedNodes(result);
    }

    return result.iterator();
  }