/**
  * Get n'th attribute (DOM NamedNodeMap method). In this implementation we number the attributes
  * as follows: 0 - the xmlns:xml namespace declaration 1-n further namespace declarations n+1...
  * "real" attribute declarations
  */
 public Node item(int index) {
   if (index < 0) {
     return null;
   }
   if (index == 0) {
     NamespaceIterator.NamespaceNodeImpl nn =
         new NamespaceIterator.NamespaceNodeImpl(parent, NamespaceConstant.XML_NAMESPACE_CODE, 0);
     return NodeOverNodeInfo.wrap(nn);
   }
   int nscount = getNumberOfNamespaces();
   if (index < nscount) {
     int[] buffer = new int[8];
     int[] nsList = parent.getDeclaredNamespaces(buffer);
     int nscode = nsList[index - 1];
     NamespaceIterator.NamespaceNodeImpl nn =
         new NamespaceIterator.NamespaceNodeImpl(parent, nscode, index);
     return NodeOverNodeInfo.wrap(nn);
   }
   int pos = 0;
   int attNr = (index - nscount);
   AxisIterator atts = parent.iterateAxis(Axis.ATTRIBUTE);
   while (true) {
     NodeInfo att = (NodeInfo) atts.next();
     if (att == null) {
       return null;
     }
     if (pos == attNr) {
       return NodeOverNodeInfo.wrap(att);
     }
     pos++;
   }
 }
 /** Get named attribute (DOM NamedNodeMap method) */
 public Node getNamedItem(String name) {
   if (name.equals("xmlns")) {
     int[] nsarray = parent.getDeclaredNamespaces(null);
     for (int i = 0; i < nsarray.length; i++) {
       if (nsarray[i] == -1) {
         return null;
       } else if (((nsarray[i] >> 16) & 0xffff) == 0) {
         NamespaceIterator.NamespaceNodeImpl nn =
             new NamespaceIterator.NamespaceNodeImpl(parent, nsarray[i], i + 1);
         return NodeOverNodeInfo.wrap(nn);
       }
     }
     return null;
   } else if (name.startsWith("xmlns:")) {
     String prefix = name.substring(6);
     if (prefix.equals("xml")) {
       NamespaceIterator.NamespaceNodeImpl nn =
           new NamespaceIterator.NamespaceNodeImpl(parent, NamespaceConstant.XML_CODE, 0);
       return NodeOverNodeInfo.wrap(nn);
     }
     int[] buffer = new int[8];
     int[] nsarray = parent.getDeclaredNamespaces(buffer);
     for (int i = 0; i < nsarray.length; i++) {
       if (nsarray[i] == -1) {
         return null;
       } else if (prefix.equals(parent.getNamePool().getPrefixFromNamespaceCode(nsarray[i]))) {
         NamespaceIterator.NamespaceNodeImpl nn =
             new NamespaceIterator.NamespaceNodeImpl(parent, nsarray[i], i + 1);
         return NodeOverNodeInfo.wrap(nn);
       }
     }
     return null;
   } else {
     AxisIterator atts = parent.iterateAxis(Axis.ATTRIBUTE);
     while (true) {
       NodeInfo att = (NodeInfo) atts.next();
       if (att == null) {
         return null;
       }
       if (name.equals(att.getDisplayName())) {
         return NodeOverNodeInfo.wrap(att);
       }
     }
   }
 }
 /** Get named attribute (DOM NamedNodeMap method) */
 public Node getNamedItemNS(String uri, String localName) {
   if (uri == null) {
     uri = "";
   }
   if (NamespaceConstant.XMLNS.equals(uri)) {
     return getNamedItem("xmlns:" + localName);
   }
   if (uri.equals("") && localName.equals("xmlns")) {
     return getNamedItem("xmlns");
   }
   AxisIterator atts = parent.iterateAxis(Axis.ATTRIBUTE);
   while (true) {
     NodeInfo att = (NodeInfo) atts.next();
     if (att == null) {
       return null;
     }
     if (uri.equals(att.getURI()) && localName.equals(att.getLocalPart())) {
       return NodeOverNodeInfo.wrap(att);
     }
   }
 }
  /**
   * Convert an XPath value to an object in this object model. If the supplied value can be
   * converted to an object in this model, of the specified class, then the conversion should be
   * done and the resulting object returned. If the value cannot be converted, the method should
   * return null. Note that the supplied class might be a List, in which case the method should
   * inspect the contents of the Value to see whether they belong to this object model.
   *
   * @throws XPathException if the target class is explicitly associated with this object model, but
   *     the supplied value cannot be converted to the appropriate class
   */
  public Object convertXPathValueToObject(Value value, Class target, XPathContext context)
      throws XPathException {
    // We accept the object if (a) the target class is Node, Node[], or NodeList,
    // or (b) the supplied object is a node, or sequence of nodes, that wrap DOM nodes,
    // provided that the target class is Object or a collection class
    boolean requireDOM =
        (Node.class.isAssignableFrom(target)
            || (target == NodeList.class)
            || (target.isArray() && Node.class.isAssignableFrom(target.getComponentType())));

    // Note: we allow the declared type of the method argument to be a subclass of Node. If the
    // actual
    // node supplied is the wrong kind of node, this will result in a Java exception.

    boolean allowDOM =
        (target == Object.class
            || target.isAssignableFrom(ArrayList.class)
            || target.isAssignableFrom(HashSet.class)
            || (target.isArray() && target.getComponentType() == Object.class));
    if (!(requireDOM || allowDOM)) {
      return null;
    }
    List nodes = new ArrayList(20);

    SequenceIterator iter = value.iterate(context);
    while (true) {
      Item item = iter.next();
      if (item == null) {
        break;
      }
      if (item instanceof VirtualNode) {
        Object o = ((VirtualNode) item).getUnderlyingNode();
        if (o instanceof Node) {
          nodes.add(o);
        } else {
          if (requireDOM) {
            DynamicError err =
                new DynamicError(
                    "Extension function required class "
                        + target.getName()
                        + "; supplied value of class "
                        + item.getClass().getName()
                        + " could not be converted");
            throw err;
          }
          ;
        }
      } else if (requireDOM) {
        if (item instanceof NodeInfo) {
          nodes.add(NodeOverNodeInfo.wrap((NodeInfo) item));
        } else {
          DynamicError err =
              new DynamicError(
                  "Extension function required class "
                      + target.getName()
                      + "; supplied value of class "
                      + item.getClass().getName()
                      + " could not be converted");
          throw err;
        }
      } else {
        return null; // DOM Nodes are not actually required; let someone else try the conversion
      }
    }

    if (nodes.size() == 0 && !requireDOM) {
      return null; // empty sequence supplied - try a different mapping
    }
    if (Node.class.isAssignableFrom(target)) {
      if (nodes.size() != 1) {
        DynamicError err =
            new DynamicError(
                "Extension function requires a single DOM Node"
                    + "; supplied value contains "
                    + nodes.size()
                    + " nodes");
        throw err;
      }
      return nodes.get(0);
      // could fail if the node is of the wrong kind
    } else if (target == NodeList.class) {
      return new DOMNodeList(nodes);
    } else if (target.isArray() && target.getComponentType() == Node.class) {
      Node[] array = new Node[nodes.size()];
      nodes.toArray(array);
      return array;
    } else if (target.isAssignableFrom(ArrayList.class)) {
      return nodes;
    } else if (target.isAssignableFrom(HashSet.class)) {
      return new HashSet(nodes);
    } else {
      // after all this work, give up
      return null;
    }
  }