Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[draft] NodeSet is an Array #2617

Draft
wants to merge 3 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
80 changes: 28 additions & 52 deletions ext/nokogiri/xml_node_set.c
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

VALUE cNokogiriXmlNodeSet ;

static ID decorate ;
/* static ID decorate ; */

static void
Check_Node_Set_Node_Type(VALUE node)
Expand Down Expand Up @@ -415,27 +415,27 @@ to_array(VALUE self)
*
* Unlink this NodeSet and all Node objects it contains from their current context.
*/
static VALUE
unlink_nodeset(VALUE self)
{
xmlNodeSetPtr node_set;
int j, nodeNr ;

Data_Get_Struct(self, xmlNodeSet, node_set);

nodeNr = node_set->nodeNr ;
for (j = 0 ; j < nodeNr ; j++) {
if (! NOKOGIRI_NAMESPACE_EH(node_set->nodeTab[j])) {
VALUE node ;
xmlNodePtr node_ptr;
node = noko_xml_node_wrap(Qnil, node_set->nodeTab[j]);
rb_funcall(node, rb_intern("unlink"), 0); /* modifies the C struct out from under the object */
Noko_Node_Get_Struct(node, xmlNode, node_ptr);
node_set->nodeTab[j] = node_ptr ;
}
}
return self ;
}
/* static VALUE */
/* unlink_nodeset(VALUE self) */
/* { */
/* xmlNodeSetPtr node_set; */
/* int j, nodeNr ; */

/* Data_Get_Struct(self, xmlNodeSet, node_set); */

/* nodeNr = node_set->nodeNr ; */
/* for (j = 0 ; j < nodeNr ; j++) { */
/* if (! NOKOGIRI_NAMESPACE_EH(node_set->nodeTab[j])) { */
/* VALUE node ; */
/* xmlNodePtr node_ptr; */
/* node = noko_xml_node_wrap(Qnil, node_set->nodeTab[j]); */
/* rb_funcall(node, rb_intern("unlink"), 0); /\* modifies the C struct out from under the object *\/ */
/* Noko_Node_Get_Struct(node, xmlNode, node_ptr); */
/* node_set->nodeTab[j] = node_ptr ; */
/* } */
/* } */
/* return self ; */
/* } */


VALUE
Expand All @@ -444,20 +444,13 @@ noko_xml_node_set_wrap(xmlNodeSetPtr c_node_set, VALUE document)
int j;
VALUE rb_node_set ;

if (c_node_set == NULL) {
c_node_set = xmlXPathNodeSetCreate(NULL);
}

rb_node_set = Data_Wrap_Struct(cNokogiriXmlNodeSet, mark, deallocate, c_node_set);

if (!NIL_P(document)) {
rb_iv_set(rb_node_set, "@document", document);
rb_funcall(document, decorate, 1, rb_node_set);
}
rb_node_set = rb_class_new_instance(1, &document, cNokogiriXmlNodeSet);

