/* $Id: ruby_xml_xpath.c,v 1.9 2002/10/22 07:56:53 sean Exp $ */

/* Please see the LICENSE file for copyright and distribution information */

#include "libxml.h"
#include "ruby_xml_xpath.h"

VALUE cXMLXPath;
VALUE eXMLXPathInvalidPath;

#ifdef LIBXML_XPATH_ENABLED


VALUE ruby_xml_xpath_debug(VALUE self) {
#ifdef LIBXML_DEBUG_ENABLED
  ruby_xml_xpath *rxxp;
  Data_Get_Struct(self, ruby_xml_xpath, rxxp);

  if (rxxp->xpop != NULL) {
    xmlXPathDebugDumpObject(stdout, rxxp->xpop, 0);
    return(Qtrue);
  } else {
    return(Qfalse);
  }
#else
  rb_warn("libxml does not have debugging turned on");
  return(Qfalse);
#endif
}


VALUE ruby_xml_xpath_each(VALUE self) {
  ruby_xml_xpath *rxxp;
  VALUE rxnset;

  Data_Get_Struct(self, ruby_xml_xpath, rxxp);
  rxnset = ruby_xml_node_set_new(cXMLNodeSet, rxxp->xd, self,
				 rxxp->xpop->nodesetval);
  ruby_xml_node_set_each(rxnset);
  return(rxnset);
}


VALUE ruby_xml_xpath_find(VALUE class, VALUE rnode, VALUE string) {
#ifdef LIBXML_XPATH_ENABLED
  xmlXPathCompExprPtr comp;
  ruby_xml_node *node;
  ruby_xml_xpath *rxxp;
  ruby_xml_xpath_context *rxxpc;
  VALUE xxpc, xpath;

  Check_Type(string, T_STRING);
  if (rb_obj_is_kind_of(rnode, cXMLNode) == Qfalse)
    rb_raise(rb_eTypeError, "require an XML::Node object");

  Data_Get_Struct(rnode, ruby_xml_node, node);
  xxpc = ruby_xml_xpath_context_new4(rnode);
  if (NIL_P(xxpc))
    return(Qnil);
  Data_Get_Struct(xxpc, ruby_xml_xpath_context, rxxpc);

  xpath = ruby_xml_xpath_new(cXMLXPath, rnode, xxpc, NULL);
  Data_Get_Struct(xpath, ruby_xml_xpath, rxxp);

  rxxpc->ctxt->node = node->node;
  comp = xmlXPathCompile(STR2CSTR(string));

  if (comp == NULL) {
    xmlXPathFreeCompExpr(comp);
    rb_raise(eXMLXPathInvalidPath, "Invalid XPath expression");
  }
  rxxp->xpop = xmlXPathCompiledEval(comp, rxxpc->ctxt);
  xmlXPathFreeCompExpr(comp);

  if (rxxp->xpop == NULL)
    rb_raise(eXMLXPathInvalidPath,
	     "Invalid XPath expression for this document");

  return(ruby_xml_node_set_new2(node->xd, xpath,
				rxxp->xpop->nodesetval));
#else
  rb_warn("libxml was compiled without XPath support");
  return(Qfalse);
#endif
}


VALUE ruby_xml_xpath_find2(VALUE node, VALUE string) {
  return(ruby_xml_xpath_find(cXMLXPath, node, string));
}


void ruby_xml_xpath_free(ruby_xml_xpath *rxxp) {
  if (rxxp->xpop != NULL)
    xmlXPathFreeObject(rxxp->xpop);

  free(rxxp);
}


void ruby_xml_xpath_mark(ruby_xml_xpath *rxxp) {
  if (rxxp == NULL) return;
  if (!NIL_P(rxxp->ctxt)) rb_gc_mark(rxxp->ctxt);
  if (!NIL_P(rxxp->xd)) rb_gc_mark(rxxp->xd);
}


VALUE ruby_xml_xpath_new(VALUE class, VALUE xd, VALUE ctxt,
			 xmlXPathObjectPtr xpop) {
  ruby_xml_xpath *rxxp;

  rxxp = ALLOC(ruby_xml_xpath);
  rxxp->ctxt = ctxt;
  rxxp->xd = xd;
  rxxp->xpop = xpop;
  return(Data_Wrap_Struct(class, ruby_xml_xpath_mark,
			  ruby_xml_xpath_free, rxxp));
}


VALUE ruby_xml_xpath_set(VALUE self) {
  ruby_xml_xpath *rxxp;
  Data_Get_Struct(self, ruby_xml_xpath, rxxp);
  return(ruby_xml_node_set_new(cXMLNodeSet, rxxp->xd, self,
			       rxxp->xpop->nodesetval));
}


VALUE ruby_xml_xpath_set_type(VALUE self) {
  ruby_xml_xpath *rxxp;
  Data_Get_Struct(self, ruby_xml_xpath, rxxp);

  return(INT2FIX(rxxp->xpop->type));
}


VALUE ruby_xml_xpath_string(VALUE self) {
  ruby_xml_xpath *rxxp;
  Data_Get_Struct(self, ruby_xml_xpath, rxxp);

  if (rxxp->xpop->stringval == NULL)
    return(Qnil);
  else
    return(rb_str_new2(rxxp->xpop->stringval));
}


void ruby_init_xml_xpath(void) {
  cXMLXPath = rb_define_class_under(mXML, "XPath", rb_cObject);

  eXMLXPathInvalidPath = rb_define_class_under(cXMLXPath,
					       "InvalidPath", rb_eException);

  rb_define_const(cXMLXPath, "UNDEFINED", INT2NUM(XPATH_UNDEFINED));
  rb_define_const(cXMLXPath, "NODESET", INT2NUM(XPATH_NODESET));
  rb_define_const(cXMLXPath, "BOOLEAN", INT2NUM(XPATH_BOOLEAN));
  rb_define_const(cXMLXPath, "NUMBER", INT2NUM(XPATH_NUMBER));
  rb_define_const(cXMLXPath, "STRING", INT2NUM(XPATH_STRING));
  rb_define_const(cXMLXPath, "POINT", INT2NUM(XPATH_POINT));
  rb_define_const(cXMLXPath, "RANGE", INT2NUM(XPATH_RANGE));
  rb_define_const(cXMLXPath, "LOCATIONSET", INT2NUM(XPATH_LOCATIONSET));
  rb_define_const(cXMLXPath, "USERS", INT2NUM(XPATH_USERS));
  rb_define_const(cXMLXPath, "XSLT_TREE", INT2NUM(XPATH_XSLT_TREE));

  rb_define_singleton_method(cXMLXPath, "find", ruby_xml_xpath_find, 2);

  rb_define_method(cXMLXPath, "debug", ruby_xml_xpath_debug, 0);
  rb_define_method(cXMLXPath, "each", ruby_xml_xpath_each, 0);
  rb_define_method(cXMLXPath, "set", ruby_xml_xpath_set, 0);
  rb_define_method(cXMLXPath, "set_type", ruby_xml_xpath_set_type, 0);
  rb_define_method(cXMLXPath, "string", ruby_xml_xpath_string, 0);
}

#endif /* ifdef LIBXML_XPATH_ENABLED */
