/* * call-seq: * children * * Get the list of children for this node as a NodeSet */ static VALUE children(VALUE self) { xmlNodePtr node; xmlNodePtr child; xmlNodeSetPtr set; VALUE document; VALUE node_set; Data_Get_Struct(self, xmlNode, node); child = node->children; set = xmlXPathNodeSetCreate(child); document = DOC_RUBY_OBJECT(node->doc); if(!child) { return Nokogiri_wrap_xml_node_set(set, document); } child = child->next; while(NULL != child) { xmlXPathNodeSetAddUnique(set, child); child = child->next; } node_set = Nokogiri_wrap_xml_node_set(set, document); return node_set; }
/* * call-seq: * element_children * * Get the list of children for this node as a NodeSet. All nodes will be * element nodes. * * Example: * * @doc.root.element_children.all? { |x| x.element? } # => true */ static VALUE element_children(VALUE self) { xmlNodePtr node; xmlNodePtr child; xmlNodeSetPtr set; VALUE document; VALUE node_set; Data_Get_Struct(self, xmlNode, node); child = xmlFirstElementChild(node); set = xmlXPathNodeSetCreate(child); document = DOC_RUBY_OBJECT(node->doc); if(!child) return Nokogiri_wrap_xml_node_set(set, document); child = xmlNextElementSibling(child); while(NULL != child) { xmlXPathNodeSetAddUnique(set, child); child = xmlNextElementSibling(child); } node_set = Nokogiri_wrap_xml_node_set(set, document); return node_set; }
/* * call-seq: * dup * * Duplicate this node set */ static VALUE duplicate(VALUE self) { xmlNodeSetPtr node_set; Data_Get_Struct(self, xmlNodeSet, node_set); xmlNodeSetPtr dupl = xmlXPathNodeSetMerge(NULL, node_set); return Nokogiri_wrap_xml_node_set(dupl, rb_iv_get(self, "@document")); }
/* * call-seq: * dup * * Duplicate this node set */ static VALUE duplicate(VALUE self) { nokogiriNodeSetTuple *tuple; xmlNodeSetPtr dupl; Data_Get_Struct(self, nokogiriNodeSetTuple, tuple); dupl = xmlXPathNodeSetMerge(NULL, tuple->node_set); return Nokogiri_wrap_xml_node_set(dupl, rb_iv_get(self, "@document")); }
static void ruby_funcall(xmlXPathParserContextPtr ctx, int nargs) { VALUE xpath_handler = Qnil; VALUE result; VALUE *argv; VALUE doc; VALUE node_set = Qnil; xmlNodeSetPtr xml_node_set = NULL; xmlXPathObjectPtr obj; int i; nokogiriNodeSetTuple *node_set_tuple; assert(ctx); assert(ctx->context); assert(ctx->context->userData); assert(ctx->context->doc); assert(DOC_RUBY_OBJECT_TEST(ctx->context->doc)); xpath_handler = (VALUE)(ctx->context->userData); argv = (VALUE *)calloc((size_t)nargs, sizeof(VALUE)); for (i = 0 ; i < nargs ; ++i) { rb_gc_register_address(&argv[i]); } doc = DOC_RUBY_OBJECT(ctx->context->doc); if (nargs > 0) { i = nargs - 1; do { obj = valuePop(ctx); switch(obj->type) { case XPATH_STRING: argv[i] = NOKOGIRI_STR_NEW2(obj->stringval); break; case XPATH_BOOLEAN: argv[i] = obj->boolval == 1 ? Qtrue : Qfalse; break; case XPATH_NUMBER: argv[i] = rb_float_new(obj->floatval); break; case XPATH_NODESET: argv[i] = Nokogiri_wrap_xml_node_set(obj->nodesetval, doc); break; default: argv[i] = NOKOGIRI_STR_NEW2(xmlXPathCastToString(obj)); } xmlXPathFreeNodeSetList(obj); } while(i-- > 0); } result = rb_funcall2( xpath_handler, rb_intern((const char *)ctx->context->function), nargs, argv ); for (i = 0 ; i < nargs ; ++i) { rb_gc_unregister_address(&argv[i]); } free(argv); switch(TYPE(result)) { case T_FLOAT: case T_BIGNUM: case T_FIXNUM: xmlXPathReturnNumber(ctx, NUM2DBL(result)); break; case T_STRING: xmlXPathReturnString( ctx, (xmlChar *)xmlXPathWrapCString(StringValuePtr(result)) ); break; case T_TRUE: xmlXPathReturnTrue(ctx); break; case T_FALSE: xmlXPathReturnFalse(ctx); break; case T_NIL: break; case T_ARRAY: { VALUE args[2]; args[0] = doc; args[1] = result; node_set = rb_class_new_instance(2, args, cNokogiriXmlNodeSet); Data_Get_Struct(node_set, nokogiriNodeSetTuple, node_set_tuple); xml_node_set = node_set_tuple->node_set; xmlXPathReturnNodeSet(ctx, xmlXPathNodeSetMerge(NULL, xml_node_set)); } break; case T_DATA: if(rb_obj_is_kind_of(result, cNokogiriXmlNodeSet)) { Data_Get_Struct(result, nokogiriNodeSetTuple, node_set_tuple); xml_node_set = node_set_tuple->node_set; /* Copy the node set, otherwise it will get GC'd. */ xmlXPathReturnNodeSet(ctx, xmlXPathNodeSetMerge(NULL, xml_node_set)); break; } default: rb_raise(rb_eRuntimeError, "Invalid return type"); } }
/* * call-seq: * evaluate(search_path, handler = nil) * * Evaluate the +search_path+ returning an XML::XPath object. */ static VALUE evaluate(int argc, VALUE *argv, VALUE self) { VALUE search_path, xpath_handler; VALUE thing = Qnil; xmlXPathContextPtr ctx; xmlXPathObjectPtr xpath; xmlChar *query; Data_Get_Struct(self, xmlXPathContext, ctx); if(rb_scan_args(argc, argv, "11", &search_path, &xpath_handler) == 1) xpath_handler = Qnil; query = (xmlChar *)StringValuePtr(search_path); if(Qnil != xpath_handler) { /* FIXME: not sure if this is the correct place to shove private data. */ ctx->userData = (void *)xpath_handler; xmlXPathRegisterFuncLookup(ctx, lookup, (void *)xpath_handler); } xmlResetLastError(); xmlSetStructuredErrorFunc(NULL, xpath_exception_handler); /* For some reason, xmlXPathEvalExpression will blow up with a generic error */ /* when there is a non existent function. */ xmlSetGenericErrorFunc(NULL, xpath_generic_exception_handler); xpath = xmlXPathEvalExpression(query, ctx); xmlSetStructuredErrorFunc(NULL, NULL); xmlSetGenericErrorFunc(NULL, NULL); if(xpath == NULL) { VALUE xpath = rb_const_get(mNokogiriXml, rb_intern("XPath")); VALUE klass = rb_const_get(xpath, rb_intern("SyntaxError")); xmlErrorPtr error = xmlGetLastError(); rb_exc_raise(Nokogiri_wrap_xml_syntax_error(klass, error)); } assert(ctx->doc); assert(DOC_RUBY_OBJECT_TEST(ctx->doc)); switch(xpath->type) { case XPATH_STRING: thing = NOKOGIRI_STR_NEW2(xpath->stringval); break; case XPATH_NODESET: if(NULL == xpath->nodesetval) { thing = Nokogiri_wrap_xml_node_set(xmlXPathNodeSetCreate(NULL), DOC_RUBY_OBJECT(ctx->doc)); } else { thing = Nokogiri_wrap_xml_node_set(xpath->nodesetval, DOC_RUBY_OBJECT(ctx->doc)); } break; case XPATH_NUMBER: thing = rb_float_new(xpath->floatval); break; case XPATH_BOOLEAN: thing = xpath->boolval == 1 ? Qtrue : Qfalse; break; default: thing = Nokogiri_wrap_xml_node_set(xmlXPathNodeSetCreate(NULL), DOC_RUBY_OBJECT(ctx->doc)); } xmlXPathFreeNodeSetList(xpath); return thing; }
static void method_caller(xmlXPathParserContextPtr ctxt, int nargs) { const xmlChar * function; const xmlChar * functionURI; size_t i, count; xsltTransformContextPtr transform; xmlXPathObjectPtr xpath; VALUE obj; VALUE *args; VALUE result; transform = xsltXPathGetTransformContext(ctxt); function = ctxt->context->function; functionURI = ctxt->context->functionURI; obj = (VALUE)xsltGetExtData(transform, functionURI); count = (size_t)ctxt->valueNr; args = calloc(count, sizeof(VALUE *)); for(i = 0; i < count; i++) { VALUE thing; xpath = valuePop(ctxt); switch(xpath->type) { case XPATH_STRING: thing = NOKOGIRI_STR_NEW2(xpath->stringval); break; case XPATH_NODESET: if(NULL == xpath->nodesetval) { thing = Nokogiri_wrap_xml_node_set( xmlXPathNodeSetCreate(NULL), DOC_RUBY_OBJECT(ctxt->context->doc)); } else { thing = Nokogiri_wrap_xml_node_set(xpath->nodesetval, DOC_RUBY_OBJECT(ctxt->context->doc)); } break; default: rb_raise(rb_eRuntimeError, "do not handle type: %d", xpath->type); } args[i] = thing; xmlFree(xpath); } result = rb_funcall3(obj, rb_intern((const char *)function), (int)count, args); free(args); switch(TYPE(result)) { case T_FLOAT: case T_BIGNUM: case T_FIXNUM: xmlXPathReturnNumber(ctxt, NUM2DBL(result)); break; case T_STRING: xmlXPathReturnString( ctxt, xmlStrdup((xmlChar *)StringValuePtr(result)) ); break; case T_TRUE: xmlXPathReturnTrue(ctxt); break; case T_FALSE: xmlXPathReturnFalse(ctxt); break; case T_NIL: break; default: rb_raise(rb_eRuntimeError, "Invalid return type"); } }