class Nokogiri::XML::XPathContext

def self.new(node) # :nodoc:

:nodoc:
def self.new(node) # :nodoc:
  LibXML.xmlXPathInit()
  ptr = LibXML.xmlXPathNewContext(node.cstruct[:doc])
  ctx = allocate
  ctx.cstruct = LibXML::XmlXpathContext.new(ptr)
  ctx.cstruct[:node] = node.cstruct
  ctx
end

def evaluate(search_path, xpath_handler=nil) # :nodoc:

:nodoc:
def evaluate(search_path, xpath_handler=nil) # :nodoc:
  lookup = nil # to keep lambda in scope long enough to avoid a possible GC tragedy
  query = search_path.to_s
  if xpath_handler
    lookup = lambda do |ctx, name, uri|
      return nil unless xpath_handler.respond_to?(name)
      ruby_funcall name, xpath_handler
    end
    LibXML.xmlXPathRegisterFuncLookup(cstruct, lookup, nil);
  end
  exception_handler = lambda do |ctx, error|
    raise XPath::SyntaxError.wrap(error)
  end
  LibXML.xmlResetLastError()
  LibXML.xmlSetStructuredErrorFunc(nil, exception_handler)
  generic_exception_handler = lambda do |ctx, msg|
    raise RuntimeError.new(msg) # TODO: varargs
  end
  LibXML.xmlSetGenericErrorFunc(nil, generic_exception_handler)
  xpath_ptr = LibXML.xmlXPathEvalExpression(query, cstruct)
  LibXML.xmlSetStructuredErrorFunc(nil, nil)
  LibXML.xmlSetGenericErrorFunc(nil, nil)
  if xpath_ptr.null?
    error = LibXML.xmlGetLastError()
    raise XPath::SyntaxError.wrap(error)
  end
  xpath = XML::XPath.new
  xpath.cstruct = LibXML::XmlXpathObject.new(xpath_ptr)
  xpath.document = cstruct[:doc]
  xpath
end

def register_namespaces(namespaces)

Register namespaces in +namespaces+
##
def register_namespaces(namespaces)
  namespaces.each do |k, v|
    k = k.gsub(/.*:/,'') # strip off 'xmlns:' or 'xml:'
    register_ns(k, v)
  end
end

def register_ns(prefix, uri) # :nodoc:

:nodoc:
def register_ns(prefix, uri) # :nodoc:
  LibXML.xmlXPathRegisterNs(cstruct, prefix, uri)
end

def ruby_funcall(name, xpath_handler) # :nodoc:

:nodoc:

returns a lambda that will call the handler function with marshalled parameters
def ruby_funcall(name, xpath_handler) # :nodoc:
  lambda do |ctx, nargs|
    parser_context = LibXML::XmlXpathParserContext.new(ctx)
    context = parser_context.context
    doc = context.doc.ruby_doc
    params = []
    nargs.times do |j|
      obj = LibXML::XmlXpathObject.new(LibXML.valuePop(ctx))
      case obj[:type]
      when LibXML::XmlXpathObject::XPATH_STRING
        params.unshift obj[:stringval]
      when LibXML::XmlXpathObject::XPATH_BOOLEAN
        params.unshift obj[:boolval] == 1
      when LibXML::XmlXpathObject::XPATH_NUMBER
        params.unshift obj[:floatval]
      when LibXML::XmlXpathObject::XPATH_NODESET
        ns_ptr = LibXML::XmlNodeSet.new(obj[:nodesetval])
        set = NodeSet.allocate
        set.cstruct = ns_ptr
        params.unshift set
      else
        char_ptr = params.unshift LibXML.xmlXPathCastToString(obj)
        string = char_ptr.read_string
        LibXML.xmlFree(char_ptr)
        string
      end
    end
    result = xpath_handler.send(name, *params)
    case result.class.to_s
    when Fixnum.to_s, Float.to_s, Bignum.to_s
      LibXML.xmlXPathReturnNumber(ctx, result)
    when String.to_s
      LibXML.xmlXPathReturnString(
        ctx,
        LibXML.xmlXPathWrapCString(result)
        )
    when TrueClass.to_s
      LibXML.xmlXPathReturnTrue(ctx)
    when FalseClass.to_s
      LibXML.xmlXPathReturnFalse(ctx)
    when NilClass.to_s
      ;
    when Array.to_s
      node_set = XML::NodeSet.new(doc, result)
      LibXML.xmlXPathReturnNodeSet(
        ctx,
        LibXML.xmlXPathNodeSetMerge(nil, node_set.cstruct)
        )
    else
      if result.is_a?(XML::NodeSet)
        LibXML.xmlXPathReturnNodeSet(
          ctx,
          LibXML.xmlXPathNodeSetMerge(nil, result.cstruct)
          )
      else
        raise RuntimeError.new("Invalid return type #{result.class.inspect}")
      end
    end
    nil
  end # lambda
end # ruby_funcall