/* make sure we create ruby objects for all the results, so they'll be marked during the GC mark phase */
for (j = 0 ; j < c_node_set->nodeNr ; j++) {
noko_xml_node_wrap_node_set_result(c_node_set->nodeTab[j], rb_node_set);
if (c_node_set) {
for (j = 0 ; j < c_node_set->nodeNr ; j++) {
rb_ary_push(rb_node_set, noko_xml_node_wrap_node_set_result(c_node_set->nodeTab[j], rb_node_set));
}
}

return rb_node_set ;
Expand All @@ -477,22 +470,5 @@ noko_xml_node_wrap_node_set_result(xmlNodePtr node, VALUE node_set)
void
noko_init_xml_node_set(void)
{
cNokogiriXmlNodeSet = rb_define_class_under(mNokogiriXml, "NodeSet", rb_cObject);

rb_define_alloc_func(cNokogiriXmlNodeSet, allocate);

rb_define_method(cNokogiriXmlNodeSet, "length", length, 0);
rb_define_method(cNokogiriXmlNodeSet, "[]", slice, -1);
rb_define_method(cNokogiriXmlNodeSet, "slice", slice, -1);
rb_define_method(cNokogiriXmlNodeSet, "push", push, 1);
rb_define_method(cNokogiriXmlNodeSet, "|", rb_xml_node_set_union, 1);
rb_define_method(cNokogiriXmlNodeSet, "-", minus, 1);
rb_define_method(cNokogiriXmlNodeSet, "unlink", unlink_nodeset, 0);
rb_define_method(cNokogiriXmlNodeSet, "to_a", to_array, 0);
rb_define_method(cNokogiriXmlNodeSet, "dup", duplicate, 0);
rb_define_method(cNokogiriXmlNodeSet, "delete", delete, 1);
rb_define_method(cNokogiriXmlNodeSet, "&", intersection, 1);
rb_define_method(cNokogiriXmlNodeSet, "include?", include_eh, 1);

decorate = rb_intern("decorate");
cNokogiriXmlNodeSet = rb_define_class_under(mNokogiriXml, "NodeSet", rb_cArray);
}
120 changes: 62 additions & 58 deletions ext/nokogiri/xml_xpath_context.c
Original file line number Diff line number Diff line change
Expand Up @@ -154,54 +154,96 @@ register_variable(VALUE self, VALUE name, VALUE value)
* returns Qundef if no conversion was possible.
*/
static VALUE
xpath2ruby(xmlXPathObjectPtr xobj, xmlXPathContextPtr xctx)
xpath2ruby(xmlXPathObjectPtr xpath_object, xmlXPathContextPtr xpath_context)
{
VALUE retval;

assert(xctx->doc);
assert(DOC_RUBY_OBJECT_TEST(xctx->doc));

switch (xobj->type) {
switch (xpath_object->type) {
case XPATH_STRING:
retval = NOKOGIRI_STR_NEW2(xobj->stringval);
xmlFree(xobj->stringval);
retval = NOKOGIRI_STR_NEW2(xpath_object->stringval);
xmlFree(xpath_object->stringval);
return retval;

case XPATH_NODESET:
return noko_xml_node_set_wrap(xobj->nodesetval,
DOC_RUBY_OBJECT(xctx->doc));
assert(xpath_context->doc);
assert(DOC_RUBY_OBJECT_TEST(xpath_context->doc));
return noko_xml_node_set_wrap(xpath_object->nodesetval,
DOC_RUBY_OBJECT(xpath_context->doc));

case XPATH_NUMBER:
return rb_float_new(xobj->floatval);
return rb_float_new(xpath_object->floatval);

case XPATH_BOOLEAN:
return (xobj->boolval == 1) ? Qtrue : Qfalse;
return (xpath_object->boolval == 1) ? Qtrue : Qfalse;

default:
return Qundef;
}
}


static VALUE
ruby2xpath_node_set_append(RB_BLOCK_CALL_FUNC_ARGLIST(rb_node, wrapped_c_node_set))
{
xmlNodeSetPtr c_node_set = (xmlNodeSetPtr)wrapped_c_node_set;
xmlNodePtr c_node;
Data_Get_Struct(rb_node, xmlNode, c_node);
xmlXPathNodeSetAddUnique(c_node_set, c_node);
return Qnil;
}

/*
* convert a Ruby object into an XPath object of the appropriate type.
* raises an exception if no conversion was possible.
*/
static xmlXPathObjectPtr
ruby2xpath(VALUE rb_object, xmlXPathContextPtr xpath_context)
{
xmlXPathObjectPtr result;

switch (TYPE(rb_object)) {
case T_FLOAT:
case T_BIGNUM:
case T_FIXNUM:
result = xmlXPathNewFloat(NUM2DBL(rb_object));
break;
case T_STRING:
result = xmlXPathWrapString(xmlCharStrdup(StringValueCStr(rb_object)));
break;
case T_TRUE:
result = xmlXPathNewBoolean(1);
break;
case T_FALSE:
case T_NIL:
result = xmlXPathNewBoolean(0);
break;
case T_ARRAY:
{
xmlNodeSetPtr c_node_set = xmlXPathNodeSetCreate(NULL);
rb_block_call(rb_object, rb_intern("each"), 0, NULL, ruby2xpath_node_set_append, (VALUE)c_node_set);
result = xmlXPathWrapNodeSet(xmlXPathNodeSetMerge(NULL, c_node_set));
}
break;
default:
rb_raise(rb_eRuntimeError, "Invalid return type");
}
return result;
}


void
Nokogiri_marshal_xpath_funcall_and_return_values(xmlXPathParserContextPtr ctx, int nargs, VALUE handler,
const char *function_name)
{
VALUE result, doc;
VALUE result;
VALUE *argv;
VALUE node_set = Qnil;
xmlNodeSetPtr xml_node_set = NULL;
xmlXPathObjectPtr obj;

assert(ctx->context->doc);
assert(DOC_RUBY_OBJECT_TEST(ctx->context->doc));

argv = (VALUE *)ruby_xcalloc((size_t)nargs, sizeof(VALUE));
for (int j = 0 ; j < nargs ; ++j) {
rb_gc_register_address(&argv[j]);
}

doc = DOC_RUBY_OBJECT(ctx->context->doc);

for (int j = nargs - 1 ; j >= 0 ; --j) {
obj = valuePop(ctx);
argv[j] = xpath2ruby(obj, ctx->context);
Expand All @@ -218,45 +260,7 @@ Nokogiri_marshal_xpath_funcall_and_return_values(xmlXPathParserContextPtr ctx, i
}
ruby_xfree(argv);

switch (TYPE(result)) {
case T_FLOAT:
case T_BIGNUM:
case T_FIXNUM:
xmlXPathReturnNumber(ctx, NUM2DBL(result));
break;
case T_STRING:
xmlXPathReturnString(
ctx,
xmlCharStrdup(StringValueCStr(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, xmlNodeSet, xml_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, xmlNodeSet, xml_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");
}
valuePush(ctx, ruby2xpath(result, ctx->context));
}

static void
Expand Down
5 changes: 5 additions & 0 deletions lib/nokogiri/xml/namespace.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@ class Namespace
include Nokogiri::XML::PP::Node
attr_reader :document

# Returns true if this is a Namespace
def namespace?
true
end

private

def inspect_attributes
Expand Down
5 changes: 5 additions & 0 deletions lib/nokogiri/xml/node.rb
Original file line number Diff line number Diff line change
Expand Up @@ -1068,6 +1068,11 @@ def fragment?
type == DOCUMENT_FRAG_NODE
end

# Returns true if this is a Namespace
def namespace?
type == NAMESPACE_DECL
end

###
# Fetch the Nokogiri::HTML4::ElementDescription for this node. Returns
# nil on XML documents and on unknown tags.
Expand Down
Loading